Skip to content
Linq Copy agent prompt

Webhooks

ModelsExpand Collapse
MessageEventV2 { id, chat, direction, 12 more }

Unified payload for message webhooks when using webhook_version: "2026-02-03".

This schema is used for message.sent, message.received, message.delivered, and message.read events when the subscription URL includes ?version=2026-02-03.

Key differences from V1 (2025-01-01):

  • direction: “inbound” or “outbound” instead of is_from_me boolean
  • sender_handle: Full handle object for the sender
  • chat: Nested object with id, is_group, and owner_handle
  • Message fields (id, parts, effect, etc.) are at the top level, not nested in message

Timestamps indicate the message state:

  • message.sent: sent_at set, delivered_at=null, read_at=null
  • message.received: sent_at set, delivered_at=null, read_at=null
  • message.delivered: sent_at set, delivered_at set, read_at=null
  • message.read: sent_at set, delivered_at set, read_at set
id: string

Message identifier

formatuuid
chat: Chat { id, health_status, is_group, owner_handle }

Chat information

id: string

Chat identifier

formatuuid
health_status: HealthStatus { doc_url, status, updated_at }

[BETA] Current health for a chat. Always present — chats start at HEALTHY and may shift based on engagement and delivery signals on the conversation. Many AT_RISK or CRITICAL chats on a single line increase the risk of line flagging.

Switch on status to surface chat and line health in your UI — the enum is the long-term contract. Each status carries a doc_url that deep-links to the relevant section of the Chat Health guide. To gate a send, act on the response rather than the status: a 403 is the authoritative answer.

See the Chat Health guide for what each status means and how to react.

doc_url: string

Deep-link to the relevant section of the Chat Health guide for this status.

formaturi
status: "HEALTHY" | "AT_RISK" | "CRITICAL" | "OPTED_OUT"

Current health bucket for the chat. See the Chat Health guide for what each value means and how to react. doc_url deep-links to the relevant section.

OPTED_OUT — the recipient sent STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, or QUIT. The keyword must be the whole trimmed message, never part of a longer one: STOP counts, please stop does not. Most keywords must match exactly, including case. OPT OUT is the exception — it matches in any casing, with or without the space or a hyphen, so opt out, Opt-Out and optout all count. It clears as soon as they reply again: any later message from them that is not itself an opt-out keyword opts them back in immediately — a reply in any conversation with you counts, the same way the block does.

OPTED_OUT marks only the conversation the keyword arrived in. The block below is wider than the mark, so a conversation still reading HEALTHY can be blocked as well — gate on the 403, not on the status. Group threads are never marked and are never blocked.

Linq enforces this: while a recipient is opted out, every send to them is rejected with 403 (error code 2024) before the message is queued, across every chat and every line on your account. Nothing is delivered, including a final courtesy message — to send one, set override_optout: true on that single request.

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
"OPTED_OUT"
updated_at: string

When this status last changed.

formatdate-time
is_group?: boolean | null

Whether this is a group chat

owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Your phone number’s handle. Always has is_me=true.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"

Message direction - “outbound” if sent by you, “inbound” if received

One of the following:
"inbound"
"outbound"
parts: Array<SchemasTextPartResponse { type, value, mention, 3 more } | SchemasMediaPartResponse { id, filename, mime_type, 3 more } | SchemasLinkPartResponse { type, value } | 2 more>

Message parts (text and/or media)

One of the following:
SchemasTextPartResponse { type, value, mention, 3 more }

A text message part

type: "text"

Indicates this is a text message part

value: string

The text content

Deprecatedmention?: string | null

DEPRECATED: Use mentions instead. Handle (E.164 phone number or Apple ID email) of the first mention on this part. A part may carry several mentions; this field shows only the first in value order, so it cannot be used to determine whether a given participant was mentioned. null when the part carries no mention.

Deprecatedmention_range?: Array<number> | null

DEPRECATED: Use mentions[].range instead. Character range [start, end) in value highlighted as the first mention only. null when the range was omitted (the whole value is highlighted) or the part carries no mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

mentions?: Array<Mention> | null

Every mention on this part, in the order they appear in value. null when the part carries no mention. A part can carry several mentions of different people — check is_me to tell whether this line was one of them.

Only iMessage carries mentions. On a received message this is populated when the sender was on iMessage; SMS and RCS have no way to mark a mention, so a message from an SMS or RCS participant arrives as plain text with mentions null, even in a group where other participants are on iMessage.

handle: string

Address of the mentioned participant, exactly as the device recorded it — an E.164 phone number or an email address.

is_me: boolean

Whether the mentioned participant is this line.

range: Array<number>

Character range [start, end) in value highlighted as this mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

Character range [start, end) in the value string where the decoration applies. start is inclusive, end is exclusive. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

animation?: "big" | "small" | "shake" | 5 more

Animated text effect to apply. Mutually exclusive with style.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
SchemasMediaPartResponse { id, filename, mime_type, 3 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
SchemasLinkPartResponse { type, value }

A rich link preview part

type: "link"

Indicates this is a rich link preview part

value: string

The URL

SchemasIMessageAppPartResponse { app, layout, type, 2 more }

An iMessage app card part.

app: App { bundle_id, name, team_id, app_store_id }

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: string

Bundle identifier of the Messages app extension.

name: string

Display name of the app.

team_id: string

The app’s 10-character team identifier.

app_store_id?: number | null

The owning app’s App Store id, when known.

formatint64
layout: Layout { caption, subcaption, trailing_caption, trailing_subcaption }

Visible layout of the card.

caption?: string | null

Primary label, top-left and bold.

subcaption?: string | null

Secondary label, below caption on the left.

trailing_caption?: string | null

Label shown top-right.

trailing_subcaption?: string | null

Label shown below trailing_caption.

type: "imessage_app"

Indicates this is an iMessage app card part.

url: string

The URL the recipient’s app opens when the user taps the card.

formaturi
fallback_text?: string | null

Fallback text for surfaces that cannot render the card.

SchemasAppClipPartResponse { type, value, description, 2 more }

An Apple Pay App Clip payment card part

type: "app_clip"

Indicates this is an App Clip payment card part

value: string

The checkout link the card opens

description?: string

The card’s summary line, composed by Linq from the checkout session

image_url?: string

The card’s preview image

title?: string

The card’s headline, composed by Linq from the checkout session

sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The handle that sent this message

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
delivered_at?: string | null

When the message was delivered. Null if not yet delivered.

formatdate-time
effect?: SchemasMessageEffect { name, type } | null

iMessage effect applied to a message (screen or bubble animation)

name?: string

Effect name (confetti, fireworks, slam, gentle, etc.)

type?: "screen" | "bubble"

Effect category

One of the following:
"screen"
"bubble"
idempotency_key?: string | null

Idempotency key for deduplication of outbound messages.

preferred_service?: "iMessage" | "SMS" | "RCS" | "auto" | null

Preferred messaging service type. Includes “auto” for default fallback behavior.

One of the following:
"iMessage"
"SMS"
"RCS"
"auto"
read_at?: string | null

When the message was read. Null if not yet read.

formatdate-time
reconciled_at?: string

Present only when this message was recovered by reconciliation rather than delivered live, and set to the time of that recovery. The field is omitted entirely for normally-delivered messages, which is the overwhelming majority. When present, expect sent_at to be substantially earlier than delivery of this event: the message is genuine but is arriving late and out of real-time order, so treat it as history rather than as a live inbound (for example, suppress auto-replies).

formatdate-time
reply_to?: ReplyTo | null

Reference to the message this is replying to (for threaded replies)

message_id?: string

ID of the message being replied to

formatuuid
part_index?: number

Index of the part being replied to

formatint32
minimum0
sent_at?: string | null

When the message was sent. Null if not yet sent.

formatdate-time
zero_retention?: boolean

True when this message was sent on a zero-day-retention line. parts is always empty in that case — Linq never persists this message’s content, so there is nothing to include here, not even a count or type of what was sent.

MessagePayload { id, created_at, delivered_at, 10 more }

Message content nested within webhook events

id?: string

Message identifier

formatuuid
created_at?: string

When the message record was created

formatdate-time
delivered_at?: string | null

When the message was delivered

formatdate-time
effect?: SchemasMessageEffect { name, type }

iMessage effect applied to a message (screen or bubble animation)

name?: string

Effect name (confetti, fireworks, slam, gentle, etc.)

type?: "screen" | "bubble"

Effect category

One of the following:
"screen"
"bubble"
is_delivered?: boolean

Whether the message has been delivered

is_read?: boolean

Whether the message has been read

parts?: Array<SchemasTextPartResponse { type, value, mention, 3 more } | SchemasMediaPartResponse { id, filename, mime_type, 3 more } | SchemasLinkPartResponse { type, value } | 2 more>

Message content parts (text and/or media)

One of the following:
SchemasTextPartResponse { type, value, mention, 3 more }

A text message part

type: "text"

Indicates this is a text message part

value: string

The text content

Deprecatedmention?: string | null

DEPRECATED: Use mentions instead. Handle (E.164 phone number or Apple ID email) of the first mention on this part. A part may carry several mentions; this field shows only the first in value order, so it cannot be used to determine whether a given participant was mentioned. null when the part carries no mention.

Deprecatedmention_range?: Array<number> | null

DEPRECATED: Use mentions[].range instead. Character range [start, end) in value highlighted as the first mention only. null when the range was omitted (the whole value is highlighted) or the part carries no mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

mentions?: Array<Mention> | null

Every mention on this part, in the order they appear in value. null when the part carries no mention. A part can carry several mentions of different people — check is_me to tell whether this line was one of them.

Only iMessage carries mentions. On a received message this is populated when the sender was on iMessage; SMS and RCS have no way to mark a mention, so a message from an SMS or RCS participant arrives as plain text with mentions null, even in a group where other participants are on iMessage.

handle: string

Address of the mentioned participant, exactly as the device recorded it — an E.164 phone number or an email address.

is_me: boolean

Whether the mentioned participant is this line.

range: Array<number>

Character range [start, end) in value highlighted as this mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

Character range [start, end) in the value string where the decoration applies. start is inclusive, end is exclusive. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

animation?: "big" | "small" | "shake" | 5 more

Animated text effect to apply. Mutually exclusive with style.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
SchemasMediaPartResponse { id, filename, mime_type, 3 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
SchemasLinkPartResponse { type, value }

A rich link preview part

type: "link"

Indicates this is a rich link preview part

value: string

The URL

SchemasIMessageAppPartResponse { app, layout, type, 2 more }

An iMessage app card part.

app: App { bundle_id, name, team_id, app_store_id }

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: string

Bundle identifier of the Messages app extension.

name: string

Display name of the app.

team_id: string

The app’s 10-character team identifier.

app_store_id?: number | null

The owning app’s App Store id, when known.

formatint64
layout: Layout { caption, subcaption, trailing_caption, trailing_subcaption }

Visible layout of the card.

caption?: string | null

Primary label, top-left and bold.

subcaption?: string | null

Secondary label, below caption on the left.

trailing_caption?: string | null

Label shown top-right.

trailing_subcaption?: string | null

Label shown below trailing_caption.

type: "imessage_app"

Indicates this is an iMessage app card part.

url: string

The URL the recipient’s app opens when the user taps the card.

formaturi
fallback_text?: string | null

Fallback text for surfaces that cannot render the card.

SchemasAppClipPartResponse { type, value, description, 2 more }

An Apple Pay App Clip payment card part

type: "app_clip"

Indicates this is an App Clip payment card part

value: string

The checkout link the card opens

description?: string

The card’s summary line, composed by Linq from the checkout session

image_url?: string

The card’s preview image

title?: string

The card’s headline, composed by Linq from the checkout session

read_at?: string | null

When the message was read

formatdate-time
reconciled_at?: string

Present only when this message was recovered by reconciliation rather than delivered live, and set to the time of that recovery. The field is omitted entirely for normally-delivered messages, which is the overwhelming majority. When present, expect sent_at to be substantially earlier than delivery of this event: the message is genuine but is arriving late and out of real-time order, so treat it as history rather than as a live inbound (for example, suppress auto-replies).

formatdate-time
reply_to?: ReplyTo { message_id, part_index }

Reference to the message this is replying to

message_id?: string

The ID of the message being replied to

formatuuid
part_index?: number

Index of the message part being replied to (0-based)

formatint32
minimum0
sent_at?: string | null

When the message was sent

formatdate-time
updated_at?: string

When the message record was last updated

formatdate-time
zero_retention?: boolean

True when this message was sent on a zero-day-retention line. parts is always empty in that case — Linq never persists this message’s content, so there is nothing to include here, not even a count or type of what was sent.

ReactionEventBase { is_from_me, reaction_type, chat_id, 9 more }
is_from_me: boolean

Whether this reaction was from the owner of the phone number (true) or from someone else (false)

reaction_type: ReactionType

Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type “custom” with the actual emoji in the custom_emoji field. Sticker reactions have type “sticker” with sticker attachment details in the sticker field.

One of the following:
"love"
"like"
"dislike"
"laugh"
"emphasize"
"question"
"custom"
"sticker"
chat_id?: string

Chat identifier (UUID)

custom_emoji?: string | null

The actual emoji when reaction_type is “custom”. Null for standard tapbacks.

Deprecatedfrom?: string

DEPRECATED: Use from_handle instead. Phone number or email address of the person who added/removed the reaction.

from_handle?: ChatHandle { id, handle, joined_at, 4 more }

The person who added/removed the reaction as a full handle object

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
message_id?: string

Message identifier (UUID) that the reaction was added to or removed from

part_index?: number

Index of the message part that was reacted to (0-based)

formatint32
minimum0
reacted_at?: string

When the reaction was added or removed

formatdate-time
reaction_id?: string

Identifier for this reaction. Pass it to PATCH /v3/messages/{messageId}/reactions/{reactionId} to move a sticker. Stickers stack, so this is what distinguishes one sticker from another on the same message.

formatuuid
service?: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
sticker?: Sticker | null

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name?: string

Filename of the sticker

height?: number

Sticker image height in pixels

mime_type?: string

MIME type of the sticker image

url?: string

Presigned URL for downloading the sticker image (expires in 1 hour).

formaturi
width?: number

Sticker image width in pixels

SchemasMediaPartResponse { id, filename, mime_type, 3 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
SchemasMessageEffect { name, type }

iMessage effect applied to a message (screen or bubble animation)

name?: string

Effect name (confetti, fireworks, slam, gentle, etc.)

type?: "screen" | "bubble"

Effect category

One of the following:
"screen"
"bubble"
SchemasTextPartResponse { type, value, mention, 3 more }

A text message part

type: "text"

Indicates this is a text message part

value: string

The text content

Deprecatedmention?: string | null

DEPRECATED: Use mentions instead. Handle (E.164 phone number or Apple ID email) of the first mention on this part. A part may carry several mentions; this field shows only the first in value order, so it cannot be used to determine whether a given participant was mentioned. null when the part carries no mention.

Deprecatedmention_range?: Array<number> | null

DEPRECATED: Use mentions[].range instead. Character range [start, end) in value highlighted as the first mention only. null when the range was omitted (the whole value is highlighted) or the part carries no mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

mentions?: Array<Mention> | null

Every mention on this part, in the order they appear in value. null when the part carries no mention. A part can carry several mentions of different people — check is_me to tell whether this line was one of them.

Only iMessage carries mentions. On a received message this is populated when the sender was on iMessage; SMS and RCS have no way to mark a mention, so a message from an SMS or RCS participant arrives as plain text with mentions null, even in a group where other participants are on iMessage.

handle: string

Address of the mentioned participant, exactly as the device recorded it — an E.164 phone number or an email address.

is_me: boolean

Whether the mentioned participant is this line.

range: Array<number>

Character range [start, end) in value highlighted as this mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

Character range [start, end) in the value string where the decoration applies. start is inclusive, end is exclusive. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

animation?: "big" | "small" | "shake" | 5 more

Animated text effect to apply. Mutually exclusive with style.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
MessageSentWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for message.sent events (2026-02-03 format)

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: MessageEventV2 { id, chat, direction, 12 more }

Unified payload for message webhooks when using webhook_version: "2026-02-03".

This schema is used for message.sent, message.received, message.delivered, and message.read events when the subscription URL includes ?version=2026-02-03.

Key differences from V1 (2025-01-01):

  • direction: “inbound” or “outbound” instead of is_from_me boolean
  • sender_handle: Full handle object for the sender
  • chat: Nested object with id, is_group, and owner_handle
  • Message fields (id, parts, effect, etc.) are at the top level, not nested in message

Timestamps indicate the message state:

  • message.sent: sent_at set, delivered_at=null, read_at=null
  • message.received: sent_at set, delivered_at=null, read_at=null
  • message.delivered: sent_at set, delivered_at set, read_at=null
  • message.read: sent_at set, delivered_at set, read_at set
id: string

Message identifier

formatuuid
chat: Chat { id, health_status, is_group, owner_handle }

Chat information

id: string

Chat identifier

formatuuid
health_status: HealthStatus { doc_url, status, updated_at }

[BETA] Current health for a chat. Always present — chats start at HEALTHY and may shift based on engagement and delivery signals on the conversation. Many AT_RISK or CRITICAL chats on a single line increase the risk of line flagging.

Switch on status to surface chat and line health in your UI — the enum is the long-term contract. Each status carries a doc_url that deep-links to the relevant section of the Chat Health guide. To gate a send, act on the response rather than the status: a 403 is the authoritative answer.

See the Chat Health guide for what each status means and how to react.

doc_url: string

Deep-link to the relevant section of the Chat Health guide for this status.

formaturi
status: "HEALTHY" | "AT_RISK" | "CRITICAL" | "OPTED_OUT"

Current health bucket for the chat. See the Chat Health guide for what each value means and how to react. doc_url deep-links to the relevant section.

OPTED_OUT — the recipient sent STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, or QUIT. The keyword must be the whole trimmed message, never part of a longer one: STOP counts, please stop does not. Most keywords must match exactly, including case. OPT OUT is the exception — it matches in any casing, with or without the space or a hyphen, so opt out, Opt-Out and optout all count. It clears as soon as they reply again: any later message from them that is not itself an opt-out keyword opts them back in immediately — a reply in any conversation with you counts, the same way the block does.

OPTED_OUT marks only the conversation the keyword arrived in. The block below is wider than the mark, so a conversation still reading HEALTHY can be blocked as well — gate on the 403, not on the status. Group threads are never marked and are never blocked.

Linq enforces this: while a recipient is opted out, every send to them is rejected with 403 (error code 2024) before the message is queued, across every chat and every line on your account. Nothing is delivered, including a final courtesy message — to send one, set override_optout: true on that single request.

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
"OPTED_OUT"
updated_at: string

When this status last changed.

formatdate-time
is_group?: boolean | null

Whether this is a group chat

owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Your phone number’s handle. Always has is_me=true.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"

Message direction - “outbound” if sent by you, “inbound” if received

One of the following:
"inbound"
"outbound"
parts: Array<SchemasTextPartResponse { type, value, mention, 3 more } | SchemasMediaPartResponse { id, filename, mime_type, 3 more } | SchemasLinkPartResponse { type, value } | 2 more>

Message parts (text and/or media)

One of the following:
SchemasTextPartResponse { type, value, mention, 3 more }

A text message part

type: "text"

Indicates this is a text message part

value: string

The text content

Deprecatedmention?: string | null

DEPRECATED: Use mentions instead. Handle (E.164 phone number or Apple ID email) of the first mention on this part. A part may carry several mentions; this field shows only the first in value order, so it cannot be used to determine whether a given participant was mentioned. null when the part carries no mention.

Deprecatedmention_range?: Array<number> | null

DEPRECATED: Use mentions[].range instead. Character range [start, end) in value highlighted as the first mention only. null when the range was omitted (the whole value is highlighted) or the part carries no mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

mentions?: Array<Mention> | null

Every mention on this part, in the order they appear in value. null when the part carries no mention. A part can carry several mentions of different people — check is_me to tell whether this line was one of them.

Only iMessage carries mentions. On a received message this is populated when the sender was on iMessage; SMS and RCS have no way to mark a mention, so a message from an SMS or RCS participant arrives as plain text with mentions null, even in a group where other participants are on iMessage.

handle: string

Address of the mentioned participant, exactly as the device recorded it — an E.164 phone number or an email address.

is_me: boolean

Whether the mentioned participant is this line.

range: Array<number>

Character range [start, end) in value highlighted as this mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

Character range [start, end) in the value string where the decoration applies. start is inclusive, end is exclusive. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

animation?: "big" | "small" | "shake" | 5 more

Animated text effect to apply. Mutually exclusive with style.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
SchemasMediaPartResponse { id, filename, mime_type, 3 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
SchemasLinkPartResponse { type, value }

A rich link preview part

type: "link"

Indicates this is a rich link preview part

value: string

The URL

SchemasIMessageAppPartResponse { app, layout, type, 2 more }

An iMessage app card part.

app: App { bundle_id, name, team_id, app_store_id }

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: string

Bundle identifier of the Messages app extension.

name: string

Display name of the app.

team_id: string

The app’s 10-character team identifier.

app_store_id?: number | null

The owning app’s App Store id, when known.

formatint64
layout: Layout { caption, subcaption, trailing_caption, trailing_subcaption }

Visible layout of the card.

caption?: string | null

Primary label, top-left and bold.

subcaption?: string | null

Secondary label, below caption on the left.

trailing_caption?: string | null

Label shown top-right.

trailing_subcaption?: string | null

Label shown below trailing_caption.

type: "imessage_app"

Indicates this is an iMessage app card part.

url: string

The URL the recipient’s app opens when the user taps the card.

formaturi
fallback_text?: string | null

Fallback text for surfaces that cannot render the card.

SchemasAppClipPartResponse { type, value, description, 2 more }

An Apple Pay App Clip payment card part

type: "app_clip"

Indicates this is an App Clip payment card part

value: string

The checkout link the card opens

description?: string

The card’s summary line, composed by Linq from the checkout session

image_url?: string

The card’s preview image

title?: string

The card’s headline, composed by Linq from the checkout session

sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The handle that sent this message

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
delivered_at?: string | null

When the message was delivered. Null if not yet delivered.

formatdate-time
effect?: SchemasMessageEffect { name, type } | null

iMessage effect applied to a message (screen or bubble animation)

name?: string

Effect name (confetti, fireworks, slam, gentle, etc.)

type?: "screen" | "bubble"

Effect category

One of the following:
"screen"
"bubble"
idempotency_key?: string | null

Idempotency key for deduplication of outbound messages.

preferred_service?: "iMessage" | "SMS" | "RCS" | "auto" | null

Preferred messaging service type. Includes “auto” for default fallback behavior.

One of the following:
"iMessage"
"SMS"
"RCS"
"auto"
read_at?: string | null

When the message was read. Null if not yet read.

formatdate-time
reconciled_at?: string

Present only when this message was recovered by reconciliation rather than delivered live, and set to the time of that recovery. The field is omitted entirely for normally-delivered messages, which is the overwhelming majority. When present, expect sent_at to be substantially earlier than delivery of this event: the message is genuine but is arriving late and out of real-time order, so treat it as history rather than as a live inbound (for example, suppress auto-replies).

formatdate-time
reply_to?: ReplyTo | null

Reference to the message this is replying to (for threaded replies)

message_id?: string

ID of the message being replied to

formatuuid
part_index?: number

Index of the part being replied to

formatint32
minimum0
sent_at?: string | null

When the message was sent. Null if not yet sent.

formatdate-time
zero_retention?: boolean

True when this message was sent on a zero-day-retention line. parts is always empty in that case — Linq never persists this message’s content, so there is nothing to include here, not even a count or type of what was sent.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

MessageReceivedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for message.received events (2026-02-03 format)

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: MessageEventV2 { id, chat, direction, 12 more }

Unified payload for message webhooks when using webhook_version: "2026-02-03".

This schema is used for message.sent, message.received, message.delivered, and message.read events when the subscription URL includes ?version=2026-02-03.

Key differences from V1 (2025-01-01):

  • direction: “inbound” or “outbound” instead of is_from_me boolean
  • sender_handle: Full handle object for the sender
  • chat: Nested object with id, is_group, and owner_handle
  • Message fields (id, parts, effect, etc.) are at the top level, not nested in message

Timestamps indicate the message state:

  • message.sent: sent_at set, delivered_at=null, read_at=null
  • message.received: sent_at set, delivered_at=null, read_at=null
  • message.delivered: sent_at set, delivered_at set, read_at=null
  • message.read: sent_at set, delivered_at set, read_at set
id: string

Message identifier

formatuuid
chat: Chat { id, health_status, is_group, owner_handle }

Chat information

id: string

Chat identifier

formatuuid
health_status: HealthStatus { doc_url, status, updated_at }

[BETA] Current health for a chat. Always present — chats start at HEALTHY and may shift based on engagement and delivery signals on the conversation. Many AT_RISK or CRITICAL chats on a single line increase the risk of line flagging.

Switch on status to surface chat and line health in your UI — the enum is the long-term contract. Each status carries a doc_url that deep-links to the relevant section of the Chat Health guide. To gate a send, act on the response rather than the status: a 403 is the authoritative answer.

See the Chat Health guide for what each status means and how to react.

doc_url: string

Deep-link to the relevant section of the Chat Health guide for this status.

formaturi
status: "HEALTHY" | "AT_RISK" | "CRITICAL" | "OPTED_OUT"

Current health bucket for the chat. See the Chat Health guide for what each value means and how to react. doc_url deep-links to the relevant section.

OPTED_OUT — the recipient sent STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, or QUIT. The keyword must be the whole trimmed message, never part of a longer one: STOP counts, please stop does not. Most keywords must match exactly, including case. OPT OUT is the exception — it matches in any casing, with or without the space or a hyphen, so opt out, Opt-Out and optout all count. It clears as soon as they reply again: any later message from them that is not itself an opt-out keyword opts them back in immediately — a reply in any conversation with you counts, the same way the block does.

OPTED_OUT marks only the conversation the keyword arrived in. The block below is wider than the mark, so a conversation still reading HEALTHY can be blocked as well — gate on the 403, not on the status. Group threads are never marked and are never blocked.

Linq enforces this: while a recipient is opted out, every send to them is rejected with 403 (error code 2024) before the message is queued, across every chat and every line on your account. Nothing is delivered, including a final courtesy message — to send one, set override_optout: true on that single request.

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
"OPTED_OUT"
updated_at: string

When this status last changed.

formatdate-time
is_group?: boolean | null

Whether this is a group chat

owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Your phone number’s handle. Always has is_me=true.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"

Message direction - “outbound” if sent by you, “inbound” if received

One of the following:
"inbound"
"outbound"
parts: Array<SchemasTextPartResponse { type, value, mention, 3 more } | SchemasMediaPartResponse { id, filename, mime_type, 3 more } | SchemasLinkPartResponse { type, value } | 2 more>

Message parts (text and/or media)

One of the following:
SchemasTextPartResponse { type, value, mention, 3 more }

A text message part

type: "text"

Indicates this is a text message part

value: string

The text content

Deprecatedmention?: string | null

DEPRECATED: Use mentions instead. Handle (E.164 phone number or Apple ID email) of the first mention on this part. A part may carry several mentions; this field shows only the first in value order, so it cannot be used to determine whether a given participant was mentioned. null when the part carries no mention.

Deprecatedmention_range?: Array<number> | null

DEPRECATED: Use mentions[].range instead. Character range [start, end) in value highlighted as the first mention only. null when the range was omitted (the whole value is highlighted) or the part carries no mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

mentions?: Array<Mention> | null

Every mention on this part, in the order they appear in value. null when the part carries no mention. A part can carry several mentions of different people — check is_me to tell whether this line was one of them.

Only iMessage carries mentions. On a received message this is populated when the sender was on iMessage; SMS and RCS have no way to mark a mention, so a message from an SMS or RCS participant arrives as plain text with mentions null, even in a group where other participants are on iMessage.

handle: string

Address of the mentioned participant, exactly as the device recorded it — an E.164 phone number or an email address.

is_me: boolean

Whether the mentioned participant is this line.

range: Array<number>

Character range [start, end) in value highlighted as this mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

Character range [start, end) in the value string where the decoration applies. start is inclusive, end is exclusive. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

animation?: "big" | "small" | "shake" | 5 more

Animated text effect to apply. Mutually exclusive with style.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
SchemasMediaPartResponse { id, filename, mime_type, 3 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
SchemasLinkPartResponse { type, value }

A rich link preview part

type: "link"

Indicates this is a rich link preview part

value: string

The URL

SchemasIMessageAppPartResponse { app, layout, type, 2 more }

An iMessage app card part.

app: App { bundle_id, name, team_id, app_store_id }

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: string

Bundle identifier of the Messages app extension.

name: string

Display name of the app.

team_id: string

The app’s 10-character team identifier.

app_store_id?: number | null

The owning app’s App Store id, when known.

formatint64
layout: Layout { caption, subcaption, trailing_caption, trailing_subcaption }

Visible layout of the card.

caption?: string | null

Primary label, top-left and bold.

subcaption?: string | null

Secondary label, below caption on the left.

trailing_caption?: string | null

Label shown top-right.

trailing_subcaption?: string | null

Label shown below trailing_caption.

type: "imessage_app"

Indicates this is an iMessage app card part.

url: string

The URL the recipient’s app opens when the user taps the card.

formaturi
fallback_text?: string | null

Fallback text for surfaces that cannot render the card.

SchemasAppClipPartResponse { type, value, description, 2 more }

An Apple Pay App Clip payment card part

type: "app_clip"

Indicates this is an App Clip payment card part

value: string

The checkout link the card opens

description?: string

The card’s summary line, composed by Linq from the checkout session

image_url?: string

The card’s preview image

title?: string

The card’s headline, composed by Linq from the checkout session

sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The handle that sent this message

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
delivered_at?: string | null

When the message was delivered. Null if not yet delivered.

formatdate-time
effect?: SchemasMessageEffect { name, type } | null

iMessage effect applied to a message (screen or bubble animation)

name?: string

Effect name (confetti, fireworks, slam, gentle, etc.)

type?: "screen" | "bubble"

Effect category

One of the following:
"screen"
"bubble"
idempotency_key?: string | null

Idempotency key for deduplication of outbound messages.

preferred_service?: "iMessage" | "SMS" | "RCS" | "auto" | null

Preferred messaging service type. Includes “auto” for default fallback behavior.

One of the following:
"iMessage"
"SMS"
"RCS"
"auto"
read_at?: string | null

When the message was read. Null if not yet read.

formatdate-time
reconciled_at?: string

Present only when this message was recovered by reconciliation rather than delivered live, and set to the time of that recovery. The field is omitted entirely for normally-delivered messages, which is the overwhelming majority. When present, expect sent_at to be substantially earlier than delivery of this event: the message is genuine but is arriving late and out of real-time order, so treat it as history rather than as a live inbound (for example, suppress auto-replies).

formatdate-time
reply_to?: ReplyTo | null

Reference to the message this is replying to (for threaded replies)

message_id?: string

ID of the message being replied to

formatuuid
part_index?: number

Index of the part being replied to

formatint32
minimum0
sent_at?: string | null

When the message was sent. Null if not yet sent.

formatdate-time
zero_retention?: boolean

True when this message was sent on a zero-day-retention line. parts is always empty in that case — Linq never persists this message’s content, so there is nothing to include here, not even a count or type of what was sent.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

MessageReadWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for message.read events (2026-02-03 format)

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: MessageEventV2 { id, chat, direction, 12 more }

Unified payload for message webhooks when using webhook_version: "2026-02-03".

This schema is used for message.sent, message.received, message.delivered, and message.read events when the subscription URL includes ?version=2026-02-03.

Key differences from V1 (2025-01-01):

  • direction: “inbound” or “outbound” instead of is_from_me boolean
  • sender_handle: Full handle object for the sender
  • chat: Nested object with id, is_group, and owner_handle
  • Message fields (id, parts, effect, etc.) are at the top level, not nested in message

Timestamps indicate the message state:

  • message.sent: sent_at set, delivered_at=null, read_at=null
  • message.received: sent_at set, delivered_at=null, read_at=null
  • message.delivered: sent_at set, delivered_at set, read_at=null
  • message.read: sent_at set, delivered_at set, read_at set
id: string

Message identifier

formatuuid
chat: Chat { id, health_status, is_group, owner_handle }

Chat information

id: string

Chat identifier

formatuuid
health_status: HealthStatus { doc_url, status, updated_at }

[BETA] Current health for a chat. Always present — chats start at HEALTHY and may shift based on engagement and delivery signals on the conversation. Many AT_RISK or CRITICAL chats on a single line increase the risk of line flagging.

Switch on status to surface chat and line health in your UI — the enum is the long-term contract. Each status carries a doc_url that deep-links to the relevant section of the Chat Health guide. To gate a send, act on the response rather than the status: a 403 is the authoritative answer.

See the Chat Health guide for what each status means and how to react.

doc_url: string

Deep-link to the relevant section of the Chat Health guide for this status.

formaturi
status: "HEALTHY" | "AT_RISK" | "CRITICAL" | "OPTED_OUT"

Current health bucket for the chat. See the Chat Health guide for what each value means and how to react. doc_url deep-links to the relevant section.

OPTED_OUT — the recipient sent STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, or QUIT. The keyword must be the whole trimmed message, never part of a longer one: STOP counts, please stop does not. Most keywords must match exactly, including case. OPT OUT is the exception — it matches in any casing, with or without the space or a hyphen, so opt out, Opt-Out and optout all count. It clears as soon as they reply again: any later message from them that is not itself an opt-out keyword opts them back in immediately — a reply in any conversation with you counts, the same way the block does.

OPTED_OUT marks only the conversation the keyword arrived in. The block below is wider than the mark, so a conversation still reading HEALTHY can be blocked as well — gate on the 403, not on the status. Group threads are never marked and are never blocked.

Linq enforces this: while a recipient is opted out, every send to them is rejected with 403 (error code 2024) before the message is queued, across every chat and every line on your account. Nothing is delivered, including a final courtesy message — to send one, set override_optout: true on that single request.

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
"OPTED_OUT"
updated_at: string

When this status last changed.

formatdate-time
is_group?: boolean | null

Whether this is a group chat

owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Your phone number’s handle. Always has is_me=true.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"

Message direction - “outbound” if sent by you, “inbound” if received

One of the following:
"inbound"
"outbound"
parts: Array<SchemasTextPartResponse { type, value, mention, 3 more } | SchemasMediaPartResponse { id, filename, mime_type, 3 more } | SchemasLinkPartResponse { type, value } | 2 more>

Message parts (text and/or media)

One of the following:
SchemasTextPartResponse { type, value, mention, 3 more }

A text message part

type: "text"

Indicates this is a text message part

value: string

The text content

Deprecatedmention?: string | null

DEPRECATED: Use mentions instead. Handle (E.164 phone number or Apple ID email) of the first mention on this part. A part may carry several mentions; this field shows only the first in value order, so it cannot be used to determine whether a given participant was mentioned. null when the part carries no mention.

Deprecatedmention_range?: Array<number> | null

DEPRECATED: Use mentions[].range instead. Character range [start, end) in value highlighted as the first mention only. null when the range was omitted (the whole value is highlighted) or the part carries no mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

mentions?: Array<Mention> | null

Every mention on this part, in the order they appear in value. null when the part carries no mention. A part can carry several mentions of different people — check is_me to tell whether this line was one of them.

Only iMessage carries mentions. On a received message this is populated when the sender was on iMessage; SMS and RCS have no way to mark a mention, so a message from an SMS or RCS participant arrives as plain text with mentions null, even in a group where other participants are on iMessage.

handle: string

Address of the mentioned participant, exactly as the device recorded it — an E.164 phone number or an email address.

is_me: boolean

Whether the mentioned participant is this line.

range: Array<number>

Character range [start, end) in value highlighted as this mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

Character range [start, end) in the value string where the decoration applies. start is inclusive, end is exclusive. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

animation?: "big" | "small" | "shake" | 5 more

Animated text effect to apply. Mutually exclusive with style.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
SchemasMediaPartResponse { id, filename, mime_type, 3 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
SchemasLinkPartResponse { type, value }

A rich link preview part

type: "link"

Indicates this is a rich link preview part

value: string

The URL

SchemasIMessageAppPartResponse { app, layout, type, 2 more }

An iMessage app card part.

app: App { bundle_id, name, team_id, app_store_id }

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: string

Bundle identifier of the Messages app extension.

name: string

Display name of the app.

team_id: string

The app’s 10-character team identifier.

app_store_id?: number | null

The owning app’s App Store id, when known.

formatint64
layout: Layout { caption, subcaption, trailing_caption, trailing_subcaption }

Visible layout of the card.

caption?: string | null

Primary label, top-left and bold.

subcaption?: string | null

Secondary label, below caption on the left.

trailing_caption?: string | null

Label shown top-right.

trailing_subcaption?: string | null

Label shown below trailing_caption.

type: "imessage_app"

Indicates this is an iMessage app card part.

url: string

The URL the recipient’s app opens when the user taps the card.

formaturi
fallback_text?: string | null

Fallback text for surfaces that cannot render the card.

SchemasAppClipPartResponse { type, value, description, 2 more }

An Apple Pay App Clip payment card part

type: "app_clip"

Indicates this is an App Clip payment card part

value: string

The checkout link the card opens

description?: string

The card’s summary line, composed by Linq from the checkout session

image_url?: string

The card’s preview image

title?: string

The card’s headline, composed by Linq from the checkout session

sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The handle that sent this message

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
delivered_at?: string | null

When the message was delivered. Null if not yet delivered.

formatdate-time
effect?: SchemasMessageEffect { name, type } | null

iMessage effect applied to a message (screen or bubble animation)

name?: string

Effect name (confetti, fireworks, slam, gentle, etc.)

type?: "screen" | "bubble"

Effect category

One of the following:
"screen"
"bubble"
idempotency_key?: string | null

Idempotency key for deduplication of outbound messages.

preferred_service?: "iMessage" | "SMS" | "RCS" | "auto" | null

Preferred messaging service type. Includes “auto” for default fallback behavior.

One of the following:
"iMessage"
"SMS"
"RCS"
"auto"
read_at?: string | null

When the message was read. Null if not yet read.

formatdate-time
reconciled_at?: string

Present only when this message was recovered by reconciliation rather than delivered live, and set to the time of that recovery. The field is omitted entirely for normally-delivered messages, which is the overwhelming majority. When present, expect sent_at to be substantially earlier than delivery of this event: the message is genuine but is arriving late and out of real-time order, so treat it as history rather than as a live inbound (for example, suppress auto-replies).

formatdate-time
reply_to?: ReplyTo | null

Reference to the message this is replying to (for threaded replies)

message_id?: string

ID of the message being replied to

formatuuid
part_index?: number

Index of the part being replied to

formatint32
minimum0
sent_at?: string | null

When the message was sent. Null if not yet sent.

formatdate-time
zero_retention?: boolean

True when this message was sent on a zero-day-retention line. parts is always empty in that case — Linq never persists this message’s content, so there is nothing to include here, not even a count or type of what was sent.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

MessageDeliveredWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for message.delivered events (2026-02-03 format)

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: MessageEventV2 { id, chat, direction, 12 more }

Unified payload for message webhooks when using webhook_version: "2026-02-03".

This schema is used for message.sent, message.received, message.delivered, and message.read events when the subscription URL includes ?version=2026-02-03.

Key differences from V1 (2025-01-01):

  • direction: “inbound” or “outbound” instead of is_from_me boolean
  • sender_handle: Full handle object for the sender
  • chat: Nested object with id, is_group, and owner_handle
  • Message fields (id, parts, effect, etc.) are at the top level, not nested in message

Timestamps indicate the message state:

  • message.sent: sent_at set, delivered_at=null, read_at=null
  • message.received: sent_at set, delivered_at=null, read_at=null
  • message.delivered: sent_at set, delivered_at set, read_at=null
  • message.read: sent_at set, delivered_at set, read_at set
id: string

Message identifier

formatuuid
chat: Chat { id, health_status, is_group, owner_handle }

Chat information

id: string

Chat identifier

formatuuid
health_status: HealthStatus { doc_url, status, updated_at }

[BETA] Current health for a chat. Always present — chats start at HEALTHY and may shift based on engagement and delivery signals on the conversation. Many AT_RISK or CRITICAL chats on a single line increase the risk of line flagging.

Switch on status to surface chat and line health in your UI — the enum is the long-term contract. Each status carries a doc_url that deep-links to the relevant section of the Chat Health guide. To gate a send, act on the response rather than the status: a 403 is the authoritative answer.

See the Chat Health guide for what each status means and how to react.

doc_url: string

Deep-link to the relevant section of the Chat Health guide for this status.

formaturi
status: "HEALTHY" | "AT_RISK" | "CRITICAL" | "OPTED_OUT"

Current health bucket for the chat. See the Chat Health guide for what each value means and how to react. doc_url deep-links to the relevant section.

OPTED_OUT — the recipient sent STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, or QUIT. The keyword must be the whole trimmed message, never part of a longer one: STOP counts, please stop does not. Most keywords must match exactly, including case. OPT OUT is the exception — it matches in any casing, with or without the space or a hyphen, so opt out, Opt-Out and optout all count. It clears as soon as they reply again: any later message from them that is not itself an opt-out keyword opts them back in immediately — a reply in any conversation with you counts, the same way the block does.

OPTED_OUT marks only the conversation the keyword arrived in. The block below is wider than the mark, so a conversation still reading HEALTHY can be blocked as well — gate on the 403, not on the status. Group threads are never marked and are never blocked.

Linq enforces this: while a recipient is opted out, every send to them is rejected with 403 (error code 2024) before the message is queued, across every chat and every line on your account. Nothing is delivered, including a final courtesy message — to send one, set override_optout: true on that single request.

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
"OPTED_OUT"
updated_at: string

When this status last changed.

formatdate-time
is_group?: boolean | null

Whether this is a group chat

owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Your phone number’s handle. Always has is_me=true.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"

Message direction - “outbound” if sent by you, “inbound” if received

One of the following:
"inbound"
"outbound"
parts: Array<SchemasTextPartResponse { type, value, mention, 3 more } | SchemasMediaPartResponse { id, filename, mime_type, 3 more } | SchemasLinkPartResponse { type, value } | 2 more>

Message parts (text and/or media)

One of the following:
SchemasTextPartResponse { type, value, mention, 3 more }

A text message part

type: "text"

Indicates this is a text message part

value: string

The text content

Deprecatedmention?: string | null

DEPRECATED: Use mentions instead. Handle (E.164 phone number or Apple ID email) of the first mention on this part. A part may carry several mentions; this field shows only the first in value order, so it cannot be used to determine whether a given participant was mentioned. null when the part carries no mention.

Deprecatedmention_range?: Array<number> | null

DEPRECATED: Use mentions[].range instead. Character range [start, end) in value highlighted as the first mention only. null when the range was omitted (the whole value is highlighted) or the part carries no mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

mentions?: Array<Mention> | null

Every mention on this part, in the order they appear in value. null when the part carries no mention. A part can carry several mentions of different people — check is_me to tell whether this line was one of them.

Only iMessage carries mentions. On a received message this is populated when the sender was on iMessage; SMS and RCS have no way to mark a mention, so a message from an SMS or RCS participant arrives as plain text with mentions null, even in a group where other participants are on iMessage.

handle: string

Address of the mentioned participant, exactly as the device recorded it — an E.164 phone number or an email address.

is_me: boolean

Whether the mentioned participant is this line.

range: Array<number>

Character range [start, end) in value highlighted as this mention. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

text_decorations?: Array<TextDecoration { range, animation, style } > | null

Text decorations applied to character ranges in the value

range: Array<number>

Character range [start, end) in the value string where the decoration applies. start is inclusive, end is exclusive. Characters are measured as UTF-16 code units. Most characters count as 1; some emoji count as 2.

animation?: "big" | "small" | "shake" | 5 more

Animated text effect to apply. Mutually exclusive with style.

One of the following:
"big"
"small"
"shake"
"nod"
"explode"
"ripple"
"bloom"
"jitter"
style?: "bold" | "italic" | "strikethrough" | "underline"

Text style to apply. Mutually exclusive with animation.

One of the following:
"bold"
"italic"
"strikethrough"
"underline"
SchemasMediaPartResponse { id, filename, mime_type, 3 more }

A media attachment part

id: string

Unique attachment identifier

formatuuid
filename: string

Original filename

mime_type: string

MIME type of the file

size_bytes: number

File size in bytes

type: "media"

Indicates this is a media attachment part

url: string

Presigned URL for downloading the attachment (expires in 1 hour).

formaturi
SchemasLinkPartResponse { type, value }

A rich link preview part

type: "link"

Indicates this is a rich link preview part

value: string

The URL

SchemasIMessageAppPartResponse { app, layout, type, 2 more }

An iMessage app card part.

app: App { bundle_id, name, team_id, app_store_id }

Identifies the iMessage app (Messages app extension) that backs the card.

bundle_id: string

Bundle identifier of the Messages app extension.

name: string

Display name of the app.

team_id: string

The app’s 10-character team identifier.

app_store_id?: number | null

The owning app’s App Store id, when known.

formatint64
layout: Layout { caption, subcaption, trailing_caption, trailing_subcaption }

Visible layout of the card.

caption?: string | null

Primary label, top-left and bold.

subcaption?: string | null

Secondary label, below caption on the left.

trailing_caption?: string | null

Label shown top-right.

trailing_subcaption?: string | null

Label shown below trailing_caption.

type: "imessage_app"

Indicates this is an iMessage app card part.

url: string

The URL the recipient’s app opens when the user taps the card.

formaturi
fallback_text?: string | null

Fallback text for surfaces that cannot render the card.

SchemasAppClipPartResponse { type, value, description, 2 more }

An Apple Pay App Clip payment card part

type: "app_clip"

Indicates this is an App Clip payment card part

value: string

The checkout link the card opens

description?: string

The card’s summary line, composed by Linq from the checkout session

image_url?: string

The card’s preview image

title?: string

The card’s headline, composed by Linq from the checkout session

sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The handle that sent this message

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
delivered_at?: string | null

When the message was delivered. Null if not yet delivered.

formatdate-time
effect?: SchemasMessageEffect { name, type } | null

iMessage effect applied to a message (screen or bubble animation)

name?: string

Effect name (confetti, fireworks, slam, gentle, etc.)

type?: "screen" | "bubble"

Effect category

One of the following:
"screen"
"bubble"
idempotency_key?: string | null

Idempotency key for deduplication of outbound messages.

preferred_service?: "iMessage" | "SMS" | "RCS" | "auto" | null

Preferred messaging service type. Includes “auto” for default fallback behavior.

One of the following:
"iMessage"
"SMS"
"RCS"
"auto"
read_at?: string | null

When the message was read. Null if not yet read.

formatdate-time
reconciled_at?: string

Present only when this message was recovered by reconciliation rather than delivered live, and set to the time of that recovery. The field is omitted entirely for normally-delivered messages, which is the overwhelming majority. When present, expect sent_at to be substantially earlier than delivery of this event: the message is genuine but is arriving late and out of real-time order, so treat it as history rather than as a live inbound (for example, suppress auto-replies).

formatdate-time
reply_to?: ReplyTo | null

Reference to the message this is replying to (for threaded replies)

message_id?: string

ID of the message being replied to

formatuuid
part_index?: number

Index of the part being replied to

formatint32
minimum0
sent_at?: string | null

When the message was sent. Null if not yet sent.

formatdate-time
zero_retention?: boolean

True when this message was sent on a zero-day-retention line. parts is always empty in that case — Linq never persists this message’s content, so there is nothing to include here, not even a count or type of what was sent.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

MessageFailedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for message.failed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { code, failed_at, chat_id, 5 more }

Error details for message.failed webhook events. See WebhookErrorCode for the full error code reference.

In rare cases the message can still be delivered after this event fires — a message.delivered webhook for the same message ID may follow.

code: number

Error codes in webhook failure events. The possible set varies by event: message.failed and poll.failed can carry 3007, 4001, 4002, 4005, 4006, 4007, or 4008; the group update failure events (chat.group_name_update_failed, chat.group_icon_update_failed) carry 3007 or 4001; chat.background_update_failed carries 1005, 2011, 4001, or 5002.

failed_at: string

When the failure was detected

formatdate-time
chat_id?: string

Chat identifier (UUID)

detail_code?: number | null

Opaque diagnostic code identifying the specific failure class within code. Values are not enumerated and may change without notice — log it and include it in support requests, but do not branch on it.

message_id?: string

Message identifier (UUID)

preferred_service?: "iMessage" | "SMS" | "RCS" | "auto" | null

Preferred messaging service type. Includes “auto” for default fallback behavior.

One of the following:
"iMessage"
"SMS"
"RCS"
"auto"
reason?: string

Human-readable description of the failure

service?: ServiceType | null

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

MessageEditedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for message.edited events (2026-02-03 format only)

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, chat, direction, 4 more }

Payload for message.edited events (2026-02-03 format).

Describes which part of a message was edited and when. Only text parts can be edited. Only available for subscriptions using webhook_version: "2026-02-03".

id: string

Message identifier

formatuuid
chat: Chat { id, health_status, is_group, owner_handle }

Chat context

id: string

Chat identifier

formatuuid
health_status: HealthStatus { doc_url, status, updated_at }

[BETA] Current health for a chat. Always present — chats start at HEALTHY and may shift based on engagement and delivery signals on the conversation. Many AT_RISK or CRITICAL chats on a single line increase the risk of line flagging.

Switch on status to surface chat and line health in your UI — the enum is the long-term contract. Each status carries a doc_url that deep-links to the relevant section of the Chat Health guide. To gate a send, act on the response rather than the status: a 403 is the authoritative answer.

See the Chat Health guide for what each status means and how to react.

doc_url: string

Deep-link to the relevant section of the Chat Health guide for this status.

formaturi
status: "HEALTHY" | "AT_RISK" | "CRITICAL" | "OPTED_OUT"

Current health bucket for the chat. See the Chat Health guide for what each value means and how to react. doc_url deep-links to the relevant section.

OPTED_OUT — the recipient sent STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, or QUIT. The keyword must be the whole trimmed message, never part of a longer one: STOP counts, please stop does not. Most keywords must match exactly, including case. OPT OUT is the exception — it matches in any casing, with or without the space or a hyphen, so opt out, Opt-Out and optout all count. It clears as soon as they reply again: any later message from them that is not itself an opt-out keyword opts them back in immediately — a reply in any conversation with you counts, the same way the block does.

OPTED_OUT marks only the conversation the keyword arrived in. The block below is wider than the mark, so a conversation still reading HEALTHY can be blocked as well — gate on the 403, not on the status. Group threads are never marked and are never blocked.

Linq enforces this: while a recipient is opted out, every send to them is rejected with 403 (error code 2024) before the message is queued, across every chat and every line on your account. Nothing is delivered, including a final courtesy message — to send one, set override_optout: true on that single request.

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
"OPTED_OUT"
updated_at: string

When this status last changed.

formatdate-time
is_group: boolean

Whether this is a group chat

owner_handle: ChatHandle { id, handle, joined_at, 4 more }

The handle that owns this chat (your phone number)

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "outbound" | "inbound"

“outbound” if you sent the original message, “inbound” if you received it

One of the following:
"outbound"
"inbound"
edited_at: string

When the edit occurred

formatdate-time
part: Part { index, text }

The edited part

index: number

Zero-based index of the edited part within the message

text: string

New text content of the part

sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The handle that sent (and edited) this message

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
zero_retention?: boolean

True when the edited message is on a zero-day-retention line. Behavior differs by direction: on an outbound edit, part.text is empty — you already saw the real edited text once, synchronously, in the edit API response, and Linq never persists it. On an inbound edit, part.text is still the real text as received; zero-day-retention only means Linq never persists it.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ReactionAddedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for reaction.added events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: ReactionEventBase { is_from_me, reaction_type, chat_id, 9 more }

Payload for reaction.added webhook events

is_from_me: boolean

Whether this reaction was from the owner of the phone number (true) or from someone else (false)

reaction_type: ReactionType

Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type “custom” with the actual emoji in the custom_emoji field. Sticker reactions have type “sticker” with sticker attachment details in the sticker field.

One of the following:
"love"
"like"
"dislike"
"laugh"
"emphasize"
"question"
"custom"
"sticker"
chat_id?: string

Chat identifier (UUID)

custom_emoji?: string | null

The actual emoji when reaction_type is “custom”. Null for standard tapbacks.

Deprecatedfrom?: string

DEPRECATED: Use from_handle instead. Phone number or email address of the person who added/removed the reaction.

from_handle?: ChatHandle { id, handle, joined_at, 4 more }

The person who added/removed the reaction as a full handle object

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
message_id?: string

Message identifier (UUID) that the reaction was added to or removed from

part_index?: number

Index of the message part that was reacted to (0-based)

formatint32
minimum0
reacted_at?: string

When the reaction was added or removed

formatdate-time
reaction_id?: string

Identifier for this reaction. Pass it to PATCH /v3/messages/{messageId}/reactions/{reactionId} to move a sticker. Stickers stack, so this is what distinguishes one sticker from another on the same message.

formatuuid
service?: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
sticker?: Sticker | null

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name?: string

Filename of the sticker

height?: number

Sticker image height in pixels

mime_type?: string

MIME type of the sticker image

url?: string

Presigned URL for downloading the sticker image (expires in 1 hour).

formaturi
width?: number

Sticker image width in pixels

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ReactionRemovedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for reaction.removed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: ReactionEventBase { is_from_me, reaction_type, chat_id, 9 more }

Payload for reaction.removed webhook events

is_from_me: boolean

Whether this reaction was from the owner of the phone number (true) or from someone else (false)

reaction_type: ReactionType

Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type “custom” with the actual emoji in the custom_emoji field. Sticker reactions have type “sticker” with sticker attachment details in the sticker field.

One of the following:
"love"
"like"
"dislike"
"laugh"
"emphasize"
"question"
"custom"
"sticker"
chat_id?: string

Chat identifier (UUID)

custom_emoji?: string | null

The actual emoji when reaction_type is “custom”. Null for standard tapbacks.

Deprecatedfrom?: string

DEPRECATED: Use from_handle instead. Phone number or email address of the person who added/removed the reaction.

from_handle?: ChatHandle { id, handle, joined_at, 4 more }

The person who added/removed the reaction as a full handle object

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
message_id?: string

Message identifier (UUID) that the reaction was added to or removed from

part_index?: number

Index of the message part that was reacted to (0-based)

formatint32
minimum0
reacted_at?: string

When the reaction was added or removed

formatdate-time
reaction_id?: string

Identifier for this reaction. Pass it to PATCH /v3/messages/{messageId}/reactions/{reactionId} to move a sticker. Stickers stack, so this is what distinguishes one sticker from another on the same message.

formatuuid
service?: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
sticker?: Sticker | null

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name?: string

Filename of the sticker

height?: number

Sticker image height in pixels

mime_type?: string

MIME type of the sticker image

url?: string

Presigned URL for downloading the sticker image (expires in 1 hour).

formaturi
width?: number

Sticker image width in pixels

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollReceivedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.received events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, created_at, direction, 7 more }

Payload for poll.received — a poll created by someone else and delivered to your line. Carries the full poll snapshot (options, no voters yet) at receipt time.

chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
created_at: string
formatdate-time
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
message_id: string
formatuuid
poll: Poll { options, total_voters }
options: Array<Option>
can_be_edited: boolean
creator_handle: ChatHandle { id, handle, joined_at, 4 more }

The participant who added this option (poll creator for the initial options; whoever added later ones). On a poll.updated this differs from the event’s sender_handle whenever a remote participant added the option. Null when unknown.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
option_id: string
formatuuid
text: string
voters: Array<Voter>
handle: string
voted_at: string
formatdate-time
total_voters: number

Distinct participants across the whole poll.

received_at: string
formatdate-time
service: string
updated_at: string
formatdate-time
sender_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

The line that created the poll (is_me=false for an inbound poll).

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
zero_retention?: boolean

True when your line has zero-day-retention enabled. Unlike other poll webhooks, option text here is still the real, unstripped text as received — Linq never persists it in the database, but this webhook fires from the live inbound event, not a database read, so this is the one place it’s shown.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollSentWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.sent events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, created_at, direction, 9 more }

Payload for poll.sent, poll.delivered, and poll.read webhook events. Timestamps indicate state (null = not yet happened): sent → sent_at; delivered → +delivered_at; read → +read_at.

chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
created_at: string
formatdate-time
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
message_id: string
formatuuid
poll: Poll { options, total_voters }
options: Array<Option>
can_be_edited: boolean
creator_handle: ChatHandle { id, handle, joined_at, 4 more }

The participant who added this option (poll creator for the initial options; whoever added later ones). On a poll.updated this differs from the event’s sender_handle whenever a remote participant added the option. Null when unknown.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
option_id: string
formatuuid
text: string
voters: Array<Voter>
handle: string
voted_at: string
formatdate-time
total_voters: number

Distinct participants across the whole poll.

service: string
updated_at: string
formatdate-time
delivered_at?: string | null
formatdate-time
read_at?: string | null
formatdate-time
sender_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

The handle that sent the poll.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
sent_at?: string | null
formatdate-time
zero_retention?: boolean

True when this poll was sent on a zero-day-retention line. Every option’s text is empty in that case — Linq never persists poll option text, so there is nothing to include here. The real text was only ever shown once, synchronously, in the API response when the poll was created or added to.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollDeliveredWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.delivered events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, created_at, direction, 9 more }

Payload for poll.sent, poll.delivered, and poll.read webhook events. Timestamps indicate state (null = not yet happened): sent → sent_at; delivered → +delivered_at; read → +read_at.

chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
created_at: string
formatdate-time
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
message_id: string
formatuuid
poll: Poll { options, total_voters }
options: Array<Option>
can_be_edited: boolean
creator_handle: ChatHandle { id, handle, joined_at, 4 more }

The participant who added this option (poll creator for the initial options; whoever added later ones). On a poll.updated this differs from the event’s sender_handle whenever a remote participant added the option. Null when unknown.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
option_id: string
formatuuid
text: string
voters: Array<Voter>
handle: string
voted_at: string
formatdate-time
total_voters: number

Distinct participants across the whole poll.

service: string
updated_at: string
formatdate-time
delivered_at?: string | null
formatdate-time
read_at?: string | null
formatdate-time
sender_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

The handle that sent the poll.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
sent_at?: string | null
formatdate-time
zero_retention?: boolean

True when this poll was sent on a zero-day-retention line. Every option’s text is empty in that case — Linq never persists poll option text, so there is nothing to include here. The real text was only ever shown once, synchronously, in the API response when the poll was created or added to.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollReadWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.read events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, created_at, direction, 9 more }

Payload for poll.sent, poll.delivered, and poll.read webhook events. Timestamps indicate state (null = not yet happened): sent → sent_at; delivered → +delivered_at; read → +read_at.

chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
created_at: string
formatdate-time
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
message_id: string
formatuuid
poll: Poll { options, total_voters }
options: Array<Option>
can_be_edited: boolean
creator_handle: ChatHandle { id, handle, joined_at, 4 more }

The participant who added this option (poll creator for the initial options; whoever added later ones). On a poll.updated this differs from the event’s sender_handle whenever a remote participant added the option. Null when unknown.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
option_id: string
formatuuid
text: string
voters: Array<Voter>
handle: string
voted_at: string
formatdate-time
total_voters: number

Distinct participants across the whole poll.

service: string
updated_at: string
formatdate-time
delivered_at?: string | null
formatdate-time
read_at?: string | null
formatdate-time
sender_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

The handle that sent the poll.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
sent_at?: string | null
formatdate-time
zero_retention?: boolean

True when this poll was sent on a zero-day-retention line. Every option’s text is empty in that case — Linq never persists poll option text, so there is nothing to include here. The real text was only ever shown once, synchronously, in the API response when the poll was created or added to.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollUpdatedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.updated events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { added_options, chat, direction, 4 more }

Payload for poll.updated (option(s) added — add-only).

added_options: Array<AddedOption>

Only the options this update added — never the ones the poll already had. Fetch the poll to read its full option set.

can_be_edited: boolean
creator_handle: ChatHandle { id, handle, joined_at, 4 more }

The participant who added this option (poll creator for the initial options; whoever added later ones). On a poll.updated this differs from the event’s sender_handle whenever a remote participant added the option. Null when unknown.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
option_id: string
formatuuid
text: string
voters: Array<Voter>
handle: string
voted_at: string
formatdate-time
chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
message_id: string
formatuuid
sender_handle: ChatHandle { id, handle, joined_at, 4 more }

Your line — the one that received or sent this update. Always present. On an inbound update this is NOT who added the option: use added_options[].creator_handle for that, which will be the remote participant.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: string
zero_retention?: boolean

True when zero-day-retention applies to this update. Behavior differs by direction: on an inbound update, added_options[].text is the real text a participant just added; on an outbound update, it is empty — you already saw the real text once, synchronously, in the API response when you made the add, and this webhook is built from a database read, which never stored it.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollFailedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.failed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, direction, error, 6 more }

Payload for poll.failed — an outbound poll (or poll action) that failed to send. Carries the poll snapshot at failure time plus the error and when it failed.

chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
error: Error { code, message }
code: number

Error codes in webhook failure events. The possible set varies by event: message.failed and poll.failed can carry 3007, 4001, 4002, 4005, 4006, 4007, or 4008; the group update failure events (chat.group_name_update_failed, chat.group_icon_update_failed) carry 3007 or 4001; chat.background_update_failed carries 1005, 2011, 4001, or 5002.

message: string
failed_at: string
formatdate-time
message_id: string
formatuuid
poll: Poll { options, total_voters }
options: Array<Option>
can_be_edited: boolean
creator_handle: ChatHandle { id, handle, joined_at, 4 more }

The participant who added this option (poll creator for the initial options; whoever added later ones). On a poll.updated this differs from the event’s sender_handle whenever a remote participant added the option. Null when unknown.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
option_id: string
formatuuid
text: string
voters: Array<Voter>
handle: string
voted_at: string
formatdate-time
total_voters: number

Distinct participants across the whole poll.

service: string
sender_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Null on failure (the send never landed).

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
zero_retention?: boolean

True when this poll was sent on a zero-day-retention line. poll is built from the same database read as poll.sent/delivered/read, so every option’s text is empty.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollVoteAddedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.vote.added events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, direction, message_id, 4 more }

Payload for poll.vote.added and poll.vote.removed (one option toggled).

chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
message_id: string
formatuuid
option_id: string
formatuuid
sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The voter — always present.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: string
zero_retention?: boolean

True when this poll is on a zero-day-retention line. Votes are unaffected by zero-day-retention — a vote choice is always persisted and delivered regardless — this flag is informational only, telling you why this poll’s other webhooks (poll.sent, poll.updated, etc.) may carry empty option text.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollVoteRemovedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.vote.removed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, direction, message_id, 4 more }

Payload for poll.vote.added and poll.vote.removed (one option toggled).

chat: Chat { id, is_group, owner_handle }

Chat info for poll webhook events.

id: string
formatuuid
is_group?: boolean | null
owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null
id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
direction: "inbound" | "outbound"
One of the following:
"inbound"
"outbound"
message_id: string
formatuuid
option_id: string
formatuuid
sender_handle: ChatHandle { id, handle, joined_at, 4 more }

The voter — always present.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
service: string
zero_retention?: boolean

True when this poll is on a zero-day-retention line. Votes are unaffected by zero-day-retention — a vote choice is always persisted and delivered regardless — this flag is informational only, telling you why this poll’s other webhooks (poll.sent, poll.updated, etc.) may carry empty option text.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PollReactionAddedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for poll.reaction.added events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: ReactionEventBase { is_from_me, reaction_type, chat_id, 9 more }

Payload for poll.reaction.added — a reaction on a poll message. Same shape as reaction.added; message_id is the poll-definition message’s ID. Poll reactions are stickers, which iMessage cannot remove, so there is no removal counterpart.

is_from_me: boolean

Whether this reaction was from the owner of the phone number (true) or from someone else (false)

reaction_type: ReactionType

Type of reaction. Standard iMessage tapbacks are love, like, dislike, laugh, emphasize, question. Custom emoji reactions have type “custom” with the actual emoji in the custom_emoji field. Sticker reactions have type “sticker” with sticker attachment details in the sticker field.

One of the following:
"love"
"like"
"dislike"
"laugh"
"emphasize"
"question"
"custom"
"sticker"
chat_id?: string

Chat identifier (UUID)

custom_emoji?: string | null

The actual emoji when reaction_type is “custom”. Null for standard tapbacks.

Deprecatedfrom?: string

DEPRECATED: Use from_handle instead. Phone number or email address of the person who added/removed the reaction.

from_handle?: ChatHandle { id, handle, joined_at, 4 more }

The person who added/removed the reaction as a full handle object

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
message_id?: string

Message identifier (UUID) that the reaction was added to or removed from

part_index?: number

Index of the message part that was reacted to (0-based)

formatint32
minimum0
reacted_at?: string

When the reaction was added or removed

formatdate-time
reaction_id?: string

Identifier for this reaction. Pass it to PATCH /v3/messages/{messageId}/reactions/{reactionId} to move a sticker. Stickers stack, so this is what distinguishes one sticker from another on the same message.

formatuuid
service?: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
sticker?: Sticker | null

Sticker attachment details when reaction_type is “sticker”. Null for non-sticker reactions.

file_name?: string

Filename of the sticker

height?: number

Sticker image height in pixels

mime_type?: string

MIME type of the sticker image

url?: string

Presigned URL for downloading the sticker image (expires in 1 hour).

formaturi
width?: number

Sticker image width in pixels

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ParticipantAddedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for participant.added events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { handle, added_at, chat_id, participant }

Payload for participant.added webhook events

Deprecatedhandle: string

DEPRECATED: Use participant instead. Handle (phone number or email address) of the added participant.

added_at?: string

When the participant was added

formatdate-time
chat_id?: string

Chat identifier (UUID) of the group chat

participant?: ChatHandle { id, handle, joined_at, 4 more }

The added participant as a full handle object

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ParticipantRemovedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for participant.removed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { handle, chat_id, participant, removed_at }

Payload for participant.removed webhook events

Deprecatedhandle: string

DEPRECATED: Use participant instead. Handle (phone number or email address) of the removed participant.

chat_id?: string

Chat identifier (UUID) of the group chat

participant?: ChatHandle { id, handle, joined_at, 4 more }

The removed participant as a full handle object

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
removed_at?: string

When the participant was removed

formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatCreatedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.created events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, created_at, display_name, 5 more }

Payload for chat.created webhook events. Matches GET /v3/chats/{chatId} response.

id: string

Unique identifier for the chat

formatuuid
created_at: string

When the chat was created

formatdate-time
display_name: string | null

Display name for the chat. Defaults to a comma-separated list of recipient handles. Can be updated for group chats.

handles: Array<ChatHandle { id, handle, joined_at, 4 more } >

List of chat participants with full handle details. Always contains at least two handles (your phone number and the other participant).

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
health_status: HealthStatus { doc_url, status, updated_at }

[BETA] Current health for a chat. Always present — chats start at HEALTHY and may shift based on engagement and delivery signals on the conversation. Many AT_RISK or CRITICAL chats on a single line increase the risk of line flagging.

Switch on status to surface chat and line health in your UI — the enum is the long-term contract. Each status carries a doc_url that deep-links to the relevant section of the Chat Health guide. To gate a send, act on the response rather than the status: a 403 is the authoritative answer.

See the Chat Health guide for what each status means and how to react.

doc_url: string

Deep-link to the relevant section of the Chat Health guide for this status.

formaturi
status: "HEALTHY" | "AT_RISK" | "CRITICAL" | "OPTED_OUT"

Current health bucket for the chat. See the Chat Health guide for what each value means and how to react. doc_url deep-links to the relevant section.

OPTED_OUT — the recipient sent STOP, UNSUBSCRIBE, OPTOUT, CANCEL, END, or QUIT. The keyword must be the whole trimmed message, never part of a longer one: STOP counts, please stop does not. Most keywords must match exactly, including case. OPT OUT is the exception — it matches in any casing, with or without the space or a hyphen, so opt out, Opt-Out and optout all count. It clears as soon as they reply again: any later message from them that is not itself an opt-out keyword opts them back in immediately — a reply in any conversation with you counts, the same way the block does.

OPTED_OUT marks only the conversation the keyword arrived in. The block below is wider than the mark, so a conversation still reading HEALTHY can be blocked as well — gate on the 403, not on the status. Group threads are never marked and are never blocked.

Linq enforces this: while a recipient is opted out, every send to them is rejected with 403 (error code 2024) before the message is queued, across every chat and every line on your account. Nothing is delivered, including a final courtesy message — to send one, set override_optout: true on that single request.

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
"OPTED_OUT"
updated_at: string

When this status last changed.

formatdate-time
is_group: boolean

Whether this is a group chat

updated_at: string

When the chat was last updated

formatdate-time
service?: ServiceType | null

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatGroupNameUpdatedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.group_name_updated events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat_id, updated_at, changed_by_handle, 2 more }

Payload for chat.group_name_updated webhook events

chat_id: string

Chat identifier (UUID) of the group chat

updated_at: string

When the update occurred

formatdate-time
changed_by_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

The handle who made the change.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
new_value?: string | null

New group name (null if the name was removed)

old_value?: string | null

Previous group name (null if no previous name)

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatGroupIconUpdatedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.group_icon_updated events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat_id, updated_at, changed_by_handle, 2 more }

Payload for chat.group_icon_updated webhook events

chat_id: string

Chat identifier (UUID) of the group chat

updated_at: string

When the update occurred

formatdate-time
changed_by_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

The handle who made the change.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
new_value?: string | null

New icon URL (null if the icon was removed)

old_value?: string | null

Previous icon URL (null if no previous icon)

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatGroupNameUpdateFailedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.group_name_update_failed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat_id, error_code, failed_at }

Error details for chat.group_name_update_failed webhook events. See WebhookErrorCode for the full error code reference.

chat_id: string

Chat identifier (UUID) of the group chat

error_code: number

Error codes in webhook failure events. The possible set varies by event: message.failed and poll.failed can carry 3007, 4001, 4002, 4005, 4006, 4007, or 4008; the group update failure events (chat.group_name_update_failed, chat.group_icon_update_failed) carry 3007 or 4001; chat.background_update_failed carries 1005, 2011, 4001, or 5002.

failed_at: string

When the failure was detected

formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatGroupIconUpdateFailedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.group_icon_update_failed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat_id, error_code, failed_at }

Error details for chat.group_icon_update_failed webhook events. See WebhookErrorCode for the full error code reference.

chat_id: string

Chat identifier (UUID) of the group chat

error_code: number

Error codes in webhook failure events. The possible set varies by event: message.failed and poll.failed can carry 3007, 4001, 4002, 4005, 4006, 4007, or 4008; the group update failure events (chat.group_name_update_failed, chat.group_icon_update_failed) carry 3007 or 4001; chat.background_update_failed carries 1005, 2011, 4001, or 5002.

failed_at: string

When the failure was detected

formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatTypingIndicatorStartedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.typing_indicator.started events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat_id }

Payload for chat.typing_indicator.started webhook events

chat_id: string

Chat identifier

formatuuid
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatTypingIndicatorStoppedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.typing_indicator.stopped events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat_id }

Payload for chat.typing_indicator.stopped webhook events

chat_id: string

Chat identifier

formatuuid
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatBackgroundUpdatedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.background_updated events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat, actor_handle, background }

Payload for chat.background_updated webhook events.

chat: Chat { id, is_group, owner_handle }

Chat information

id: string

Chat identifier

formatuuid
is_group?: boolean | null

Whether this is a group chat

owner_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Your phone number’s handle. Always has is_me=true.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
actor_handle?: ChatHandle { id, handle, joined_at, 4 more } | null

Who changed it. is_me is true when your own number set it.

id: string

Unique identifier for this handle

formatuuid
handle: string

Phone number (E.164) or email address of the participant

joined_at: string

When this participant joined the chat

formatdate-time
service: ServiceType

Messaging service type

One of the following:
"iMessage"
"SMS"
"RCS"
is_me?: boolean | null

Whether this handle belongs to the sender (your phone number)

left_at?: string | null

When they left (if applicable)

formatdate-time
status?: "active" | "left" | "removed" | null

Participant status

One of the following:
"active"
"left"
"removed"
background?: Background | null

A chat transcript background. Fields are populated per type.

type: "color" | "dynamic" | "photo"

The background family.

One of the following:
"color"
"dynamic"
"photo"
image_url?: string | null

Photo: a hosted URL for the background image, whether you set it or a participant did. Apple stores the image, not the URL it came from, so the image is re-hosted and this is our URL rather than the one you supplied. null only if the image could not be hosted.

shades?: Array<string> | null

Color: the two gradient stops as hex, top then bottom.

style?: "sky" | "water" | "aurora" | "glitter" | null

Dynamic: the animated style.

One of the following:
"sky"
"water"
"aurora"
"glitter"
variant?: string | null

Color: custom (the stored two colors) or a named swatch. Dynamic: the variant within the style (e.g. sunrise).

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ChatBackgroundUpdateFailedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for chat.background_update_failed events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { chat_id, error_code, failed_at }

Error details for chat.background_update_failed webhook events. See WebhookErrorCode for the full error code reference.

chat_id: string

Chat identifier (UUID) whose background update failed

error_code: number

Error codes in webhook failure events. The possible set varies by event: message.failed and poll.failed can carry 3007, 4001, 4002, 4005, 4006, 4007, or 4008; the group update failure events (chat.group_name_update_failed, chat.group_icon_update_failed) carry 3007 or 4001; chat.background_update_failed carries 1005, 2011, 4001, or 5002.

failed_at: string

When the failure was detected

formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ContactCardReceivedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for contact_card.received events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { first_name, last_name, owner_handle, 2 more }

Payload for contact_card.received webhook events.

A contact belongs to a line, not to an individual chat. You receive one event per person who shares their contact, regardless of how many chats they have in common with your line.

The event fires again whenever the shared contact’s name or media changes.

first_name: string

First name from the shared contact card

last_name: string

Last name from the shared contact card (may be empty)

owner_handle: string

Which of your lines they shared it with.

sender_handle: string

The person who shared their card — a phone number or email address.

media_url?: string | null

URL of the contact’s media, served from cdn.linqapp.com. null when the contact shared no media, and also when media was shared but could not be retrieved — this field does not distinguish the two.

Download the media and store it yourself. The URL may be signed and expire, in as little as 45 minutes, and altering its query string invalidates it immediately.

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: WebhookEventType

Valid webhook event types that can be subscribed to.

Note: message.edited is only delivered to subscriptions using webhook_version: "2026-02-03". Subscribing to this event on a v2025 subscription will not produce any deliveries.

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PhoneNumberStatusUpdatedWebhookEvent { api_version, created_at, data, 5 more }

Complete webhook payload for phone_number.status_updated events

api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { changed_at, new_reputation, new_status, 3 more }

Payload for phone_number.status_updated webhook events

changed_at: string

When the status change occurred

formatdate-time
new_reputation: "HEALTHY" | "AT_RISK" | "CRITICAL"

The new line reputation

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
new_status: "ACTIVE" | "FLAGGED"

The new service status

One of the following:
"ACTIVE"
"FLAGGED"
phone_number: string

Phone number in E.164 format

previous_reputation: "HEALTHY" | "AT_RISK" | "CRITICAL"

The previous line reputation

One of the following:
"HEALTHY"
"AT_RISK"
"CRITICAL"
previous_status: "ACTIVE" | "FLAGGED"

The previous service status

One of the following:
"ACTIVE"
"FLAGGED"
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "message.sent" | "message.received" | "message.read" | 43 more

The type of event

One of the following:
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ConnectionCreatedWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, amount, checkout_url, 17 more }

The payment request, as returned by GET /v3/payment_requests/{paymentRequestId}.

id: string

The payment request id.

formatuuid
amount: number

What was charged at checkout, in the currency’s minor units. In subscription mode this is the first invoice’s total — all items after any discounts are applied.

formatint64
checkout_url: string

URL the recipient opens to pay (https://zero.linqapp.com/pay/{slug}?session=...).

created_at: string
formatdate-time
currency: string
object: string
status: "succeeded" | "failed" | "canceled" | "expired"
One of the following:
"succeeded"
"failed"
"canceled"
"expired"
description?: string
discount?: Discount { coupon, label, promotion_code }

Subscription mode — the discount Stripe applied, read back from the coupon. Absent when none was applied.

coupon?: string
label?: string

Name of the coupon/promo code displayed to customers.

promotion_code?: string
interval?: "day" | "week" | "month" | "year"

Subscription mode — how often the subscription renews.

One of the following:
"day"
"week"
"month"
"year"
interval_count?: number

Subscription mode — intervals per renewal.

formatint64
metadata?: Record<string, string>
mode?: "payment" | "subscription"

Whether the request collected a one-time charge or started a subscription.

One of the following:
"payment"
"subscription"
natural?: Natural { payment_request_id, transaction_id }

Natural-rail join keys, present when rail: natural.

payment_request_id?: string

The Natural payment request (prq_...).

transaction_id?: string

The settled transaction (txn_...).

price_id?: string

Subscription mode — the recurring price subscribed to.

quantity?: number

Subscription mode — units of the price subscribed to.

formatint64
rail?: "stripe" | "natural"

The rail this request settled on.

One of the following:
"stripe"
"natural"
stripe?: Stripe { customer_id, payment_intent_id, subscription_id }

Ids of the Stripe objects on your connected account — join keys into your own Stripe Dashboard/API. Manage a subscription’s post-checkout lifecycle with subscription_id.

customer_id?: string

The Customer the request is attached to (cus_...). Always set in subscription mode; set in payment mode only when the request was created with a customer_id.

payment_intent_id?: string

The PaymentIntent collected at checkout (pi_...).

subscription_id?: string

Subscription mode — the Subscription (sub_...).

trial_end?: string

Subscription mode — when the free trial ends and the first charge happens. On a trial request, payment.succeeded means the payment method was collected ($0 moved).

formatdate-time
updated_at?: string
formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "payment.succeeded" | "payment.canceled" | "payment.expired" | 43 more
One of the following:
"payment.succeeded"
"payment.canceled"
"payment.expired"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

ConnectionRevokedWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, amount, checkout_url, 17 more }

The payment request, as returned by GET /v3/payment_requests/{paymentRequestId}.

id: string

The payment request id.

formatuuid
amount: number

What was charged at checkout, in the currency’s minor units. In subscription mode this is the first invoice’s total — all items after any discounts are applied.

formatint64
checkout_url: string

URL the recipient opens to pay (https://zero.linqapp.com/pay/{slug}?session=...).

created_at: string
formatdate-time
currency: string
object: string
status: "succeeded" | "failed" | "canceled" | "expired"
One of the following:
"succeeded"
"failed"
"canceled"
"expired"
description?: string
discount?: Discount { coupon, label, promotion_code }

Subscription mode — the discount Stripe applied, read back from the coupon. Absent when none was applied.

coupon?: string
label?: string

Name of the coupon/promo code displayed to customers.

promotion_code?: string
interval?: "day" | "week" | "month" | "year"

Subscription mode — how often the subscription renews.

One of the following:
"day"
"week"
"month"
"year"
interval_count?: number

Subscription mode — intervals per renewal.

formatint64
metadata?: Record<string, string>
mode?: "payment" | "subscription"

Whether the request collected a one-time charge or started a subscription.

One of the following:
"payment"
"subscription"
natural?: Natural { payment_request_id, transaction_id }

Natural-rail join keys, present when rail: natural.

payment_request_id?: string

The Natural payment request (prq_...).

transaction_id?: string

The settled transaction (txn_...).

price_id?: string

Subscription mode — the recurring price subscribed to.

quantity?: number

Subscription mode — units of the price subscribed to.

formatint64
rail?: "stripe" | "natural"

The rail this request settled on.

One of the following:
"stripe"
"natural"
stripe?: Stripe { customer_id, payment_intent_id, subscription_id }

Ids of the Stripe objects on your connected account — join keys into your own Stripe Dashboard/API. Manage a subscription’s post-checkout lifecycle with subscription_id.

customer_id?: string

The Customer the request is attached to (cus_...). Always set in subscription mode; set in payment mode only when the request was created with a customer_id.

payment_intent_id?: string

The PaymentIntent collected at checkout (pi_...).

subscription_id?: string

Subscription mode — the Subscription (sub_...).

trial_end?: string

Subscription mode — when the free trial ends and the first charge happens. On a trial request, payment.succeeded means the payment method was collected ($0 moved).

formatdate-time
updated_at?: string
formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "payment.succeeded" | "payment.canceled" | "payment.expired" | 43 more
One of the following:
"payment.succeeded"
"payment.canceled"
"payment.expired"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

LocationSharingStartedWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { began_at, chat_id, ends_at, 2 more }
began_at: string | null

When location sharing started. Always present: falls back to when the share was first observed if the device reported no start time.

formatdate-time
chat_id: string | null

The chat this share was first sent to. Location sharing is per-contact rather than per-chat, so the location may also be visible in other chats with the same handle; this identifies where the share originated and does not change if the contact later shares into another chat. Null when the originating chat could not be determined.

formatuuid
ends_at: string | null

When location sharing will expire. Null when sharing indefinitely.

formatdate-time
shared_by: string

Phone number of the person sharing their location

shared_with: string

Your phone number receiving the location

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "location.sharing.started" | "message.sent" | "message.received" | 43 more
One of the following:
"location.sharing.started"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.stopped"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

LocationSharingStoppedWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { began_at, chat_id, ended_at, 2 more }
began_at: string | null

When the sharing session started, matching began_at on its started event. Always present.

formatdate-time
chat_id: string | null

The chat the ended share was first sent to, matching the chat_id on its started event. Sharing always stops for the contact as a whole, never for a single chat, so this is the session’s origin rather than the chat it stopped in. Null when the originating chat could not be determined.

formatuuid
ended_at: string

When the sharing session was observed to stop.

formatdate-time
shared_by: string

Phone number of the person who stopped sharing

shared_with: string

Your phone number that was receiving the location

event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "location.sharing.stopped" | "message.sent" | "message.received" | 43 more
One of the following:
"location.sharing.stopped"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"payment.succeeded"
"payment.canceled"
"payment.expired"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PaymentAuthorizedWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, amount, checkout_url, 17 more }

The payment request, as returned by GET /v3/payment_requests/{paymentRequestId}.

id: string

The payment request id.

formatuuid
amount: number

What was charged at checkout, in the currency’s minor units. In subscription mode this is the first invoice’s total — all items after any discounts are applied.

formatint64
checkout_url: string

URL the recipient opens to pay (https://zero.linqapp.com/pay/{slug}?session=...).

created_at: string
formatdate-time
currency: string
object: string
status: "succeeded" | "failed" | "canceled" | "expired"
One of the following:
"succeeded"
"failed"
"canceled"
"expired"
description?: string
discount?: Discount { coupon, label, promotion_code }

Subscription mode — the discount Stripe applied, read back from the coupon. Absent when none was applied.

coupon?: string
label?: string

Name of the coupon/promo code displayed to customers.

promotion_code?: string
interval?: "day" | "week" | "month" | "year"

Subscription mode — how often the subscription renews.

One of the following:
"day"
"week"
"month"
"year"
interval_count?: number

Subscription mode — intervals per renewal.

formatint64
metadata?: Record<string, string>
mode?: "payment" | "subscription"

Whether the request collected a one-time charge or started a subscription.

One of the following:
"payment"
"subscription"
natural?: Natural { payment_request_id, transaction_id }

Natural-rail join keys, present when rail: natural.

payment_request_id?: string

The Natural payment request (prq_...).

transaction_id?: string

The settled transaction (txn_...).

price_id?: string

Subscription mode — the recurring price subscribed to.

quantity?: number

Subscription mode — units of the price subscribed to.

formatint64
rail?: "stripe" | "natural"

The rail this request settled on.

One of the following:
"stripe"
"natural"
stripe?: Stripe { customer_id, payment_intent_id, subscription_id }

Ids of the Stripe objects on your connected account — join keys into your own Stripe Dashboard/API. Manage a subscription’s post-checkout lifecycle with subscription_id.

customer_id?: string

The Customer the request is attached to (cus_...). Always set in subscription mode; set in payment mode only when the request was created with a customer_id.

payment_intent_id?: string

The PaymentIntent collected at checkout (pi_...).

subscription_id?: string

Subscription mode — the Subscription (sub_...).

trial_end?: string

Subscription mode — when the free trial ends and the first charge happens. On a trial request, payment.succeeded means the payment method was collected ($0 moved).

formatdate-time
updated_at?: string
formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "payment.succeeded" | "payment.canceled" | "payment.expired" | 43 more
One of the following:
"payment.succeeded"
"payment.canceled"
"payment.expired"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PaymentCanceledWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, amount, checkout_url, 17 more }

The payment request, as returned by GET /v3/payment_requests/{paymentRequestId}.

id: string

The payment request id.

formatuuid
amount: number

What was charged at checkout, in the currency’s minor units. In subscription mode this is the first invoice’s total — all items after any discounts are applied.

formatint64
checkout_url: string

URL the recipient opens to pay (https://zero.linqapp.com/pay/{slug}?session=...).

created_at: string
formatdate-time
currency: string
object: string
status: "succeeded" | "failed" | "canceled" | "expired"
One of the following:
"succeeded"
"failed"
"canceled"
"expired"
description?: string
discount?: Discount { coupon, label, promotion_code }

Subscription mode — the discount Stripe applied, read back from the coupon. Absent when none was applied.

coupon?: string
label?: string

Name of the coupon/promo code displayed to customers.

promotion_code?: string
interval?: "day" | "week" | "month" | "year"

Subscription mode — how often the subscription renews.

One of the following:
"day"
"week"
"month"
"year"
interval_count?: number

Subscription mode — intervals per renewal.

formatint64
metadata?: Record<string, string>
mode?: "payment" | "subscription"

Whether the request collected a one-time charge or started a subscription.

One of the following:
"payment"
"subscription"
natural?: Natural { payment_request_id, transaction_id }

Natural-rail join keys, present when rail: natural.

payment_request_id?: string

The Natural payment request (prq_...).

transaction_id?: string

The settled transaction (txn_...).

price_id?: string

Subscription mode — the recurring price subscribed to.

quantity?: number

Subscription mode — units of the price subscribed to.

formatint64
rail?: "stripe" | "natural"

The rail this request settled on.

One of the following:
"stripe"
"natural"
stripe?: Stripe { customer_id, payment_intent_id, subscription_id }

Ids of the Stripe objects on your connected account — join keys into your own Stripe Dashboard/API. Manage a subscription’s post-checkout lifecycle with subscription_id.

customer_id?: string

The Customer the request is attached to (cus_...). Always set in subscription mode; set in payment mode only when the request was created with a customer_id.

payment_intent_id?: string

The PaymentIntent collected at checkout (pi_...).

subscription_id?: string

Subscription mode — the Subscription (sub_...).

trial_end?: string

Subscription mode — when the free trial ends and the first charge happens. On a trial request, payment.succeeded means the payment method was collected ($0 moved).

formatdate-time
updated_at?: string
formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "payment.succeeded" | "payment.canceled" | "payment.expired" | 43 more
One of the following:
"payment.succeeded"
"payment.canceled"
"payment.expired"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PaymentDeclinedWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, amount, checkout_url, 17 more }

The payment request, as returned by GET /v3/payment_requests/{paymentRequestId}.

id: string

The payment request id.

formatuuid
amount: number

What was charged at checkout, in the currency’s minor units. In subscription mode this is the first invoice’s total — all items after any discounts are applied.

formatint64
checkout_url: string

URL the recipient opens to pay (https://zero.linqapp.com/pay/{slug}?session=...).

created_at: string
formatdate-time
currency: string
object: string
status: "succeeded" | "failed" | "canceled" | "expired"
One of the following:
"succeeded"
"failed"
"canceled"
"expired"
description?: string
discount?: Discount { coupon, label, promotion_code }

Subscription mode — the discount Stripe applied, read back from the coupon. Absent when none was applied.

coupon?: string
label?: string

Name of the coupon/promo code displayed to customers.

promotion_code?: string
interval?: "day" | "week" | "month" | "year"

Subscription mode — how often the subscription renews.

One of the following:
"day"
"week"
"month"
"year"
interval_count?: number

Subscription mode — intervals per renewal.

formatint64
metadata?: Record<string, string>
mode?: "payment" | "subscription"

Whether the request collected a one-time charge or started a subscription.

One of the following:
"payment"
"subscription"
natural?: Natural { payment_request_id, transaction_id }

Natural-rail join keys, present when rail: natural.

payment_request_id?: string

The Natural payment request (prq_...).

transaction_id?: string

The settled transaction (txn_...).

price_id?: string

Subscription mode — the recurring price subscribed to.

quantity?: number

Subscription mode — units of the price subscribed to.

formatint64
rail?: "stripe" | "natural"

The rail this request settled on.

One of the following:
"stripe"
"natural"
stripe?: Stripe { customer_id, payment_intent_id, subscription_id }

Ids of the Stripe objects on your connected account — join keys into your own Stripe Dashboard/API. Manage a subscription’s post-checkout lifecycle with subscription_id.

customer_id?: string

The Customer the request is attached to (cus_...). Always set in subscription mode; set in payment mode only when the request was created with a customer_id.

payment_intent_id?: string

The PaymentIntent collected at checkout (pi_...).

subscription_id?: string

Subscription mode — the Subscription (sub_...).

trial_end?: string

Subscription mode — when the free trial ends and the first charge happens. On a trial request, payment.succeeded means the payment method was collected ($0 moved).

formatdate-time
updated_at?: string
formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "payment.succeeded" | "payment.canceled" | "payment.expired" | 43 more
One of the following:
"payment.succeeded"
"payment.canceled"
"payment.expired"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PaymentExpiredWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, amount, checkout_url, 17 more }

The payment request, as returned by GET /v3/payment_requests/{paymentRequestId}.

id: string

The payment request id.

formatuuid
amount: number

What was charged at checkout, in the currency’s minor units. In subscription mode this is the first invoice’s total — all items after any discounts are applied.

formatint64
checkout_url: string

URL the recipient opens to pay (https://zero.linqapp.com/pay/{slug}?session=...).

created_at: string
formatdate-time
currency: string
object: string
status: "succeeded" | "failed" | "canceled" | "expired"
One of the following:
"succeeded"
"failed"
"canceled"
"expired"
description?: string
discount?: Discount { coupon, label, promotion_code }

Subscription mode — the discount Stripe applied, read back from the coupon. Absent when none was applied.

coupon?: string
label?: string

Name of the coupon/promo code displayed to customers.

promotion_code?: string
interval?: "day" | "week" | "month" | "year"

Subscription mode — how often the subscription renews.

One of the following:
"day"
"week"
"month"
"year"
interval_count?: number

Subscription mode — intervals per renewal.

formatint64
metadata?: Record<string, string>
mode?: "payment" | "subscription"

Whether the request collected a one-time charge or started a subscription.

One of the following:
"payment"
"subscription"
natural?: Natural { payment_request_id, transaction_id }

Natural-rail join keys, present when rail: natural.

payment_request_id?: string

The Natural payment request (prq_...).

transaction_id?: string

The settled transaction (txn_...).

price_id?: string

Subscription mode — the recurring price subscribed to.

quantity?: number

Subscription mode — units of the price subscribed to.

formatint64
rail?: "stripe" | "natural"

The rail this request settled on.

One of the following:
"stripe"
"natural"
stripe?: Stripe { customer_id, payment_intent_id, subscription_id }

Ids of the Stripe objects on your connected account — join keys into your own Stripe Dashboard/API. Manage a subscription’s post-checkout lifecycle with subscription_id.

customer_id?: string

The Customer the request is attached to (cus_...). Always set in subscription mode; set in payment mode only when the request was created with a customer_id.

payment_intent_id?: string

The PaymentIntent collected at checkout (pi_...).

subscription_id?: string

Subscription mode — the Subscription (sub_...).

trial_end?: string

Subscription mode — when the free trial ends and the first charge happens. On a trial request, payment.succeeded means the payment method was collected ($0 moved).

formatdate-time
updated_at?: string
formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "payment.succeeded" | "payment.canceled" | "payment.expired" | 43 more
One of the following:
"payment.succeeded"
"payment.canceled"
"payment.expired"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.

PaymentSucceededWebhookEvent { api_version, created_at, data, 5 more }
api_version: string

API version for the webhook payload format

created_at: string

When the event was created

formatdate-time
data: Data { id, amount, checkout_url, 17 more }

The payment request, as returned by GET /v3/payment_requests/{paymentRequestId}.

id: string

The payment request id.

formatuuid
amount: number

What was charged at checkout, in the currency’s minor units. In subscription mode this is the first invoice’s total — all items after any discounts are applied.

formatint64
checkout_url: string

URL the recipient opens to pay (https://zero.linqapp.com/pay/{slug}?session=...).

created_at: string
formatdate-time
currency: string
object: string
status: "succeeded" | "failed" | "canceled" | "expired"
One of the following:
"succeeded"
"failed"
"canceled"
"expired"
description?: string
discount?: Discount { coupon, label, promotion_code }

Subscription mode — the discount Stripe applied, read back from the coupon. Absent when none was applied.

coupon?: string
label?: string

Name of the coupon/promo code displayed to customers.

promotion_code?: string
interval?: "day" | "week" | "month" | "year"

Subscription mode — how often the subscription renews.

One of the following:
"day"
"week"
"month"
"year"
interval_count?: number

Subscription mode — intervals per renewal.

formatint64
metadata?: Record<string, string>
mode?: "payment" | "subscription"

Whether the request collected a one-time charge or started a subscription.

One of the following:
"payment"
"subscription"
natural?: Natural { payment_request_id, transaction_id }

Natural-rail join keys, present when rail: natural.

payment_request_id?: string

The Natural payment request (prq_...).

transaction_id?: string

The settled transaction (txn_...).

price_id?: string

Subscription mode — the recurring price subscribed to.

quantity?: number

Subscription mode — units of the price subscribed to.

formatint64
rail?: "stripe" | "natural"

The rail this request settled on.

One of the following:
"stripe"
"natural"
stripe?: Stripe { customer_id, payment_intent_id, subscription_id }

Ids of the Stripe objects on your connected account — join keys into your own Stripe Dashboard/API. Manage a subscription’s post-checkout lifecycle with subscription_id.

customer_id?: string

The Customer the request is attached to (cus_...). Always set in subscription mode; set in payment mode only when the request was created with a customer_id.

payment_intent_id?: string

The PaymentIntent collected at checkout (pi_...).

subscription_id?: string

Subscription mode — the Subscription (sub_...).

trial_end?: string

Subscription mode — when the free trial ends and the first charge happens. On a trial request, payment.succeeded means the payment method was collected ($0 moved).

formatdate-time
updated_at?: string
formatdate-time
event_id: string

Unique identifier for this event (for deduplication)

formatuuid
event_type: "payment.succeeded" | "payment.canceled" | "payment.expired" | 43 more
One of the following:
"payment.succeeded"
"payment.canceled"
"payment.expired"
"message.sent"
"message.received"
"message.read"
"message.delivered"
"message.failed"
"message.edited"
"reaction.added"
"reaction.removed"
"poll.received"
"poll.failed"
"poll.sent"
"poll.delivered"
"poll.read"
"poll.updated"
"poll.vote.added"
"poll.vote.removed"
"poll.reaction.added"
"participant.added"
"participant.removed"
"chat.created"
"chat.group_name_updated"
"chat.group_icon_updated"
"chat.group_name_update_failed"
"chat.group_icon_update_failed"
"chat.background_updated"
"chat.background_update_failed"
"chat.typing_indicator.started"
"chat.typing_indicator.stopped"
"phone_number.status_updated"
"contact_card.received"
"call.initiated"
"call.ringing"
"call.answered"
"call.ended"
"call.failed"
"call.declined"
"call.no_answer"
"location.sharing.started"
"location.sharing.stopped"
"payment.declined"
"payment.authorized"
"connection.created"
"connection.revoked"
partner_id: string

Partner identifier. Present on all webhooks for cross-referencing.

trace_id: string

Trace ID for debugging and correlation across systems.

webhook_version: string

Date-based webhook payload version. Determined by the ?version= query parameter in your webhook subscription URL. If no version parameter is specified, defaults based on subscription creation date.