Skip to content
Linq

Get a message's timeline

GET/v1/messages/{msg}/timeline

Retrieves the full story of one outbound send: when it was accepted, its current delivery state, the terminal events it produced, and the webhook deliveries that carried those events to your endpoints. GET /v1/messages/{msg} answers “what happened”; this answers “why does it say that”.

Requirements

  • msg is an outbound send id: the ids POST /v1/chats/{chat}/messages returns. Inbound customer messages have no message id and return HTTP 404, as does another brand’s id or an unknown one.

Limits

  • No per-attempt history: entries carry counters and a current state.
  • No intermediate delivery states: you get the current state plus the immutable terminal events.
  • Entries past the retention horizons are marked by retention and truncated rather than silently absent.
Path ParametersExpand Collapse
msg: string
ReturnsExpand Collapse
chat_id: string
entries: array of object { at, kind, attempt, 13 more }

Every entry we hold, oldest first.

at: string

When the fact was recorded: accept time on accepted, when the delivery row last moved on delivery (it is mutable), the journal row’s time on a terminal event, and the outbox row’s creation on webhook.

formatdate-time
kind: string

What this entry is. Documented-open; skip values you do not know.

attempt: optional number

The row’s attempt counter. Present-and-zero is a real answer (“accepted, never attempted”), so read absence and zero differently.

formatint32
attempt_404: optional number

delivery only: attempts that answered 404.

formatint32
attempt_auth: optional number

delivery only: attempts that failed authentication.

formatint32
endpoint_id: optional string

webhook only: YOUR endpoint id (wh_…) the row is bound for.

event_type: optional string

webhook only: the partner event kind the row carries (message.sent | message.failed).

last_error: optional string

Failure-only: the gateway’s (or your endpoint’s) error text on a FAILED row.

last_status: optional number

Failure-only, exactly as on GET /v1/messages/{msg}: the gateway’s (or your endpoint’s) HTTP status on a FAILED row, and absent otherwise: a success does not publish its status here.

formatint32
message_id: optional string

webhook only: the message the row’s own payload names: the finer attribution key on the window-spanning row above.

next_attempt_at: optional string

When the next attempt is due. Rides only while the row is NON-terminal, so a settled entry never advertises a retry that will not happen.

formatdate-time
reason: optional string

message_failed only: the failure discriminator (permanent, undelivered, auth_error, suppressed, …), the same value that webhook carried.

seq: optional number

Journal entries only: the event’s seq: the same watermark GET /v1/chats/{chat}/events pages on.

formatint64
seq_from: optional number

webhook only: the row’s journal-seq window (inclusive). Every per-message emit produces a single-seq window today, so a webhook entry names exactly one message; the schema permits a wider one, and such a row appears on EVERY covered message’s timeline.

formatint64
seq_to: optional number
formatint64
state: optional string

Current state of the mutable row behind this entry: the delivery state (queued | sending | retry | sent | failed | undelivered | suppressed | cancelled) on delivery, the outbox state (pending | sending | delivered | failed) on webhook.

message_id: string

The message this timeline is about (msg_…).

retention: object { delivery_from, webhook_from }

The two retention horizons this response was computed against: the database clock’s now() minus each sweep’s own window. They differ, which is the whole reason truncated exists.

delivery_from: string

Delivery rows that settled before this may have been swept (90 days).

formatdate-time
webhook_from: string

Webhook outbox rows created before this may have been swept (30 days).

formatdate-time
truncated: array of string

Each section whose retention horizon this message has outlived; "delivery", "webhook", or both. Always present; an EMPTY array means an empty section is a fact about the world rather than a sweep.

It exists because the horizons differ: a 45-day-old send that delivered and whose webhook fired shows a delivery entry and zero webhook entries, which would otherwise be byte-identical to “the webhook was never created” on the one endpoint built to answer that question.

“MAY have been swept”, never “was”: the 30-day webhook sweep takes delivered rows ONLY, so a pending or failed outbox row survives past its horizon and truncation can hide only a SUCCESS.

Get a message's timeline

curl https://messages.api.linqapp.com/v1/messages/$MSG/timeline \
    -H "Authorization: Bearer $LINQ_AMB_API_KEY"
{
  "message_id": "msg_2c7d90",
  "chat_id": "chat_4f81b2",
  "entries": [
    {
      "kind": "accepted",
      "at": "2026-08-06T14:02:12Z"
    },
    {
      "kind": "delivery",
      "at": "2026-08-06T14:02:13Z",
      "state": "sent",
      "attempt": 1
    },
    {
      "kind": "message_sent",
      "at": "2026-08-06T14:02:13Z",
      "seq": 14
    },
    {
      "kind": "webhook",
      "at": "2026-08-06T14:02:13Z",
      "state": "delivered",
      "attempt": 1,
      "endpoint_id": "wh_7c31a8",
      "event_type": "message.sent",
      "seq_from": 14,
      "seq_to": 14,
      "message_id": "msg_2c7d90"
    }
  ],
  "retention": {
    "delivery_from": "2026-05-08T14:02:20Z",
    "webhook_from": "2026-07-07T14:02:20Z"
  },
  "truncated": []
}
Returns Examples
{
  "message_id": "msg_2c7d90",
  "chat_id": "chat_4f81b2",
  "entries": [
    {
      "kind": "accepted",
      "at": "2026-08-06T14:02:12Z"
    },
    {
      "kind": "delivery",
      "at": "2026-08-06T14:02:13Z",
      "state": "sent",
      "attempt": 1
    },
    {
      "kind": "message_sent",
      "at": "2026-08-06T14:02:13Z",
      "seq": 14
    },
    {
      "kind": "webhook",
      "at": "2026-08-06T14:02:13Z",
      "state": "delivered",
      "attempt": 1,
      "endpoint_id": "wh_7c31a8",
      "event_type": "message.sent",
      "seq_from": 14,
      "seq_to": 14,
      "message_id": "msg_2c7d90"
    }
  ],
  "retention": {
    "delivery_from": "2026-05-08T14:02:20Z",
    "webhook_from": "2026-07-07T14:02:20Z"
  },
  "truncated": []
}