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.
webhook.test
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. historyComplete is false while older conversations are still being imported; the moment it turns true is also announced by history.completed.
history.completed
conversationCount 0, null dates). The figures are those of the connection at that moment: oldestConversationAt and newestConversationAt are the first message of the oldest and of the newest conversation, recentConversationCount the same activity metric as GET /api/connections.
Anything that needs the whole history runs here: a rule on how far back it goes, a one-pass processing of the conversations, an “import finished” screen. The same figures are on GET /api/connections and GET /api/users (historyComplete, oldestConversationAt, newestConversationAt), so users connected before you handled this event need no replay. After a reconnect, the replacing connection goes through its own import and sends its own history.completed.
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; Verso closes the connection after that. Anything else, including a timeout, counts as a failure and the delivery is retried with exponential backoff, six attempts in total. Apps are delivered in parallel, so another partner’s slow endpoint never delays yours.
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.