Set Up Webhooks
Sail pushes events to your server as jobs are completed. Connection status changes, account updates, and other data changes are delivered as webhook events. This is the primary way to know when new data is available to fetch.
In this guide, you’ll register a webhook endpoint, verify event signatures with your webhook secret, and handle incoming events from Sail.
Step 1: Register endpoint
Contact Sail to register your endpoint and receive your webhook secret.
Step 2: Verify signature
Validate the Sail-Signature header on every incoming event.
Step 3: Handle events
Process event payloads and fetch updated resources by Id.
Requirements
Before you configure webhooks, make sure you have the following:
- An API key.
- A webhook endpoint on your server that can receive events from Sail.
Step 1: Register your endpoint
Contact the Sail team to register your webhook endpoint. Provide your endpoint URL and Sail will return a webhook secret for signature verification.
Your webhook endpoint must:
- Accept POST requests with a JSON payload.
- Return a 2xx status code after successfully receiving the event.
- Respond promptly to prevent unnecessary webhook retries.
Step 2: Verify the signature
Every webhook delivery includes a Sail-Signature header. Verify this header before processing the event to confirm it came from Sail.
The header contains a timestamp (t) and a signature (v1):
Sail-Signature: t=<UNIX_TIMESTAMP>,v1=<HEX_SIGNATURE>To verify, compute an HMAC-SHA256 of <timestamp>.<raw_request_body> using your webhook secret and compare it to the signature. Reject events with timestamps older than 5 minutes to prevent replay attacks.
Step 3: Handle events
Sail sends every webhook as a JSON envelope:
{
"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
}
}The event field identifies what happened, while data contains the ids and event-specific information you need to retrieve the current state.
For example, when you receive a connection.synced event, the data field tells you which kinds of data moved (balances_updated, activity_updated, expenses_updated):
{
"event": "connection.synced",
"data": {
"connection_id": "conn_4d81",
"job_id": "job_r8v2",
"balances_updated": true,
"activity_updated": false,
"expenses_updated": true
}
}Use the connection_id and the flags to fetch only the resources that changed, such as expenses through List Expenses. This ensures your application works with the latest resource state rather than relying on the event payload alone.
What to expect from webhooks
Keep the following behaviors in mind when receiving and processing webhook events:
- The
dataobject contains resource ids. Fetch the full resource by id to get current state. - You may receive the same event more than once. Deduplicate on the event
id. - Events may arrive out of order. Treat each event as a signal to fetch the latest state, not as a state transfer.
- If your endpoint doesn’t return a
2xxresponse, Sail retries with exponential backoff for 24 hours.
Next steps
Your setup is complete. Choose the guide that matches your integration path:
- To turn transactions into item-level purchase data, see Enrich transactions with item-level data.
- To classify transactions you already have, see Classify your own transactions.
- To connect a user’s HSA/FSA account, see Connect HSA/FSA accounts.