Webhooks let your application (or a third-party service like Zapier) receive real-time notifications when something happens in a Synci account: new transactions arrive, a balance changes, an account is added or removed, or investment holdings update.Setting up an endpoint#
Create endpoints in the dashboard under Developers → Webhooks, or via the API (Create new webhook endpoint, scope webhooks:write). Each endpoint has:URL: the HTTPS address Synci sends POST requests to.
Events: the event types you want to receive (see below). You only receive events you subscribe to.
Account filter: optionally limit the endpoint to specific bank accounts. Leave empty to receive events for all accounts.
Omit sensitive identifiers: strips IBANs, BBANs, and similar identifiers from payloads. Required for Zapier endpoints.
Signing secret: a whsec_... secret generated when the endpoint is created, used to verify that deliveries really come from Synci. It is shown once on creation and on rotation.
Webhook delivery requires an active Synci subscription.Event types#
| Event | Fires when |
|---|
transactions.created | New transactions are fetched from the bank |
transactions.updated | An existing transaction changes (for example, a pending transaction books) |
balance.updated | An account balance changes |
holdings.updated | Investment or crypto holdings for an account change |
account.created | A financial account is added |
account.updated | A financial account's details or status change |
account.deleted | A financial account is removed |
Payload versions. Endpoints created today use payload version v2, which uses the event names above. Older endpoints on v1 receive the legacy names bank_account.created, bank_account.updated, and bank_account.deleted instead of account.*, and a bank_account key instead of financial_account in payloads. The version is pinned when the endpoint is created, so existing integrations keep working unchanged.
Every delivery is a POST with a JSON body in a common envelope:{
"event_type": "transactions.created",
"event_id": "evt_0197f2a4c3de7a1b9f04d2e6b8c11a2f",
"webhook_endpoint_id": 123,
"created_at": "2026-07-11T10:00:00+00:00",
"data": {
"...": "event-specific payload"
}
}
event_id uniquely identifies the event and is stable across retries: if a delivery fails and is retried, the retry carries the same event_id. Use it to deduplicate.
Requests are sent with the User-Agent Synci-Webhooks/1.0.
Verifying signatures#
Every delivery includes an X-Synci-Signature header: a hex-encoded HMAC-SHA256 of the raw request body, keyed with your endpoint's signing secret. Always verify it before trusting the payload.Compute the HMAC over the raw bytes of the body (before any JSON parsing), and compare using a constant-time comparison.Rotating the secret#
Rotate the signing secret from the endpoint's settings in the dashboard, or via the "Rotate webhook secret" endpoint. The new secret is returned once; update your verification code immediately, as the old secret stops being used.Retries and failure handling#
Your endpoint should respond with a 2xx status within a few seconds.
Failed deliveries (non-2xx or timeouts) are retried automatically with increasing delays: about 1 minute, then 10 minutes, then 1 hour.
If your endpoint responds 410 Gone, Synci stops sending to it and disables the endpoint immediately.
Endpoints that keep failing are marked unhealthy and eventually disabled automatically, and you are notified. You can re-enable an endpoint from the dashboard once it is fixed.
Testing#
Send a test event from the endpoint's page in the dashboard, or via the "Test webhook" endpoint. You can choose real data from your account or dummy data. Test deliveries:Are limited to 5 per minute.
Are sent once, without retries.
Are signed exactly like real deliveries, so you can verify your signature code end to end.
Best practices#
Acknowledge fast, process async. Return 2xx immediately and hand the payload to a queue or background worker. Slow responses count as failures and trigger retries.
Deduplicate on event_id. Retries mean you can receive the same event more than once; event_id is stable across retries, so skipping an event_id you have already processed makes repeat deliveries harmless.
Verify every signature. Anyone can POST to a public URL; the signature is your proof the event came from Synci.
Don't rely on ordering. Events are delivered independently and retries can arrive out of order. Use the created_at timestamp and fetch current state from the API when ordering matters.
Modified at 2026-07-11 12:18:00