Receiving New Data

Learn why Sail signals new data through webhooks instead of polling, and the two ways new data actually shows up.

Sail never expects you to poll for the current status. Every piece of new or changed data is signaled by a webhook instead. New data shows up either through a periodic automatic refresh or through a refresh you trigger yourself. This page covers both, plus what you need to handle on every delivery.

Every webhook delivery is a POST to your configured endpoint, carrying one JSON envelope. Setting up that endpoint is a manual process today, the same as getting an API key. You send Sail your endpoint URL, and Sail sends back your webhook secret.

{
  "id": "evt_7f3k9m",
  "event": "receipt.uploaded",
  "created_at": "2026-08-02T14:31:07Z",
  "user_id": "usr_abc123",
  "data": { "expense_id": "exp_9f2c" }
}

The data field carries ids only, never PII. Treat every event as "something changed, go look," not as a state transfer. Fetch the resource by id for its current state instead of trusting the payload as the full picture.

How new data shows up

Sail syncs a connection's data in one of two ways:

  • Periodic automatic refresh happens on a cadence configurable per customer.
  • On-demand refresh happens when you trigger a sync yourself through Force Refresh, which returns 202 and a job_id while the sync runs asynchronously.

Either way, you find out it's done through a connection.synced webhook, which names which kinds of data moved (balances_updated, activity_updated, expenses_updated) so you know what to go pull.

Events by resource

Full per-event payload shapes live in Webhook Events. The table below groups each event by the resource it applies to.

ResourceEvents
Connectionsconnection.created, connection.status_changed, connection.synced, connection.deleted
Accountsaccount.created
Expensesexpenses.213d_verified, transactions.ingested
Receiptsreceipt.uploaded, receipt.processed, receipt.failed
Connect Account Widgethosted_session.expired
Usersuser.deleted

What every delivery requires of you

Verify the Sail-Signature header (t=<unix_ts>,v1=<hex>, an HMAC-SHA256 of the timestamp and raw body using your webhook secret) before processing a delivery, and reject anything outside a 5-minute timestamp tolerance. Delivery is at-least-once with no ordering guarantee, so deduplicate on the event's id rather than assuming each event arrives once or in order. A failed delivery retries with exponential backoff for up to 24 hours, after which Sail stops retrying.

Next steps


Did this page help you?