Webhook destinations are generally available. This payload predates API v2, but webhook destinations are also manageable through API v2 destinations. Existing fields, their meanings, and the
X-Redbark-Signature / X-Redbark-Timestamp headers remain compatible. Additive fields may appear without changing api_version, so consumers should ignore unknown fields; a breaking change would use a new api_version. For change notifications (run finished, connection events) use v2 event destinations.transactions.synced events. Every request is signed with HMAC-SHA256. Brokerage accounts currently sync only to Google Sheets; the trades.synced schema below is a reference contract, not an available brokerage-to-webhook setup.
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 indata.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).Updated transaction fields
The payload schema supports entries indata.updated with the transaction fields above, plus:
Ordinary transaction syncs currently leave
data.updated empty. Do not rely on webhook destinations to deliver same-ID corrections or pending-to-posted updates. Pending and posted records can have different provider IDs, with no guaranteed linkage. Periodic API reconciliation is needed if your ledger must reflect later changes.
Trade fields
Trade events use the same envelope as transactions, but withtype: "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.
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. Usemetadata.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
- Extract the
X-Redbark-TimestampandX-Redbark-Signatureheaders - Concatenate the timestamp and raw request body with a period:
<timestamp>.<body> - Compute the HMAC-SHA256 digest of this string using your signing secret
- Compare your computed signature with the one in the header (strip the
sha256=prefix) - 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’sconsecutiveFailures 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.
Any chunk that is not confirmed, failed or timed out, marks the sync run as failed. The run’s added and updated counts include only rows from chunks your endpoint confirmed. Rows from unconfirmed chunks are sent again on the next run.
Deduplication
Entity IDs (transaction IDs and trade IDs) that have been successfully delivered are tracked per destination. Each successful delivery upserts the row, refreshingdeliveredAt — 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.
The delivery UUID identifies one chunk and stays the same across its immediate retries. A later sync or replay creates a new UUID, so event-level deduplication is not enough. Verify the signature, persist the received payload durably, return 2xx promptly, then process asynchronously with idempotent transaction upserts keyed by account and provider transaction ID.
Payload transaction IDs are raw provider IDs; API v2 uses provider-tagged IDs such as txn_fk_<provider-id> for Fiskil. Use account_public_id to correlate accounts and normalise the transaction-ID format before comparing the two surfaces.
These delivery rules differ from v2 thin events, which carry notifications rather than transaction rows and retry for up to three days. Neither surface is an indefinite delivery guarantee. Reconcile posted transactions over an overlapping date window, follow every page, and honour Retry-After on API 429 responses.
