Gareth Wilson Gareth Wilson

Guide to Fivetran Webhooks: Features and Best Practices

Published


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

FeatureDetails
ConfigurationREST API only: POST /v1/webhooks/account or POST /v1/webhooks/group/{groupId}
Subscription modelYou choose the events per webhook
URLMust be HTTPS. An HTTP URL is refused with 400 Bad Request
Creation checkOne test POST on create or activate. If it fails, the webhook is not created
Signature headerX-Fivetran-Signature-256, only when a secret is set
SchemeHMAC-SHA256, hex digest. Fivetran's examples compare it as uppercase
Signed contentThe raw request body, nothing else
Replay windowNone. No timestamp is sent or signed
Response window10 seconds, expecting 200 OK
RetriesUp to five retries over 24 hours
Auto-deactivationAfter more than three days of only non-2xx responses
Test eventsPOST /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:

AreaEventsFires when
Syncssync_start, sync_endA connection sync starts or ends. data.status on sync_end is SUCCESSFUL or FAILED
Transformationstransformation_start, transformation_succeeded, transformation_failedA transformation run starts, succeeds or fails
Connection testsconnection_successful, connection_failureA connection's setup test passes or fails
Connector lifecyclecreate_connector, pause_connector, resume_connector, edit_connector, delete_connectorA connector is created, paused, resumed, edited or deleted
Resyncsforce_update_connector, resync_connector, resync_tableA sync is forced, or a connector or table is resynced
DeprecatedstatusMarked "(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.

  1. Generate a random secret and store it server-side, for example as FIVETRAN_WEBHOOK_SECRET.
  2. 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.

  1. Fivetran sends one test POST to 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": false to skip the test, but it runs again when you activate it.
  2. 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-256 in either hex case, answers the creation test with a 200, 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 200 for any well-formed request it does not recognise.
  • Create with "active": false if 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 secret on 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 for SUCCESSFUL.
  • Subscribe to connection_failure and transformation_failed for 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.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.