Event types
Each webhook you configure subscribes to exactly one event type.
Payload envelope
Every event body has the same two-key shape. The event type is inevent, and everything else is under data:
Events
Data Model Changes
Fires as records are written during a sync. Use it to mirror our data into your own store.type is one of:
- CREATE — data is synced for the first time, or a new record appears on the connected platform.
- UPDATE — a record actually changed. Not sent on every sync: if we sync daily and the platform reports no changes, you get nothing.
- DELETE — a record was removed on the connected platform. Only detected during a full sync.
The same record can arrive more than once in a sync. A record is re-sent as UPDATE whenever any of its fields changed since the last fetch — including fields you may not care about. On Stripe invoices the hosted payment link is regenerated on every fetch, so most invoices arrive as a CREATE and then again as an UPDATE with no other field changed.Deduplicate on
platform_id and compare the fields you actually use, rather than treating every UPDATE as a meaningful change.Sync Started
Sync Completed
Fires once an asynchronous sync finishes. Use it to check which data models succeeded and which failed.status is derived from the job counts: SUCCESS when every job succeeded, otherwise FAILED, RUNNING, or CREATED. A partial failure surfaces as FAILED with a non-zero failed_jobs_count — inspect the counts rather than treating the event as all-or-nothing.
id and sync_id carry the same value; prefer sync_id.
Connection Changed
Fires when a connection is established via an invite link, or when its status changes — including expiry and disconnection. Use it to prompt customers to reconnect.status is one of HEALTHY, EXPIRED, or DISCONNECTED. connection_id is the composite {company_id}_{integration_type}.
Disconnect Requested
Topics
Topics narrow aDATA_MODEL_CHANGES webhook to the changes you care about. A topic is a data model and an operation, both lowercase:
GET /v4/core/webhook-config/platform-topics. Invalid topics are rejected when you create or update the config.
A
DATA_MODEL_CHANGES webhook with an empty topic list matches every data model and operation. The dashboard requires you to pick at least one topic; the API does not. If you create configs through the API, set topics explicitly unless you truly want everything.Configuring a webhook
- Dashboard
- API
- Go to Webhooks and click Create Webhook.
- Name the webhook and choose the event type.
- For Data Model Changes, select the topics to subscribe to.
- Enter your endpoint URL. Optionally add custom headers, and a signing key so you can verify that deliveries came from us.
Delivery
All webhooks are sent asPOST requests with a JSON body. The body is exactly the event payload — there is no additional wrapper.
Headers
Any custom headers on the config are also sent. If the originating event carried
x-request-id, x-correlation-id, or x-trace-id, those are forwarded too. Our own X-RootFi-* headers take precedence over custom headers with the same name.
Retries
A delivery succeeds on any2xx. Anything else — including a timeout — is retried up to 5 attempts total, with exponential backoff of min(60 × 2^attempt, 900) seconds: roughly 1 min, 2 min, 4 min, 8 min, then capped at 15 min. Requests time out after 30 seconds.
After the final failed attempt the delivery is marked failed and not retried again. Deliveries are also dropped if the config is disabled or deleted before they are sent.
Delivery history, attempt counts, and response codes are available via GET /v4/core/webhook-logs and in the dashboard.
Verifying signatures
When a config has a signing key, we sign each delivery and send the result inX-RootFi-Signature:
t is the unix timestamp and v1 is a hex HMAC-SHA256. The signed message is the timestamp, a literal ., and the raw request body:
t and v1, recompute over the raw body, and compare in constant time. Sign the bytes you received — re-serializing parsed JSON changes key order and whitespace, and the signature will not match.
Handling events reliably
- Deduplicate on
X-RootFi-Webhook-Delivery-Id. Retries reuse the id, and at-least-once delivery means you may see the same event twice. - Don’t assume ordering. Deliveries are queued independently; a
SYNC_COMPLETEDcan arrive before the lastDATA_MODEL_CHANGESbatch of that sync. - Treat
recordsas one page of many. Reconcile againstsync_idrather than expecting a single complete payload. - Upsert rather than insert. Combined with partial data, the same
platform_idwill legitimately arrive more than once.