Skip to main content

Delivery

Every event is a POST to your webhook URL with a JSON body and these headers:
deliveryId identifies the delivery, not the event: a retried delivery keeps the same id. Use it to ignore duplicates.

Events

Every payload includes userRef, the identifier you passed to signLink(), so you can route the event without a lookup.

connect.started

user_id is Verso’s internal id for the user. Nothing is connected yet; the user may still abandon the page.

connection.created

replaces lists previous connections of the same user and provider account, now revoked. Their conversations moved to connectionId. It is empty on a first connection.

data.imported

Not sent when a sync finds nothing new. inserted counts conversations seen for the first time, which during the initial backfill includes old history; use GET /api/connections for recent activity.

reauth.needed

Sign a new connect link for the user. Until they reconnect, the connection stays needs_reauth and its conversations remain readable.

data.deleted

connection.deleted

Verifying signatures

The signature is Stripe-style: t is the Unix timestamp of the delivery, v1 is HMAC-SHA256(secret, t + "." + body) in hex. Verify on the raw body.
verifyWebhook rejects signatures older than 5 minutes by default; pass a fourth argument to change the tolerance. Without the SDK:

Retries

Your endpoint must answer with a 2xx status within 10 seconds. Anything else, including a timeout, counts as a failure and the delivery is retried with exponential backoff, six attempts in total. After the sixth failure the delivery is dropped. Deliveries are independent: a retried event can arrive after a newer one, so do not rely on ordering. If your app has no webhook URL configured, events are discarded.