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.
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
| Event | Availability | Description |
|---|---|---|
connection.accepted | not_realtime (~2h) | A pending invitation you sent was accepted by the recipient. |
connection.new | not_realtime (~4h) · opt-in | Any 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.
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 (alexjordanfromlinkedin.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_atcan 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[].
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
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
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_urlErrors you may hit
| Code | HTTP | Cause | Fix |
|---|---|---|---|
INVALID_REQUEST | 400 | An 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_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.
- Messaging events: message and chat payloads.
- Account status events: account lifecycle payloads.