Messaging events
Seven events that fire whenever a message or chat changes state on a connected LinkedIn account, with full message content delivered in every message payload.
You need an API key and a connected LinkedIn account: account_ids
is required and non-empty. Read your ids with GET /v1/accounts; if
it is empty, start with the
Quickstart.
Build against the envelope: the top-level event plus data, which always carries
account_id and occurred_at. Treat new data fields as additive.
Quick reference
| Event | Description |
|---|---|
message.received | A new inbound message arrived in the account's inbox. |
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 five message.* events share one data shape; the two chat.* events carry a
minimal chat shape. All seven work on any paid seat. The chat.* events are
opt-in and must be named in events:
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.read",
"message.reaction",
"message.edited",
"message.deleted"
]
}'message.received
Fired when an inbound message arrives, classic message or InMail. Inbound only:
a message the account sent never fires it, and there is no outbound push event. On
the read path, is_delivered on the message resource is set on a successful send;
it may reflect send success rather than confirmed delivery.
{
"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
sender:name,profile_url, andprovider_id(the LinkedIn member URN).attachments: empty when there are none; each item carriesid,type,url,mimetype, andsize.chat_id: the conversation, the same across every event in the thread.motion_id,enrollment_id: on everymessage.*event, set when the chat belongs to a Motion's Enrollment on this account,nullotherwise. Usemotion_idson the webhook to receive only those.
message.read
Fired when a message is read. Adds read_by when the reader is disclosed; otherwise
the field is absent, not null.
{
"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. reaction carries the value, for
example an emoji string.
{
"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"
}message.edited
Fired when a message is edited. text is the post-edit content; the original is not
included.
{
"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"
}message.deleted
Fired when a message is deleted. text may be absent; do not depend on it.
{
"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"
}chat.updated
Fired when a thread's state changed, for example archived, muted, or read-state.
It is about a chat thread, not a message. Opt-in. account_id and chat_id
are always present; look up the current thread state on your side.
{
"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"
}chat.deleted
Fired when a chat thread was deleted. Opt-in. account_id and chat_id (the
deleted thread) are always present.
{
"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"
}Field remapping
The data array is accepted, stored, and echoed back on read, but it
does not change what is delivered. The payload is always the
fixed shape above. Do not build a handler that depends on it.
On this source the array is not validated, so a typo is accepted silently (the
user source does validate). The 27 keys intended for messaging:
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 folderis_sender is listed but never delivered: data.is_sender is always
undefined.
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 tenant. | 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 | Tenant 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.