Webhooks

Account status events

All 15 events that track the full lifecycle of a connected LinkedIn account, from creation to removal, plus the initial history backfill.

Quick reference

Events marked no_longer_realtime are not delivered; not_realtime means a delay.

EventAvailabilityDescription
account.createdReal-timeFirst successful link.
account.connectedReal-timeHealthy, including a reactivated account.
account.syncedno_longer_realtimeA sync cycle completed.
account.reconnectedReal-timeRe-authorized in place.
account.reconnect_neededReal-timeNeeds re-authentication.
account.restrictednot_realtimeConnected, but some actions fail.
account.creation_failedno_longer_realtimeFirst link attempt failed.
account.disconnectedReal-timeDisconnected; cannot act.
account.pausedno_longer_realtimeSync paused, not an error.
account.errorReal-timeSync halted on an error.
account.connectingno_longer_realtimeA link is in progress.
account.permission_revokedno_longer_realtimeLinkedIn permissions withdrawn.
account.initial_sync.runningReal-time · opt-inHistory backfill started.
account.initial_sync.completedReal-time · opt-inBackfill done; history queryable.
account.initial_sync.failedReal-time · opt-inBackfill failed, with a reason.

Create the webhook:

curl -X POST https://api.curviate.com/v1/webhooks \
  -H "Authorization: Bearer cvt_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "account_status",
    "request_url": "https://hooks.example.com/curviate",
    "account_ids": ["acc_YOUR_ACCOUNT_ID"]
  }'

Omit account_ids to cover every current and future account in your workspace, including accounts connected later: the webhook then reads back "account_ids": null. Send "account_ids": null in PATCH /v1/webhooks/{id} to switch an existing one. Every account.* payload carries the account's external_id (or null), so you can route an event to your own user without a lookup.

Omitting events subscribes to the 12 lifecycle events by default. The three account.initial_sync.* events are opt-in: name them in events[].

Five events are no longer delivered

account.synced, account.creation_failed, account.connecting, account.paused, and account.permission_revoked are not delivered. Subscribing to them still succeeds; each section below names what to read instead.

Events

account.created

A new account link completed for the first time. Start post-onboarding logic here.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.created",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "active",
    "occurred_at": "2026-05-28T08:00:00.000Z"
  },
  "delivered_at": "2026-05-28T08:00:01.123Z"
}

account.connected

Fired when the account is healthy, including a connect that reactivates an account you had disconnected ("recovered": true). An unclean reactivation fires account.reconnect_needed, account.restricted, or account.disconnected instead, matching the connect response's status.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.connected",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "active",
    "occurred_at": "2026-05-28T08:05:00.000Z"
  },
  "delivered_at": "2026-05-28T08:05:01.456Z"
}

account.reconnected

Credentials were refreshed in place, same account ID. Reactivating a disconnected account does not fire it; listen for account.connected and the degraded states instead.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.reconnected",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "active",
    "occurred_at": "2026-05-28T10:00:00.000Z"
  },
  "delivered_at": "2026-05-28T10:00:01.234Z"
}

account.reconnect_needed

The session expired or credentials became invalid. The account stays in your tenant but is inactive. Next call: POST /v1/auth/intent with its account_id in the body re-authenticates it in place (or reconnect from the dashboard).

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.reconnect_needed",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "reconnect_needed",
    "occurred_at": "2026-05-28T11:30:00.000Z"
  },
  "delivered_at": "2026-05-28T11:30:01.567Z"
}

account.synced · no_longer_realtime

Not delivered, with no replacement signal. The shape it carried:

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.synced",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "active",
    "occurred_at": "2026-05-28T08:10:00.000Z"
  },
  "delivered_at": "2026-05-28T08:10:01.789Z"
}

account.error

Synchronization halted on an error. Action required: check the dashboard and consider a reconnect.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.error",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "reconnect_needed",
    "occurred_at": "2026-05-28T12:00:00.000Z"
  },
  "delivered_at": "2026-05-28T12:00:01.890Z"
}

account.paused · no_longer_realtime

Synchronization was paused deliberately. Not delivered; read GET /v1/accounts/{account_id} instead. The shape it carried:

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.paused",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "reconnect_needed",
    "occurred_at": "2026-05-28T13:00:00.000Z"
  },
  "delivered_at": "2026-05-28T13:00:01.012Z"
}

account.restricted · not_realtime

The account is connected, but some actions on it fail: LinkedIn restricted the account, or one LinkedIn product on it (such as Sales Navigator or Recruiter) needs re-authentication or is failing. Surface it to a human. If a single product is failing, a reconnect may restore it. A LinkedIn restriction is usually detected by a daily check (up to 24 hours late) or immediately by a connect; a failing product is reported immediately. GET /v1/accounts/{account_id} always has the current state.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.restricted",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "restricted",
    "occurred_at": "2026-05-28T13:30:00.000Z"
  },
  "delivered_at": "2026-05-28T13:30:01.204Z"
}

account.permission_revoked · no_longer_realtime

A LinkedIn-side permission the account relies on was withdrawn. Not delivered; read GET /v1/accounts/{account_id} instead. The shape it carried:

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.permission_revoked",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "restricted",
    "occurred_at": "2026-05-28T14:00:00.000Z"
  },
  "delivered_at": "2026-05-28T14:00:01.234Z"
}

account.creation_failed · no_longer_realtime

The first account-link attempt failed; no account was established. Not delivered; read GET /v1/accounts/{account_id} instead. The shape it carried:

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.creation_failed",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "disconnected",
    "occurred_at": "2026-05-28T07:55:00.000Z"
  },
  "delivered_at": "2026-05-28T07:55:01.345Z"
}

account.disconnected

The account cannot act until reconnected. Causes: a disconnect, removal (dashboard or DELETE /v1/accounts/{id}), or a connect that finds it already disconnected. A removed account then returns 404 and its webhooks stop; read GET /v1/accounts/{account_id} to tell removal from disconnection.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.disconnected",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "disconnected",
    "occurred_at": "2026-05-28T15:00:00.000Z"
  },
  "delivered_at": "2026-05-28T15:00:01.456Z"
}

account.connecting · no_longer_realtime

Not delivered. Read GET /v1/accounts/{account_id} for a link in progress; account.created or account.connected signals completion. The shape it carried:

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.connecting",
  "data": {
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "status":      "connecting",
    "occurred_at": "2026-05-28T07:59:30.000Z"
  },
  "delivered_at": "2026-05-28T07:59:31.678Z"
}

Initial sync

Three opt-in events bracket the history backfill after each connect: exactly one account.initial_sync.running, then exactly one of account.initial_sync.completed or account.initial_sync.failed.

account.initial_sync.running

The backfill started. Informational.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.initial_sync.running",
  "data": {
    "status":      "active",
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "event":       "account.initial_sync.running",
    "occurred_at": "2026-05-28T08:00:00.000Z"
  },
  "delivered_at": "2026-05-28T08:00:01.000Z"
}

account.initial_sync.completed

The actionable signal: the account's full LinkedIn history is now queryable.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.initial_sync.completed",
  "data": {
    "status":      "active",
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "event":       "account.initial_sync.completed",
    "occurred_at": "2026-05-28T08:12:00.000Z"
  },
  "delivered_at": "2026-05-28T08:12:01.000Z"
}

account.initial_sync.failed

The backfill failed. reason is exactly one of internal, proxy, or provider.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "account.initial_sync.failed",
  "data": {
    "status":      "active",
    "reason":      "provider",
    "account_id":  "acc_YOUR_ACCOUNT_ID",
    "event":       "account.initial_sync.failed",
    "occurred_at": "2026-05-28T08:12:00.000Z"
  },
  "delivered_at": "2026-05-28T08:12:01.000Z"
}

Delivery payload

Every data object carries account_id, event (same as the top-level event), occurred_at, and status; account.initial_sync.failed adds reason. There is no timestamp field. No LinkedIn content is included.

Lifecycle events (the 12):

{
  "status":      "active",
  "account_id":  "acc_YOUR_ACCOUNT_ID",
  "event":       "account.connected",
  "occurred_at": "2026-05-28T11:30:00.000Z"
}

account.initial_sync.* events (the 3):

{
  "status":      "active",
  "reason":      "provider",
  "account_id":  "acc_YOUR_ACCOUNT_ID",
  "event":       "account.initial_sync.failed",
  "occurred_at": "2026-05-28T08:12:00.000Z"
}

status uses the vocabulary of GET /v1/accounts/{account_id} and is fixed per event:

data.statusevent
activeaccount.created, account.connected, account.reconnected, account.synced, account.initial_sync.*
connectingaccount.connecting
reconnect_neededaccount.reconnect_needed, account.error, account.paused
restrictedaccount.restricted, account.permission_revoked
disconnectedaccount.disconnected, account.creation_failed

Branch on event for what happened, on data.status for the resulting state.

A data field-remapping array on an account_status create is accepted and silently ignored.