Balance Extracts Webhooks
Please note that the balance extracts webhooks are released as a BETA version for evaluation purposes only and are not recommended for production use.
Webhooks are a mechanism that allows Fynapse to automatically send real-time data to your system when a specific event occurs. It works by making an HTTP request (usually a POST) to a predefined URL, enabling event-driven communication without polling.
This section provides detailed instruction on how to configure webhooks for balance extracts.
The examples below assume the service is exposed through Ingress. Replace https://<base_url> with the external base URL of event-publisher.
Required REST API permissions
All endpoints under /api/** require authentication. The following permissions are required to work with webhooks:
The minimum permission set for a user who only configures webhooks:
- webhook_subscription_read
- webhook_subscription_edit
If the same user should also investigate delivery failures and retries, they also need:
- delivery_audit_read
These permissions were included in the Event Publisher Client permission in the Fynapse REST API Client role available by default in Fynapse. Therefore, please ensure the user who will be working with webhooks has this role assigned or their assigned custom role includes these permissions.
Additionally, you also need a user with Extractions Editor role assigned, e.g. Editor, to create the extract on the Extracts screen.
Receiver Requirements
- The endpoint must accept
POSTrequests withContent-Type: application/json. - The endpoint should return
2xxonce the message has been safely accepted. - The receiver should treat the
Idempotency-Keyheader as the deduplication key. - The receiver should validate the
Webhook-Signaturebefore processing the payload.
How to Create a Secret?
The secret is used to sign every webhook request using HMAC-SHA256.
Important:
- The secret value is generated by the service
- The value is returned only in the response to
createorrotate, andGET /api/v1/event-deliveries/secrets/{id} - The
secretRefin the subscription points toname, not toid
Example:
Example response:
Store the value securely on the receiver side. This is the value required to validate webhook signatures.
If the secret must be rotated, use:
After rotation, the service may include two v1 values in the same signature header to support a safe transition to the new secret.
How to Create a Webhook Subscription?
A subscription binds together:
- the HTTP endpoint
secretRef- the list of event types that should be delivered
Example:
Please note:
- a new subscription is created as
active - explicitly setting
eventTypesis recommended endpointUrlshould be the fully resolved receiver URL that will accept the webhook requestsecretRefis optional — if omitted, the service automatically creates a new secret with the same name as the subscription and returns its value in thesecretValuefield of the response
Example:
The example below does not contain secretRef.
In this case, the response contains fields with the generated value in secretValue and this value must be saved on the receiver side:
How to Create an Extract with Webhook Target in the UI?
Once you create a secret and subscription, you need to go to the Fynapse UI and create an extract that publishes events for target WEBHOOK. This way an event is produced and delivered to the subscribers:
- Go to Operations > Extracts > Extracts in Fynapse.
- Follow the standard configuration steps for Balance Extracts available in Balances Tutorials.
- In Target name select Webhook.
- The only available Data source is Balances.
- Complete the remaining configuration and either activate the schedule or run the extract manually.
Outbound webhook format
The service sends a POST request with these headers:
Content-Type: application/jsonWebhook-Signature: t=<unix_timestamp>,v1=<hex_hmac>[,v1=<hex_hmac_for_previous_secret>]Idempotency-Key: <idempotency_key>
Example:
Example body:
where:
event_id: unique event identifierevent_type: event type, for examplebalance.extractedmetadata: additional technical metadataevent_timestamp: event timestampdata: business payload
Retry behavior
Delivery statuses:
PENDING— awaiting delivery or scheduled for retryDELIVERED— success (2xxreceived)DEAD_LETTER— non-retryable HTTP status or all retries exhaustedCANCELLED— the subscription was inactive when the delivery was picked up for processing; no HTTP attempt is made
Receiver response semantics:
2xx- success, no retry400,401,402,405,406,410,413- no retry, the delivery is moved toDEAD_LETTER408- retry, but skips micro-retry and goes directly to engine-level retry- all other HTTP errors and network / timeout failures - retry
Default retry policy:
- micro-retry within a single execution: up to
2attempts in total - micro-retry delay: with jitter, random between
200 msand2000 ms - queue-level retry: up to
20attempts in total - retry window: up to
72hfrom delivery creation time - delay between queued retries: starts from approximately
30 sand grows up to4 h(multiplier:3xper attempt) - queue-level retry uses exponential backoff with jitter
- for normal queued retry, jitter is symmetric, approximately
+/-20% - if the endpoint returns
Retry-After, the service uses that value as the base and adds positive jitter up to approximately+10%
If the endpoint returns the Retry-After header, the service takes it into account when scheduling the next attempt.
Practical recommendation on the receiver side:
- return
2xxonly after the message has been durably stored or safely buffered - deduplicate by
Idempotency-Key, because the same event may be delivered again during retries
Validating the Webhook-Signature
The signature is calculated as HMAC-SHA256 over:
Important rules:
- use the raw HTTP body exactly as received
- do not recompute the signature from a re-serialized JSON object
- compare signatures using a constant-time comparison
- validate the timestamp with a tolerance window, for example
5 minutes - if the header contains multiple
v1values, accept the request if any one of them matches - the receiver normally keeps one active secret; multiple
v1values in the header are there so the sender can rotate its secret safely - do not compare the signature against the raw body alone; the input must always be
timestamp + "." + rawBody
Java example:
Usage example: