Docs/Webhooks/Account

connect.session.completed

Fired once when a user finishes a Connect session — on any channel or PMS. Carries your state token, the session id and the account that was connected, so you can tie the connection to your own user. The same account (provider + externalAccountId) is on every later event's account block and on every reservation, conversation and review.

Payload

Every delivery uses the same outer envelope (event, eventId, apiVersion, timestamp, data). Dedupe on eventId — it stays stable across retries and replays, while the X-Repull-Delivery-Id header changes on every attempt.

{
  "event": "connect.session.completed",
  "eventId": "3f1c9a2e-8b7d-4c6a-9e0f-1a2b3c4d5e6f",
  "apiVersion": "2026-04",
  "timestamp": "2026-05-01T12:34:56.000Z",
  "data": {
    "sessionId": "cs_abc123",
    "state": "user_8421",
    "provider": "vrbo",
    "externalAccountId": "36",
    "connectionId": null,
    "purpose": "connect",
    "completedAt": "2026-09-29T12:00:00.000Z"
  }
}

Verifying signatures

Every delivery includes a timestamped X-Repull-Signature header of the form t=<unix_ts>,v1=<hex>, where v1 is HMAC-SHA256(signing_secret, `${t}.${raw_body}`). Verify it before processing — see Verify Signatures for full Node.js and Python examples.

Use the raw body

Sign the raw request body exactly as received, not a re-stringified JSON object. Re-serialisation can reorder keys or change whitespace and break the signature.

Example handler

if (event === 'connect.session.completed') {
  // state is what you passed to POST /v1/connect — e.g. your user id
  await accounts.link(data.state, `${data.provider}:${data.externalAccountId}`)
}

Tip: Acknowledge with a 2xx status within 10 seconds. Failed deliveries are retried up to 5 times with exponential backoff.Webhook reliability →

AI