Event reference
Every event Curviate can deliver to your webhook endpoint, grouped by source.
Reading the catalogue needs only an API key. Subscribing needs a
connected LinkedIn account for messaging, user, and post webhooks, because
account_ids is required and non-empty there. Read your ids with GET /v1/accounts; if it is empty,
start with the
Quickstart.
Quick reference
not_realtime means a delay; no_longer_realtime means not delivered. Message
events are inbound only.
| Event | Source | Availability | Description |
|---|---|---|---|
message.received | messaging | Real-time | Inbound message or InMail arrived. |
message.read | messaging | Real-time | A message was read. |
message.reaction | messaging | Real-time | Reaction added. |
message.edited | messaging | Real-time | Message edited. |
message.deleted | messaging | Real-time | Message deleted. |
chat.updated | messaging | Real-time · opt-in | Thread archived, muted, or read-state changed. |
chat.deleted | messaging | Real-time · opt-in | Thread deleted. |
connection.accepted | user | not_realtime (~8h) | Your invitation was accepted. |
connection.new | user | not_realtime (~4h) · opt-in | Any new connection. |
account.created | account_status | Real-time | First successful link. |
account.connected | account_status | Real-time | Healthy, including a reactivated account. |
account.synced | account_status | no_longer_realtime | A sync cycle completed. |
account.reconnected | account_status | Real-time | Re-authorized in place. |
account.reconnect_needed | account_status | Real-time | Needs re-authentication. |
account.restricted | account_status | not_realtime | Connected, but some actions fail. |
account.creation_failed | account_status | no_longer_realtime | First link attempt failed. |
account.disconnected | account_status | Real-time | Disconnected; cannot act. |
account.error | account_status | Real-time | Sync error, not a credentials issue. |
account.paused | account_status | no_longer_realtime | Sync paused, not an error. |
account.connecting | account_status | no_longer_realtime | A link is in progress. |
account.permission_revoked | account_status | no_longer_realtime | LinkedIn permissions withdrawn. |
account.initial_sync.running | account_status | Real-time · opt-in | History backfill started. |
account.initial_sync.completed | account_status | Real-time · opt-in | Backfill done; history queryable. |
account.initial_sync.failed | account_status | Real-time · opt-in | Backfill failed, with a reason. |
post.published | post | Real-time | A scheduled post was published. |
post.publish_failed | post | Real-time | A scheduled post could not be published. |
Delivery availability
GET /v1/webhooks/events returns the live catalogue, with an availability field:
| Value | Meaning |
|---|---|
omitted, or realtime | Real time. The three |
not_realtime | Delivered, not sub-second. |
no_longer_realtime | Not delivered. For most, read
|
GET /v1/webhooks/events returns 29 events across 6
sources. This article documents the 26 across 4 sources
you can subscribe to. Create rejects the other sources with
400 INVALID_REQUEST; the create schema is the authority.
account.synced, account.creation_failed,
account.connecting, account.paused, and
account.permission_revoked
stay in the create enum: subscribing returns 201 and no delivery
ever arrives. If a subscription is silent, check its events here first.
Default and opt-in events
Omitting events on create subscribes to the source's default set:
| Source | Default events (when events is omitted) |
|---|---|
messaging | message.received |
user | connection.accepted |
account_status | the 12 lifecycle events (not the three account.initial_sync.*) |
post | post.published and post.publish_failed |
chat.updated, chat.deleted, connection.new, and the account.initial_sync.*
events are never auto-subscribed; name them in events.
Payload structure
Every delivery shares the same outer envelope:
{
"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, how can I help?",
"sender": {
"name": "Alex Jordan",
"profile_url": "https://www.linkedin.com/in/alexjordan",
"provider_id": "urn:li:member:123456789"
},
"attachments": [],
"occurred_at": "2026-05-28T09:15:00.000Z"
},
"delivered_at": "2026-05-28T09:15:01.234Z"
}id (wdl_...) changes on every retry. data always carries account_id, event,
and occurred_at: deduplicate on those three. Message and connection events carry
their content in data; account-status events carry state only; post events carry
ids, timestamps, and a failure code, never the post text or media.
When an account needs re-authentication (account.reconnect_needed), the next call
is POST /v1/auth/intent with its account_id.
Errors you may hit
| Code | HTTP | Cause | Fix |
|---|---|---|---|
INVALID_REQUEST | 400 | An event outside the chosen source, or an unknown source. | The message lists the accepted values. |
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>. |
RATE_LIMIT_TENANT | 429 | Tenant quota exceeded. | Honour the Retry-After header. |
Full envelope shapes are in the error reference.
Next steps
- Messaging events: full payloads for the 7 messaging events.
- User events: full payloads for the 2 connection events.
- Account status events: full payloads for all 15 account-status events.
- Post events: payloads for scheduled-post outcomes.
- Verifying signatures: validate every delivery before you process it.