Skip to main content
Fluent delivers webhooks at least once. Your receiver must be idempotent, tolerate out-of-order events, and acknowledge accepted requests promptly.

Verify the signature

Every attempt has a Fluent-Signature header:
The v1 value is:
Verify the original bytes before parsing JSON. Re-serializing a parsed object can change whitespace or property ordering and invalidate the signature. The timestamp is generated separately for each attempt, so a legitimate retry can still pass the replay window.

Node.js and TypeScript

This example uses only Node.js built-ins. Pass it the original body bytes, not a parsed object.

Language-independent procedure

  1. Split the header on commas.
  2. Extract t and every v1 value.
  3. Parse t as Unix seconds and reject timestamps more than five minutes from your current time.
  4. Construct the signed bytes as UTF-8 t, then ., then the unmodified request-body bytes.
  5. Calculate HMAC-SHA256 using the signing secret as UTF-8 bytes.
  6. Encode the result as lowercase hexadecimal.
  7. Compare it with each v1 value using a constant-time function. Accept if any match.
Allowing multiple v1 values makes the parser compatible with future signing transitions even though Fluent currently sends one.
Verify the signature before logging or parsing the payload. Store the signing secret in a secret manager, and never log the secret or full document payloads.

Idempotency

Automatic retries send the same Fluent-Event-Id and Fluent-Delivery-Id with a higher Fluent-Attempt. Store the event ID in a table with a unique constraint before performing side effects. If the event ID already exists, return 2xx without repeating the work. Do not treat a duplicate as an error; an error response causes more retries. Manual retry starts the completed delivery again. Business idempotency must therefore remain keyed by event ID, even after the automatic retry window ends.

Ordering

Delivery order is not guaranteed. Retries, multiple workers, and concurrent transactions can make a newer state arrive before an older one. Treat each payload as a snapshot. For each document or run, store the greatest applied sequence and ignore an event with a lower sequence. Sequence values are global and gappy, so do not use missing numbers to detect lost events. Before an irreversible action, fetch current state from GET /api/v1/documents/{id} or GET /api/v1/runs/{id}. This protects against both stale delivery and a state transition that occurred after the event was created.

Response and retry behavior

Fluent gives each request 10 seconds and accepts any 2xx response as success. Fluent stores at most the first 2,048 bytes of the response body for endpoint diagnostics. Return 2xx after durably recording the event, then do expensive work asynchronously. A slow synchronous receiver can complete its own operation but still exceed Fluent’s timeout and receive the same event again.
Use 429 only when retrying later can succeed. Authentication or validation failures should return their appropriate non-retryable 4xx response so Fluent does not repeat an invalid request.

Attempts and endpoint health

Each delivery can make up to six automatic attempts. Fluent records the current attempt, response status, response snippet, error, and duration in the endpoint’s delivery log. A terminal delivery failure increments the endpoint’s consecutive-failure count when the failure came from the customer endpoint. A successful delivery resets the count to zero. Fluent-side preparation errors are retried but do not count against endpoint health. After 20 consecutive terminal failures, Fluent:
  • Disables the endpoint.
  • Marks pending deliveries as failed.
  • Stops recording and delivering new events for that endpoint.
  • Records the disable reason.
  • Emails team administrators once.
A team administrator can fix the receiver and re-enable the endpoint, which resets the failure count. Events that occurred while it was disabled are not recreated.

Manual retries

Team administrators can open a completed delivery and select Retry. Both succeeded and failed deliveries can be retried while the endpoint is enabled. This resets delivery-attempt diagnostics and places the same event back in the delivery queue. Your event-ID idempotency remains authoritative: retrying a succeeded delivery should not repeat a business side effect unless you intentionally remove or override your deduplication record.

Secret rotation

Rotating a signing secret is an immediate cutover. The next delivery uses the new secret, and the previous secret stops verifying without an overlap window. Use this sequence:
  1. Prepare the receiver to read the new secret from your secret manager.
  2. Rotate the secret in Fluent and copy the newly displayed value.
  3. Save the value in your secret manager.
  4. Restart or refresh the receiver so it reads the new value.
  5. Send a test event.
Because Fluent does not reveal a future secret before rotation, a zero-failure cutover requires temporarily disabling the endpoint during steps 2–4. Events occurring while disabled are not backfilled, so choose a quiet maintenance window or tolerate and manually retry deliveries that fail during rotation.

Network requirements

Webhook destinations must:
  • Use HTTPS.
  • Have no username or password in the URL.
  • Resolve to public IPv4 or IPv6 addresses only.
  • Accept the request without an HTTP redirect.
  • Respond within 10 seconds.
Fluent validates DNS when the endpoint is created or changed and immediately before delivery. It pins delivery to the validated addresses while retaining the hostname for TLS certificate validation. If your firewall uses an allowlist, coordinate the required network configuration with Fluent support before enabling production delivery.

Receiver checklist

  • Preserve the raw body and verify Fluent-Signature first.
  • Keep clocks synchronized so the five-minute replay window is reliable.
  • Enforce a unique constraint on Fluent-Event-Id.
  • Persist first, acknowledge with 2xx, and process asynchronously.
  • Apply only snapshots newer than the resource’s stored sequence.
  • Ignore unknown fields and event types.
  • Reconcile current API state before irreversible work.
  • Alert on terminal failures and disabled endpoints.
  • Avoid logging signing secrets or sensitive fetched document_data.