Guide to Fivetran Webhooks: Features and Best Practices
Fivetran webhooks tell your systems what your data pipelines are doing: a connection starts or finishes a sync, a transformation succeeds or fails, a connection test fails, or someone pauses, edits or deletes a connector. If you run downstream jobs after a sync, such as dbt models, cache refreshes or alerts, webhooks let them start the moment the data lands instead of on a schedule.
This guide covers how Fivetran webhooks are created and signed, the events and payload, the test request that can stop a webhook from being created, the retry and deactivation rules, and the best practices for running a Fivetran receiver in production.
What are Fivetran webhooks?
A Fivetran webhook is a URL, a list of events and an optional secret, created through the REST API at account level or for a single group. There is no dashboard screen for webhooks. When a subscribed event happens, Fivetran sends a JSON POST to the URL.
Signing is opt-in. Fivetran's docs say "the webhook payload can be signed if you specified a secret during the webhook creation", using "the SHA-256 HMAC algorithm". A webhook created without a secret sends unsigned requests. The page itself does not name the header or the encoding. Both come from Fivetran's example receivers, which read the x-fivetran-signature-256 header and compare it to a hex digest of the raw body.
Fivetran webhook features
| Feature | Details |
|---|---|
| Configuration | REST API only: POST /v1/webhooks/account or POST /v1/webhooks/group/{groupId} |
| Subscription model | You choose the events per webhook |
| URL | Must be HTTPS. An HTTP URL is refused with 400 Bad Request |
| Creation check | One test POST on create or activate. If it fails, the webhook is not created |
| Signature header | X-Fivetran-Signature-256, only when a secret is set |
| Scheme | HMAC-SHA256, hex digest. Fivetran's examples compare it as uppercase |
| Signed content | The raw request body, nothing else |
| Replay window | None. No timestamp is sent or signed |
| Response window | 10 seconds, expecting 200 OK |
| Retries | Up to five retries over 24 hours |
| Auto-deactivation | After more than three days of only non-2xx responses |
| Test events | POST /v1/webhooks/{webhookId}/test with an event name |
Fivetran publishes no source IP list for webhooks, so set a secret and verify the signature.
Common events
Fivetran supports 16 events:
| Area | Events | Fires when |
|---|---|---|
| Syncs | sync_start, sync_end | A connection sync starts or ends. data.status on sync_end is SUCCESSFUL or FAILED |
| Transformations | transformation_start, transformation_succeeded, transformation_failed | A transformation run starts, succeeds or fails |
| Connection tests | connection_successful, connection_failure | A connection's setup test passes or fails |
| Connector lifecycle | create_connector, pause_connector, resume_connector, edit_connector, delete_connector | A connector is created, paused, resumed, edited or deleted |
| Resyncs | force_update_connector, resync_connector, resync_table | A sync is forced, or a connector or table is resynced |
| Deprecated | status | Marked "(deprecated)" in Fivetran's list, with no replacement named |
Fivetran publishes one example payload, for sync_end:
{
"event": "sync_end",
"created": "2021-08-18T11:38:34.386Z",
"connector_type": "asana",
"connector_id": "mystified_presiding",
"connector_name": "mystified_presiding",
"sync_id": "mystified_presiding",
"destination_group_id": "deck_enjoy",
"data": {
"status": "SUCCESSFUL"
}
}
The top-level fields are common to every event. event is the event type, created is when the event was generated, and the connector_*, sync_id and destination_group_id fields identify where it came from. Fivetran says the data object's fields "vary depending on the event type", and it documents no other shape, so read data defensively. There is no event ID.
Setting up Fivetran webhooks
Start your receiver first. Fivetran tests the URL as part of creating the webhook.
- Generate a random secret and store it server-side, for example as
FIVETRAN_WEBHOOK_SECRET. - Create the webhook with your Fivetran API key and secret as Basic auth credentials:
curl -X POST "https://api.fivetran.com/v1/webhooks/account" \
-u "$FIVETRAN_API_KEY:$FIVETRAN_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/fivetran",
"events": ["sync_start", "sync_end", "connection_failure"],
"active": true,
"secret": "'"$FIVETRAN_WEBHOOK_SECRET"'"
}'
For one group only, use POST /v1/webhooks/group/{groupId} with the same body.
- Fivetran sends one test
POSTto the URL. If the endpoint "is not reachable or returns an unsuccessful status code, Fivetran does not create the webhook and returns an HTTP 400 Bad Request error". You can create the webhook with"active": falseto skip the test, but it runs again when you activate it. - Send a test event whenever you need one:
curl -X POST "https://api.fivetran.com/v1/webhooks/$WEBHOOK_ID/test" \
-u "$FIVETRAN_API_KEY:$FIVETRAN_API_SECRET" \
-H "Content-Type: application/json" \
-d '{"event": "sync_end"}'
For local development, the Hookdeck CLI (hookdeck listen 3000 fivetran --path /webhooks/fivetran) gives you a public HTTPS URL that forwards to your local server, with no account required.
Securing Fivetran webhooks
Read X-Fivetran-Signature-256, compute an HMAC-SHA256 of the raw body with your secret as the key, encode it as hex, and compare in constant time. Fivetran's Flask and Express examples both upper-case the computed digest and the header before comparing, so the case of the hex on the wire is not documented. Normalise both sides to the same case.
const crypto = require('crypto');
const express = require('express');
function verifyFivetranSignature(rawBody, signatureHeader, secret) {
if (!signatureHeader || !secret) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex').toUpperCase();
const received = Buffer.from(signatureHeader.trim().toUpperCase(), 'utf8');
const computed = Buffer.from(expected, 'utf8');
return received.length === computed.length && crypto.timingSafeEqual(received, computed);
}
const app = express();
app.post('/webhooks/fivetran', express.raw({ type: '*/*' }), (req, res) => {
if (!verifyFivetranSignature(req.body, req.get('x-fivetran-signature-256'), process.env.FIVETRAN_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString('utf8'));
// Queue work keyed on event.event, then answer with 200 OK.
res.sendStatus(200);
});
The same check in Python:
import hashlib
import hmac
import os
def verify_fivetran_signature(raw_body: bytes, signature_header, secret: str) -> bool:
if not signature_header or not secret:
return False
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest().upper()
received = signature_header.strip().upper().encode("utf-8")
return hmac.compare_digest(received, expected.encode("utf-8"))
# In FastAPI: raw_body = await request.body()
# verify_fivetran_signature(raw_body, request.headers.get("x-fivetran-signature-256"),
# os.environ["FIVETRAN_WEBHOOK_SECRET"])
To check your verifier, use the secret fivetran-test-secret and this exact body:
{"event":"sync_end","created":"2021-08-18T11:38:34.386Z","connector_id":"mystified_presiding","data":{"status":"SUCCESSFUL"}}
The signature should be this, in either case:
e1d81e7016efd5c28fca5e778a9a2a3d8f7778f0291116f86513b6fd1004e418
Fivetran does not say whether the test request sent on creation is signed. If your receiver rejects unsigned requests, check its logs when you create a webhook.
Make Fivetran webhooks production-ready. Hookdeck Event Gateway verifies
X-Fivetran-Signature-256in either hex case, answers the creation test with a200, and durably queues every sync and transformation event before your service sees it.
Fivetran webhook limitations and pain points
The webhook won't create
The Problem: The create call returns 400 Bad Request and no webhook exists.
Why It Happens: Fivetran sends a test POST during creation and refuses to create the webhook unless it gets a successful response. An HTTP URL, a server that is not running yet, a tunnel that has closed, or a handler that rejects the test body all cause it.
Workarounds:
- Use an HTTPS URL and start the endpoint before calling the API.
- Make sure your handler returns
200for any well-formed request it does not recognise. - Create with
"active": falseif you need the webhook record first, then activate it once the endpoint is live.
How Hookdeck Can Help: A Hookdeck source answers Fivetran's test request with a 200 straight away, whether or not your service is running, so the webhook is created and you can inspect the test body in the request log.
A missing secret means no signature
The Problem: Requests arrive with no X-Fivetran-Signature-256 header, so there is nothing to verify.
Why It Happens: Signing only happens when a secret was set at creation. The secret cannot be read back from the API either.
Workarounds:
- Always pass
secreton creation. - If you lost it, set a new one with the modify-webhook endpoint (
PATCH /v1/webhooks/{webhookId}) and update your environment at the same time. - If you run a webhook without a secret, treat its requests as unauthenticated and re-check anything important against the Fivetran API.
How Hookdeck Can Help: Hookdeck verifies the signature when you give the source your secret. If you didn't set a secret in Fivetran, leave verification off on the source.
Five retries in 24 hours, then the event is gone
The Problem: A sync finished during an outage, the retries ran out, and the job that should have followed it never ran.
Why It Happens: Fivetran times out after 10 seconds and retries "for up to 24 hours in increasing time intervals": 6 minutes, 21 minutes, then roughly 1h18, 4h40 and 16h48 later in its worked example, five retries in all. After that it stops sending that event instance. Each instance is retried on its own, so the next sync's events keep arriving while an earlier one is still failing. If a webhook fails with only non-2xx responses for more than three days, Fivetran deactivates it.
Workarounds:
- Answer quickly and queue the downstream work.
- After an outage, check sync history through the Fivetran API rather than relying on webhooks you may have missed.
- If a webhook is deactivated, fix the endpoint and update it with
"active": true, which runs the creation test again.
How Hookdeck Can Help: Hookdeck acknowledges every delivery and stores it, then retries delivery to your service on your own schedule. Events that arrive during your downtime are held and can be replayed, so the 24-hour window stops being the limit.
sync_end doesn't mean success
The Problem: A dbt run or an alert fires after a failed sync.
Why It Happens: sync_end fires whether the sync succeeded or failed. The outcome is in data.status.
Workarounds:
- Branch on
data.status, and only trigger downstream jobs forSUCCESSFUL. - Subscribe to
connection_failureandtransformation_failedfor alerting.
How Hookdeck Can Help: Hookdeck filters can route sync_end events with data.status of FAILED to an alerting destination and successful ones to your job runner.
Best practices
Verify against the raw body
Mount express.raw() on the route, read await request.body() in FastAPI, or use Buffer.from(await request.arrayBuffer()) in a Next.js route handler. Parse only after verifying.
Subscribe to what you use
Fivetran recommends subscribing only to the events you need, which "minimizes the number of HTTPS requests to your server". You can change the event list later with the modify-webhook endpoint.
Build your own idempotency key
There is no event ID. Combine event, connector_id, sync_id and created into a key and store it before processing. See our guide to webhook idempotency.
Acknowledge fast and process asynchronously
Fivetran waits 10 seconds for a 200 OK. Verify, queue and answer, and run the downstream jobs from the queue. See why to process webhooks asynchronously.
Read data defensively
Only the sync_end shape is documented. Use optional access on data and log events whose shape you have not seen before, so you learn their fields from real traffic.
Conclusion
Fivetran webhooks are created only through the REST API, tested on creation, and signed only when you supply a secret. Set a secret, verify X-Fivetran-Signature-256 against the raw body without caring about hex case, branch on data.status for sync_end, and answer within 10 seconds. With five retries in 24 hours, plan for the event you might miss.
Hookdeck Event Gateway answers the creation test, verifies each signature and holds every event until your service has processed it, so a pipeline outage on your side does not drop the signal that a sync finished.
Get started with Hookdeck for free and handle Fivetran webhooks reliably in minutes.