Delivery
Every event is aPOST 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
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
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.