Skip to main content
Webhooks are in beta. The payload format and signing scheme may change.
When a sync runs for a webhook destination, Redbark sends one or more HTTP POSTs to your endpoint with a JSON payload. Transaction syncs produce transactions.synced events; brokerage syncs produce trades.synced events. Every request is signed with HMAC-SHA256.

Event types

The envelope shape is identical across event types — only the type field and the contents of data.new / data.updated change. Consumers that only care about one event type should filter on event.type and ignore the rest.

Request headers

Each webhook request includes these headers:

Payload format

All field names use snake_case. Monetary amounts on transactions are integers in the smallest currency unit (e.g. cents). Envelope timestamps (created) are Unix epoch seconds. The local_date field on transactions is a YYYY-MM-DD string; transaction_date and post_date are full ISO 8601 timestamps passed through from the banking provider. Missing optional fields are null, never empty strings.

Top-level fields

Transaction fields

Each transaction in data.new contains:
Amounts are integers in the smallest currency unit. For example, $45.50 AUD is 4550. Zero-decimal currencies like JPY use the full amount (e.g. ¥500 is 500).
The subcategory, confidence_level, and custom_category fields are only emitted when the destination has enriched categories enabled. They are omitted from the object entirely otherwise, rather than sent as null. Strict parsers that reject unknown keys must allow for these three fields appearing or not appearing depending on the destination’s configuration.

Updated transaction fields

Each entry in data.updated contains all the same fields above, plus: data.updated is populated when a sync detects mutations to previously-delivered transactions (for example, the banking provider’s stored values for a posted transaction change between syncs). The full current attributes are sent on every entry; you can compare against your own stored copy to derive the diff while previous_attributes remains reserved.

Trade fields

Trade events use the same envelope as transactions, but with type: "trades.synced" and Trade objects in data.new. Quantities, prices, and fees are decimal strings (not integers) to preserve precision for fractional shares. data.updated is reserved for future use and currently always empty.
Each trade in data.new contains:
Trade amounts are decimal strings, not integers. This is different from the transaction payload (which uses smallest-unit integers) because brokerage quantities can be fractional — parsing as a Decimal/BigDecimal on your side is recommended.

Metadata fields

Chunking

When a sync produces more than 500 transactions, the payload is split into multiple requests. Each chunk is delivered sequentially as a separate POST. Use metadata.chunk and metadata.total_chunks to reassemble if needed.

Verifying signatures

Every webhook request is signed with HMAC-SHA256 using your signing secret. Verify the signature on every request to confirm it came from Redbark and has not been tampered with.

Verification steps

  1. Extract the X-Redbark-Timestamp and X-Redbark-Signature headers
  2. Concatenate the timestamp and raw request body with a period: <timestamp>.<body>
  3. Compute the HMAC-SHA256 digest of this string using your signing secret
  4. Compare your computed signature with the one in the header (strip the sha256= prefix)
  5. Check that the timestamp is within 5 minutes of the current time to prevent replay attacks

Example: Node.js

Example: Python

Expected response

Your endpoint must return a 2xx status code (e.g. 200 OK) to acknowledge receipt. The response body is ignored.

Retry schedule

Each delivery makes up to 3 attempts in total. Sleeps before each retry use exponential backoff with a base of 3: The per-attempt request timeout is 30 seconds.

Auto-disable on failed deliveries

A single failed delivery cycle (all 3 attempts for the same payload exhausted, or a non-retried 4xx) increments the destination’s consecutiveFailures counter. Once the counter reaches 1, the destination is automatically disabled (disabledAt is set) and no further deliveries are attempted until the destination is manually re-enabled from the dashboard. The successful delivery of any subsequent payload resets the counter to zero. Auto-disable is intentionally aggressive: webhook endpoints are integrator-controlled, and continuing to retry against a broken endpoint causes more harm than good. Timeouts are the exception. A timed-out chunk is recorded as a failed delivery, but it does not increment consecutiveFailures, does not disable the destination, and does not halt delivery of the remaining chunks in the sync.

Deduplication

Entity IDs (transaction IDs and trade IDs) that have been successfully delivered are tracked per destination. Each successful delivery upserts the row, refreshing deliveredAt — the 90-day retention window therefore runs from the last successful delivery of that entity, not the first. If an entity is still inside its window when the next sync runs, it will not appear in data.new. Transactions and trades are deduplicated independently — a trade ID that happens to match a transaction ID won’t collide. Your endpoint should still handle potential duplicates gracefully (e.g. using <event.type>:<object.id> as an idempotency key) in case of network-level retries.