Messaging events
Eight events that fire whenever a message or chat changes state on a connected LinkedIn account, with full message content delivered in every message payload.
Creating a messaging webhook needs an API key and at least one
connected LinkedIn account: account_ids is required and must
be non-empty. Read your ids with GET /v1/accounts, and if that returns
an empty list, connect an account first (see
Authentication and accounts).
The message and chat events are newly wired on the current
platform substrate. The delivery envelope is stable and safe to
build against. It is the top-level event plus the data object,
which always carries account_id and occurred_at.
Individual data field shapes may still be refined as live validation
completes, so pin your handler to the envelope and treat per-field additions as
backward-compatible.
Quick reference
| Event | Description |
|---|---|
message.received | A new inbound message arrived in the account's inbox. |
message.delivered | An outbound message was confirmed delivered to the recipient. |
message.read | A message in the conversation was read. |
message.reaction | A reaction was added to a message. |
message.edited | A message was edited; the payload carries the updated text. |
message.deleted | A message was deleted from the conversation. |
chat.updated | A chat's container state changed (e.g. archived, muted, read-state). Opt-in. |
chat.deleted | A chat thread was deleted. Opt-in. |
The six message.* events share the same data shape (with per-event additions noted
below); the two chat.* events carry a minimal chat-container shape (documented at the
end). All are Core-tier. Subscribe to any subset when creating your webhook, the
chat.* events are opt-in and must be named explicitly:
curl -X POST https://api.curviate.com/v1/webhooks \
-H "Authorization: Bearer cvt_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "messaging",
"request_url": "https://hooks.example.com/curviate",
"account_ids": ["acc_YOUR_ACCOUNT_ID"],
"events": [
"message.received",
"message.delivered",
"message.read",
"message.reaction",
"message.edited",
"message.deleted"
]
}'message.received
Fired when a new inbound message arrives in the connected account's inbox, classic LinkedIn messages or InMail.
message.received fires only for messages the account
received, not for messages it sent. A message the account sent
does not trigger this event. For outbound delivery confirmation use
message.delivered.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "message.received",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "message.received",
"chat_id": "chat_YOUR_CHAT_ID",
"message_id": "msg_YOUR_MESSAGE_ID",
"text": "Hello, I saw your profile and wanted to connect.",
"sender": {
"name": "Alex Jordan",
"profile_url": "https://www.linkedin.com/in/alexjordan",
"provider_id": "urn:li:member:123456789"
},
"attachments": [],
"occurred_at": "2026-05-28T14:22:05.000Z"
},
"delivered_at": "2026-05-28T14:22:06.789Z"
}Notable fields
text: the full message body.sender: the LinkedIn member who sent the message:name,profile_url, andprovider_id(the LinkedIn member URN).attachments: array of attachment objects; empty when there are none. Each item carriesid,type,url,mimetype, andsize.chat_id: identifies the conversation; consistent across all events in the same thread.
message.delivered
Fired when an outbound message sent from the connected account was confirmed delivered by the platform. Use this event to close the send loop in fire-and-forget agent patterns.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "message.delivered",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "message.delivered",
"chat_id": "chat_YOUR_CHAT_ID",
"message_id": "msg_YOUR_MESSAGE_ID",
"text": "Thanks for reaching out, happy to connect.",
"sender": {
"name": "Jordan Lee",
"profile_url": "https://www.linkedin.com/in/jordanlee",
"provider_id": "urn:li:member:987654321"
},
"attachments": [],
"occurred_at": "2026-05-28T14:23:10.000Z"
},
"delivered_at": "2026-05-28T14:23:11.456Z"
}message.read
Fired when a message in the conversation is read. The payload carries the standard
messaging data shape, plus a read_by field when the platform discloses the reader.
read_by is conditional: absent, not null, when the reader is not disclosed.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "message.read",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "message.read",
"chat_id": "chat_YOUR_CHAT_ID",
"message_id": "msg_YOUR_MESSAGE_ID",
"text": "Hello, I saw your profile and wanted to connect.",
"sender": {
"name": "Alex Jordan",
"profile_url": "https://www.linkedin.com/in/alexjordan",
"provider_id": "urn:li:member:123456789"
},
"read_by": "urn:li:member:987654321",
"attachments": [],
"occurred_at": "2026-05-28T14:25:00.000Z"
},
"delivered_at": "2026-05-28T14:25:01.012Z"
}message.reaction
Fired when a reaction is added to a message in the conversation.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "message.reaction",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "message.reaction",
"chat_id": "chat_YOUR_CHAT_ID",
"message_id": "msg_YOUR_MESSAGE_ID",
"text": "Thanks for reaching out, happy to connect.",
"reaction": "👍",
"sender": {
"name": "Alex Jordan",
"profile_url": "https://www.linkedin.com/in/alexjordan",
"provider_id": "urn:li:member:123456789"
},
"attachments": [],
"occurred_at": "2026-05-28T14:26:30.000Z"
},
"delivered_at": "2026-05-28T14:26:31.234Z"
}Notable fields
reaction: the reaction value added to the message (e.g. an emoji string).
message.edited
Fired when a message is edited after it was sent. The text field carries the
post-edit content.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "message.edited",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "message.edited",
"chat_id": "chat_YOUR_CHAT_ID",
"message_id": "msg_YOUR_MESSAGE_ID",
"text": "Thanks for reaching out, let's schedule a call.",
"sender": {
"name": "Jordan Lee",
"profile_url": "https://www.linkedin.com/in/jordanlee",
"provider_id": "urn:li:member:987654321"
},
"attachments": [],
"occurred_at": "2026-05-28T14:27:45.000Z"
},
"delivered_at": "2026-05-28T14:27:46.789Z"
}Notable fields
text: the post-edit content. The original message text is not included.
message.deleted
Fired when a message is deleted from the conversation. The text field may be absent
when the platform does not include the deleted content.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "message.deleted",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "message.deleted",
"chat_id": "chat_YOUR_CHAT_ID",
"message_id": "msg_YOUR_MESSAGE_ID",
"sender": {
"name": "Jordan Lee",
"profile_url": "https://www.linkedin.com/in/jordanlee",
"provider_id": "urn:li:member:987654321"
},
"attachments": [],
"occurred_at": "2026-05-28T14:28:00.000Z"
},
"delivered_at": "2026-05-28T14:28:01.345Z"
}Notable fields
text: may be absent on deletion; do not depend on it being present when handling this event.
chat.updated
Fired when a chat's container state changed, for example the thread was archived, muted, or its read-state changed. Useful for inbox automation that mirrors thread state. This event is about a chat thread, not a single message.
chat.updated is not in the messaging default set. Name it explicitly
in the events array when creating or updating the webhook to receive
it.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "chat.updated",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "chat.updated",
"chat_id": "chat_YOUR_CHAT_ID",
"occurred_at": "2026-05-28T14:30:00.000Z"
},
"delivered_at": "2026-05-28T14:30:01.000Z"
}Notable fields
account_idandchat_idare always present,chat_ididentifies the thread whose container state changed. The delivery carries these identifying fields; use them to look up the current thread state on your side.
chat.deleted
Fired when a chat thread was deleted, actionable for inbox agents that mirror thread state.
chat.deleted is not in the messaging default set. Name it explicitly
in the events array to receive it.
{
"id": "wdl_YOUR_DELIVERY_ID",
"webhook_id": "wh_YOUR_WEBHOOK_ID",
"event": "chat.deleted",
"data": {
"account_id": "acc_YOUR_ACCOUNT_ID",
"event": "chat.deleted",
"chat_id": "chat_YOUR_CHAT_ID",
"occurred_at": "2026-05-28T14:31:00.000Z"
},
"delivered_at": "2026-05-28T14:31:01.000Z"
}Notable fields
account_idandchat_idare always present,chat_ididentifies the deleted thread.
Field remapping
The data array is accepted, stored, and echoed back when you read the
webhook, but it does not currently change what is delivered. The
delivered data object is the fixed shape documented above for every
event. Set it if you want it recorded for later; do not build a handler that
depends on it, and do not spend time tuning it when a field is missing.
The data array is not validated against a key list on this source, so a typo is
accepted at create time and produces no error anywhere. (The user source does
validate, which is why the two behave differently.) The 27 keys intended for the
messaging source:
account_id account_type account_info
chat_id timestamp webhook_name
message_id message mentions
reaction reaction_sender read_by
sender is_sender attendees
attachments subject provider_chat_id
provider_message_id is_event chat_pinned
quoted is_forwarded chat_content_type
message_type is_group folderWhen data is omitted, and today whatever you set, the default payload shape
documented above is delivered.
is_sender appears in the list above but is never delivered: it is an internal
routing signal, so data.is_sender is always undefined. To tell sent from received,
branch on the event name: message.received for inbound, message.delivered for
messages the account sent.
Errors you may hit
| Code | HTTP | Cause | Fix |
|---|---|---|---|
INVALID_REQUEST | 400 | An event in events[] does not belong to the messaging source, request_url is not HTTPS, or a field is missing. | Read the message, it names the field. |
ACCOUNT_NOT_FOUND | 404 | An acc_... in account_ids is not owned by this workspace. | Read your ids from GET /v1/accounts. |
UNAUTHORIZED | 401 | Missing or invalid API key. | Send Authorization: Bearer cvt_live_<key>. |
PAYMENT_REQUIRED | 402 | No active subscription. | Add a seat in the dashboard. |
RATE_LIMIT_TENANT | 429 | Workspace quota exceeded. | Honour the Retry-After header. |
Full envelope shapes are in the error reference.
Next steps
- Verifying signatures: validate every delivery before you process it.
- Delivery and retries: the retry schedule and endpoint health.
- Event reference: the full catalogue across all sources.