Event reference
Every event Curviate can deliver to your webhook endpoint, grouped by source. All are Core-tier unless noted.
Quick reference
The Availability column marks how an event is delivered. Events with no marker
are delivered in real time. See Delivery availability
below for the full meaning of each marker.
The Availability markers below — and the inbound-only direction note
on the message.* events — reflect the current platform substrate and
are an early surface. The event names, sources, and payload envelope are stable and
safe to build against; the exact latency labels and directionality are refined as
live validation completes.
| Event | Source | Availability | Description |
|---|---|---|---|
message.received | messaging | Real-time | An inbound message arrived in any chat (classic message or InMail). |
message.delivered | messaging | Real-time | An outbound message was delivered. |
message.read | messaging | Real-time | A message in a chat was read by the recipient. |
message.reaction | messaging | Real-time | A reaction was added to a message. |
message.edited | messaging | Real-time | A message was edited after send. |
message.deleted | messaging | Real-time | A message was deleted. |
chat.updated | messaging | Real-time · opt-in | A chat's container state changed (e.g. archived, muted, read-state) — useful for inbox automation. |
chat.deleted | messaging | Real-time · opt-in | A chat thread was deleted. |
connection.accepted | user | not_realtime (~8h) | A pending invitation was accepted by the recipient (a new relation). |
connection.new | user | not_realtime (~4h) · opt-in | Any new relation appeared on the account — not just ones you invited. |
account.created | account_status | Real-time | A new account link completed successfully for the first time. |
account.connected | account_status | Real-time | The account health check reported a healthy, connected state. |
account.synced | account_status | no_longer_realtime | A synchronization cycle completed successfully. |
account.reconnected | account_status | Real-time | An existing account was re-authorized in place (credentials refreshed). |
account.reconnect_needed | account_status | Real-time | Credentials expired or the session cookie was invalidated — the account needs reconnection. |
account.creation_failed | account_status | no_longer_realtime | The initial account-link attempt failed before completion (terminal). |
account.disconnected | account_status | Real-time | The account was removed from the platform (terminal state). |
account.error | account_status | Real-time | Account synchronization encountered an error (needs investigation — not a credentials issue). |
account.paused | account_status | Real-time | Account synchronization was stopped or paused externally. Not an error — may resume automatically. |
account.connecting | account_status | no_longer_realtime | An account link is in progress (transient, high-frequency). |
account.permission_revoked | account_status | no_longer_realtime | A LinkedIn-side scope or permission the account relies on was withdrawn. |
account.initial_sync.running | account_status | Real-time · opt-in | The initial history backfill for a newly connected account has started (informational). |
account.initial_sync.completed | account_status | Real-time · opt-in | The initial history backfill completed — full LinkedIn history is now queryable. |
account.initial_sync.failed | account_status | Real-time · opt-in | The initial history backfill failed — the delivery carries a neutral reason. |
Delivery availability
GET /v1/webhooks/events returns an availability field on the events that are
not delivered in real time. Three values are possible:
| Value | Meaning |
|---|---|
omitted, or realtime | Delivered in real time (the default). Most events omit the field; the
three |
not_realtime | The event is delivered, but on a poll delay (hours, not
seconds). Do not build latency-sensitive logic on it. Applies to
|
no_longer_realtime | The event is no longer delivered on the current platform.
Use the read-path alternative named in the event's description — for most,
an on-demand account read via |
Default and opt-in events
When you create a webhook without an events array, it subscribes to that
source's default set. Any event listed for the source can also be subscribed
explicitly.
| Source | Default events (when events is omitted) |
|---|---|
messaging | message.received |
user | connection.accepted |
account_status | the 11 lifecycle events (not the three account.initial_sync.*) |
chat.updated, chat.deleted, connection.new,
and the three account.initial_sync.* events are never
auto-subscribed. To receive them, name them explicitly in the events
array when creating (or updating) the webhook.
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",
"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 is the delivery ID (wdl_…); webhook_id (wh_…) references the webhook.
data.account_id is present on every delivery.
Content events — the messaging message.* events and
the connection.* events — carry the event's content directly in
data (text, sender, and
attachments for messaging; the new contact's profile fields for
connections), so your handler receives everything it needs in one delivery.
Structural events — the account-status lifecycle events and
account.initial_sync.* — carry account state only, not LinkedIn
content. See the per-source articles for the full payload shape.
Messaging events
Messaging events notify your endpoint whenever a message or chat changes state on a
connected account. The message.* payloads include the full message content; the
chat.* events describe a chat thread rather than a single message.
message.received
Fired when an inbound message arrives in any chat — classic LinkedIn messages or
InMail. Fires for inbound messages only; a message the account sent does not fire it
(use message.delivered for outbound confirmation).
message.delivered
Fired when an outbound message was delivered. Use it to close the send loop in fire-and-forget agent patterns.
message.read
Fired when a message in a chat was read by the recipient. The payload may include reader identity where available.
message.reaction
Fired when a reaction is added to a message. The data object includes a reaction
field with the reaction value.
message.edited
Fired when a message is edited after send. The text field carries the post-edit
content.
message.deleted
Fired when a message is deleted. The text field may be absent.
chat.updated
Fired when a chat's container state changed — for example archived, muted, or
read-state. Useful for inbox automation. chat.updated is opt-in — name it in
events[] to receive it. See the Messaging events
article for the delivered data shape.
chat.deleted
Fired when a chat thread was deleted. chat.deleted is opt-in — name it in
events[] to receive it.
User events
connection.accepted
Fired when a pending invitation you sent was accepted by the recipient. Once received, the two members are connected and messaging is available without InMail credits.
connection.new
Fired when any new relation appears on the account — not just ones you invited, so it
is a superset of connection.accepted. connection.new is opt-in — name it in
events[] to receive it.
Both user events carry availability: "not_realtime": LinkedIn
relationship state is polled on a schedule, so delivery lags the real event.
connection.accepted may arrive up to ~8 hours after
the recipient accepts; connection.new up to ~4 hours
after the relation appears. A freshly connected account may also receive a
backfill burst of connection.new events for its
pre-existing network during the initial-sync window. This is a platform polling
constraint, not a Curviate bug — design for the delay and for volume tolerance.
Account status events
Account events track the lifecycle of connected LinkedIn accounts — from creation through session expiry and eventual removal.
account.error means synchronization halted due to an error (needs
investigation — not a credentials issue). account.paused means
synchronization was stopped or paused externally — not an error, and it may
resume automatically. Treat them differently in your handler.
account.synced, account.creation_failed,
account.connecting, and account.permission_revoked carry
availability: "no_longer_realtime" — they are not delivered
on the current platform. For account.creation_failed and
account.permission_revoked, detect the state via an on-demand account
read (GET /v1/accounts/{account_id}). For
account.synced and account.connecting there is no
equivalent event and no direct read-path signal.
account.created
Fired when a new account link completes successfully for the first time — the initial connect path.
account.connected
Fired when a health check confirms the account is in a healthy, connected state.
account.synced · no_longer_realtime
Historically fired when a synchronization cycle completed. No longer delivered on the current platform, and there is no equivalent event or read-path — do not build on it.
account.reconnected
Fired when an existing account was re-authorized in place — credentials refreshed without creating a new account ID.
account.reconnect_needed
Fired when credentials expire or the session cookie is invalidated. The account
remains in your workspace but is inactive until re-authentication. Trigger a reconnect
flow from the dashboard or call POST /v1/auth/intent with the account's account_id
in the body to re-authenticate it in place.
account.creation_failed · no_longer_realtime
The initial account-link attempt failed before completion (terminal — no account was
established). No longer delivered — detect via an on-demand account read
(GET /v1/accounts/{account_id}).
account.disconnected
Fired when the account was removed from the platform (terminal — deletion or disconnect). The account ID is no longer valid and any webhooks scoped to it stop delivering.
account.error
Fired when account synchronization encountered an error (needs investigation — not a credentials issue).
account.paused
Fired when account synchronization was stopped or paused externally — not an error, and it may resume automatically.
account.connecting · no_longer_realtime
Historically a transient, high-frequency event fired while an account link was in
progress. No longer delivered on the current platform. Detect link-in-progress via the
account read (GET /v1/accounts/{account_id}) instead.
account.permission_revoked · no_longer_realtime
A LinkedIn-side scope or permission the account relies on was withdrawn. No longer
delivered — detect via an on-demand account read (GET /v1/accounts/{account_id}).
account.initial_sync.running · account.initial_sync.completed · account.initial_sync.failed
The initial history backfill for a newly connected account is bracketed by these three
opt-in events: exactly one account.initial_sync.running (informational), followed
by exactly one of account.initial_sync.completed (the actionable signal — full
LinkedIn history is now queryable) or account.initial_sync.failed. The failed
delivery carries a neutral reason — one of internal, proxy, or provider. See
the Account status events article for the full
treatment.