Sail

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.

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 data object 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: