Skip to main content
See Transaction webhooks for setup in the web app and through MCP.

Authentication and endpoints

Use your personal API key with the developer API. Developer API access is required; the key needs data.read for listing or data.write for changes. The API key that authenticates your request to Set Aside Money is separate from the destination token inside headers.

Create

Creation returns HTTP 201, the webhook’s ID and settings, and a secret beginning with whsec_. Store that signing secret securely; it cannot be retrieved later. Destination header values are omitted from the response.

List, update, test, or remove

For example, replace an existing webhook’s authentication header:
Omitting headers preserves the saved values. Sending "headers": {} clears them. Sending "enabled": false pauses the webhook and cancels its queued backlog; true resumes delivery for future transactions. You can have up to ten webhooks per user per Set Aside Money account, including paused webhooks. Tests are limited to one per minute per webhook. Changing a destination URL or signing secret requires creating a replacement webhook and removing the old one. Revoking the API key used to create a webhook does not remove that webhook.

Transaction events

A transaction.created event is sent for each newly observed pending or booked transaction, including supported bank imports, Wallet/FinanceKit imports, and manual transactions created through the API. Initial bank history is skipped. Updates, deletions, pending-to-posted replacements, and replacements of already observed Wallet transactions do not emit additional events. Delivery timing depends on provider availability and the next sync; a bank purchase is not necessarily available immediately. Example payload with synthetic identifiers:
amount is the stored transaction amount in integer cents. type describes its classification, and concept is the merchant description. set_aside_account_id identifies the personal or joint account; transaction.account_id identifies the bank or Wallet account within it. Payloads omit bank credentials, raw provider data, and private notes. Test events use name: "webhook.test" and a unique evt_test_… event ID.

Verify and acknowledge deliveries

Each request is a JSON POST with: Remove whsec_ from the signing secret and base64-decode the remainder to obtain the HMAC key. Compute HMAC-SHA256 over the exact UTF-8 bytes of:
Use the actual header values and the unmodified raw request body, separated by periods. Compare signatures using a constant-time comparison, reject timestamps outside a five-minute window, and parse JSON after verification. Deduplicate by webhook ID and event ID before applying effects. Return a 2xx response promptly to acknowledge receipt.

Retries and troubleshooting

Delivery is at least once, with no ordering guarantee. Timeouts, network failures, HTTP 408, 429, and 5xx retry up to eight total attempts. Backoff begins at 30 seconds and doubles, capped at one hour. Other non-2xx responses are terminal for that event. HTTP 410 also pauses the webhook. Redirects are not followed. Destinations must resolve to public addresses; private, loopback, and link-local addresses are refused.

HTTP 401 or 403

The destination rejected the request. A displayed header name confirms that a header is configured, but does not prove that its value is valid for the destination. Check that the header value uses the destination’s required authentication scheme, token, and permissions. For bearer authentication, enter Bearer YOUR_DESTINATION_TOKEN as the value, without repeating Authorization:. Replace the headers if necessary, then send a new test and refresh status. A previous failed event is not automatically retried after a 401 or 403.

No deliveries

Confirm the webhook is active, the creator is still an account member, and the account has an active subscription. Check that the relevant bank account remains subscribed and visible. Initial history does not trigger events. Use Send test to test the destination separately from bank-sync timing.