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.
Creating a user webhook needs an API key and at least one
connected LinkedIn account: account_ids is required and must
be non-empty. Read your ids with GET /v1/accounts, and if that returns
an empty list, connect an account first (see
Authentication and accounts).
connection.new is newly wired on the current platform substrate. The
delivery envelope is stable and safe to build against, the
top-level event plus the data object, which always carries
account_id and occurred_at. Individual data
field shapes may still be refined as live validation completes, so pin your handler
to the envelope and treat per-field additions as backward-compatible.
Quick reference
| Event | Availability | Description |
|---|---|---|
connection.accepted | not_realtime (~8h) | 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. |
Subscribe to receive connection events when creating your webhook. The default event
is connection.accepted; connection.new is opt-in and must be named explicitly:
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 a pending connection invitation sent from a connected account is accepted by the recipient. Once received, the two members are connected and messaging is available without InMail credits.
connection.accepted is not real-time. LinkedIn
relationship state is polled on a schedule, so delivery of this event may be
delayed by up to 8 hours after the recipient accepts. This is a
platform constraint, not a Curviate bug. Design your workflow to be tolerant of
this delay; do not build logic that expects sub-second or even sub-minute delivery
of connection events.
{
"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.full_name, the new connection's display name on LinkedIn.data.user.provider_id, the LinkedIn member URN (e.g.urn:li:member:123456789).data.user.public_identifier, the LinkedIn vanity URL slug (e.g.alexjordanfromlinkedin.com/in/alexjordan).data.user.profile_url, direct link to the connection's public profile.data.user.picture_url, profile photo URL; may be absent when the member has no public photo.data.occurred_at, when the connection was formed on LinkedIn. Due to the polling constraint,delivered_atmay be significantly later thanoccurred_at.
connection.new
Fired when any new relation appears on the connected account, any new LinkedIn
connection, not just ones you invited. It is a superset of connection.accepted.
connection.new is opt-in, name it in events[] to receive it.
connection.new carries availability: "not_realtime".
LinkedIn relationship state is polled, so delivery may lag the real event by
up to ~4 hours. Additionally, a freshly connected account may
receive a backfill burst of connection.new events for
its pre-existing network during the initial-sync window, build for volume
tolerance, not sub-minute delivery. This is a platform polling constraint, not a
Curviate bug.
connection.new delivers the same data shape as connection.accepted.
data.account_id is always present, sourced from the delivery, 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
Curviate does not offer a follower.new webhook event, new-follower activity is not deliverable as a webhook. To track followers,
poll instead: read the account's followers or use the
followers_of search filter (see the API reference). Naming
follower.new in a webhook's events array is rejected like
any unknown event with 400 INVALID_REQUEST.
Field remapping
The data array is accepted, stored, and echoed back when you read the
webhook, but it does not currently change what is delivered. The
delivered data object is the fixed shape documented above. Set it if
you want it recorded for later; do not build a handler that depends on it.
Unlike the messaging source, user validates the array against a closed enum, so a
key outside the five below fails at create time with 400 INVALID_REQUEST naming the
accepted values.
user_provider_id user_full_name user_public_identifier
user_profile_url user_picture_urlThose five are the whole set. account_id, account_type, webhook_name, and
timestamp are not accepted here, whatever a messaging-source example may
suggest.
When data is omitted the default payload shape documented above is delivered.
Errors 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 workspace. | 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 | Workspace 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.