Sail

Webhook Events

Sail sends webhook events as data moves through your integration. Each event has an event field that identifies what happened, and a data field with the resource Ids you need to fetch the current state.

A webhook payload has the following shape:

{
  "id": "evt_7f3k9m",
  "event": "connection.synced",
  "created_at": "2026-08-02T14:31:07Z",
  "user_id": "usr_abc123",
  "data": {
    "connection_id": "conn_4d81",
    "job_id": "job_r8v2",
    "balances_updated": true,
    "activity_updated": false,
    "expenses_updated": true
  }
}

Expand a resource below to see the events it can send.

Connections #

Events that fire for all connection types (card, store, benefit_account).

EventFires whendata fields
connection.createdA user completes a connect account flowconnection_id, type
connection.status_changedAny status transition (for example, aggregating to active, or to error)connection_id, previous_status, status, error_code
connection.syncedA sync completes with no status changeconnection_id, job_id, balances_updated, activity_updated, expenses_updated
connection.synced.initialInitial basic account info syncs for a benefit account connectionconnection_id, account_id
connection.synced.profileAccount profile data syncs for a benefit account connectionconnection_id, account_id
connection.synced.activityAccount activity syncs for a benefit account connectionconnection_id, account_id
connection.deletedA connection is revokedconnection_id
Expenses #

Events that fire when expenses have been fully classified.

EventFires whendata fields
expenses.verifiedA user’s expenses have been fully 213d classifieduser_id
Transactions #

Events that fire when you push your own transaction data through an external connection.

EventFires whendata fields
transactions.ingestedA batch finishes enrichmentconnection_id, job_id, created, updated
Receipts #

Events that fire when receipts are uploaded through the API or a connect account widget.

EventFires whendata fields
receipt.uploadedA receipt upload landedexpense_id
receipt.processedItemization and classification finishedexpense_id, expense_ids
receipt.failedReceipt unreadable or rejectedexpense_id, reason
Reimbursements #

Events that fire when a reimbursement changes status.

EventFires whendata fields
reimbursement.status_changedAny reimbursement status transitionreimbursement_id, previous_status, status, failure_reason
Connect Account Widgets #
EventFires whendata fields
hosted_session.expiredA session ended without the user finishingsession_id, flow
Users #
EventFires whendata fields
user.deletedPurge completed after deleting a useruser_id (in the envelope)

Events by integration path

Not every integration needs every event. Here’s what to listen for based on your use case.

Integration pathEvents to listen for
Expensesconnection.created, connection.status_changed, connection.synced, expenses.verified
Classificationtransactions.ingested, expenses.verified
Account connectionconnection.created, connection.status_changed, connection.synced, connection.synced.initial, connection.synced.profile, connection.synced.activity