Webhooks

User events

Two events that fire when a LinkedIn connection is formed, carrying the new contact's profile fields in a single delivery. Both are polled, not real-time.

Before you start

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.

Quick reference

EventAvailabilityDescription
connection.acceptednot_realtime (~2h)A pending invitation you sent was accepted by the recipient.
connection.newnot_realtime (~4h) · opt-inAny new relation appeared on the account, not just ones you invited.

The default event is connection.accepted; connection.new is 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": "user",
    "request_url": "https://hooks.example.com/curviate",
    "account_ids": ["acc_YOUR_ACCOUNT_ID"],
    "events": ["connection.accepted"]
  }'

connection.accepted

Fired when an invitation sent from a connected account is accepted. The two members are now connected, so messaging needs no InMail credits.

Delivery timing, about 2 hours

connection.accepted is not real-time. LinkedIn relationship state is polled, so delivery can lag the acceptance by about 2 hours. Do not build logic that expects sub-minute delivery.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "connection.accepted",
  "data": {
    "account_id": "acc_YOUR_ACCOUNT_ID",
    "event":       "connection.accepted",
    "user": {
      "full_name":         "Alex Jordan",
      "provider_id":       "urn:li:member:123456789",
      "public_identifier": "alexjordan",
      "profile_url":       "https://www.linkedin.com/in/alexjordan",
      "picture_url":       "https://media.licdn.com/dms/image/example/photo.jpg"
    },
    "occurred_at": "2026-05-28T16:45:00.000Z"
  },
  "delivered_at": "2026-05-29T00:45:01.012Z"
}

Notable fields

  • data.user.provider_id: the LinkedIn member URN.
  • data.user.public_identifier: the vanity slug (alexjordan from linkedin.com/in/alexjordan).
  • data.user.picture_url: may be absent when the member has no public photo.
  • data.occurred_at: when the connection formed. delivered_at can be much later.

connection.new

Fired for any new connection on the account, not just ones you invited, so it is a superset of connection.accepted. Opt-in: name it in events[].

Not real-time, plus a backfill burst

connection.new is not_realtime: delivery can lag by up to ~4 hours. A freshly connected account may also receive a backfill burst of connection.new events for its existing network during initial sync. Build for volume.

Same data shape as connection.accepted. data.account_id is always present, so scope your handler on it.

{
  "id":          "wdl_YOUR_DELIVERY_ID",
  "webhook_id":  "wh_YOUR_WEBHOOK_ID",
  "event":       "connection.new",
  "data": {
    "account_id": "acc_YOUR_ACCOUNT_ID",
    "event":       "connection.new",
    "user": {
      "full_name":         "Sam Rivera",
      "provider_id":       "urn:li:member:246813579",
      "public_identifier": "samrivera",
      "profile_url":       "https://www.linkedin.com/in/samrivera",
      "picture_url":       "https://media.licdn.com/dms/image/example/photo.jpg"
    },
    "occurred_at": "2026-05-28T18:00:00.000Z"
  },
  "delivered_at": "2026-05-28T22:00:01.500Z"
}

Tracking new followers

follower.new is not a webhook event

There is no follower.new event; naming it in events returns 400 INVALID_REQUEST. To track followers, poll: read the account's followers or use the followers_of search filter.

Field remapping

Has no effect on delivery

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 the user source the array is validated: a key outside these five fails at create with 400 INVALID_REQUEST naming the accepted values. account_id, account_type, webhook_name, and timestamp are not accepted here.

user_provider_id        user_full_name        user_public_identifier
user_profile_url        user_picture_url

Errors you may hit

CodeHTTPCauseFix
INVALID_REQUEST400An event that does not belong to the user source (follower.new is the common one), or a data key outside the five accepted values.Read the message, it names the accepted values.
ACCOUNT_NOT_FOUND404An acc_... in account_ids is not owned by this tenant.Read your ids from GET /v1/accounts.
UNAUTHORIZED401Missing or invalid API key.Send Authorization: Bearer cvt_live_<key>.
PAYMENT_REQUIRED402No active subscription.Add a seat in the dashboard.
RATE_LIMIT_TENANT429Tenant quota exceeded.Honour the Retry-After header.

Full envelope shapes are in the error reference.

Next steps