Webhook Events
Event envelope
All webhook events use a JSON envelope:Managing accounts and event fan-in
If your integration uses managed accounts or individual accounts, a single managing account can receive events for every account it manages.- A webhook subscription owned by a managing account receives events for every account it manages, plus its own.
- A webhook subscription owned by a managed account receives only that account’s own events.
data.account_id from the payload. It is present on payment.completed, transfer.created, and transfer.failed, in addition to the account.verification.* events that already carry it.
Your handler should branch on type.
transfer.created
Emitted when a Brale transfer is created.
Example:
- Treat
data.idas the transfer ID. data.account_ididentifies the account the transfer belongs to. Managing accounts use this to attribute events to a specific managed account.- Additional fields may be present as the transfer schema evolves.
- Your integration should ignore unknown fields.
transfer.completed
Emitted when a Brale transfer reaches complete status.
Use this event to replace polling transfer status.
Example:
- Treat
data.idas the transfer ID. - Treat
data.statusas the completed transfer state. The event name istransfer.completed; the API status iscomplete. - For outbound domestic wires,
transfer.completedis not a guarantee that the payout cannot be returned later. If the bank returns the wire, the same transfer moves tofailedand Brale emitstransfer.failed. Keep acceptingtransfer.failedfor transfers you recorded as complete. - Additional fields may be present as the transfer schema evolves.
- Your integration should ignore unknown fields.
transfer.failed
Emitted when a Brale transfer reaches failed status.
Use this event to react to permanent transfer failures (e.g., ACH returns, outbound domestic wire returns, or other rail/provider failures). For ACH-specific return details, inspect data.failure.ach_return. For wire return details, inspect data.failure.wire_return.
Example:
- Treat
data.idas the transfer ID. data.account_ididentifies the account the transfer belongs to. Managing accounts use this to attribute events to a specific managed account.- Treat
data.statusas the failed transfer state. data.failureis usually populated fortransfer.failed, but may benullwhen Brale does not have structured failure details. Always null-check before reading nested fields.data.failure.retriableindicates whether the failure is retriable. Spelledretriable, notretryable.data.failure.ach_returnis only set for ACH return failures. See the transferfailurefield for the full schema.- Additional fields may be present as the transfer schema evolves.
- Your integration should ignore unknown fields.
Returned outbound wires
Brale uses the existingtransfer.failed event for returned outbound domestic wires. There is no separate returned or refunded event. The same transfer can produce transfer.completed and later transfer.failed if the wire is returned after completion.
Example (illustrative, not an observed production payload):
- Read
data.statusfor the payout state. Readdata.failure.typefor the failure classification whendata.failureis present. - When
data.failure.typeiswire_return, readdata.failure.wire_return, notdata.failure.ach_return. - Do not require an ACH return code or a specific bank reason code to recognize a wire return.
data.failure.wire_return.codeisWIRE_RETURNwhen the provider supplies no code, anddata.failure.wire_return.reasoncan benull. - Missing structured failure details do not mean the transfer succeeded. If
data.statusisfailed, update your records even whendata.failureisnull. transfer.failedconfirms that the payout failed or was returned. It does not confirm that stablecoins were refunded or that refunded funds are available.- Track payout state, return classification, and any refund credit separately.
- Do not assume event ordering. Retrieve the latest transfer when you need to reconcile its current state.
payment.completed
Emitted when a payment reaches complete status.
Example:
data.account_ididentifies the account the payment belongs to. Managing accounts use this to attribute events to a specific managed account.- Brale also emits
payment.completedfor inbound payments created by Simulate an Automation deposit on testnet. There is no simulation-specific webhook type, so the same handler covers simulated and real deposits.
transfer.canceled
Emitted when a Brale transfer is canceled.
Example:
- Treat
data.idas the transfer ID. - Treat
data.statusas the canceled transfer state. - Additional fields may be present as the transfer schema evolves.
- Your integration should ignore unknown fields.
account.verification.documents_required
Emitted when Brale’s KYB review determines one or more documents must be uploaded.
Use this event to prompt the account holder to submit the requested documents. For each document with status: "pending_upload", stage the file (use document_kind and person_label from the payload), then link staged submissions. You can also poll required documents instead of using this webhook.
Example:
- Treat
data.account_idas the account ID. - Each entry in
data.documentsdescribes a required document.statusispending_uploaduntil the document is submitted. - Include
person_labelwhen staging individual (KYC) documents. - Your integration should ignore unknown fields.
account.verification.completed
Emitted when account verification completes and the account is enabled.
Example:
- Treat
data.account_idas the account ID. - Treat
data.statusas the completed verification state (complete). - This event fires only when the account reaches
completestatus. - Your integration should ignore unknown fields.
account.verification.rejected
Emitted when account verification is declined. This is the terminal outcome for a review: the account will not become complete on its own, and no further verification events follow.
Example:
- Treat
data.account_idas the account ID. - Treat
data.statusas the declined verification state (rejected). - The account’s
statusfromGET /accounts/{account_id}becomesrejectedat the same time, so polling and webhooks agree. - Uploading more documents does not reopen a declined account. Contact Brale if you believe a decline is incorrect.
- Your integration should ignore unknown fields.
Subscriptions to the
account.verification.* wildcard receive this event automatically. If you subscribed to account.verification.completed by name, add account.verification.rejected to your subscription to be told about declines.Handling future event types
Brale may add new event types over time. Best practices:- Use
GET /accounts/{account_id}/webhooks/event_typesto discover supported events. - Branch on
event.type. - Acknowledge unknown event types safely.
- Ignore unknown fields in
data. - Do not assume event ordering.
- To automatically receive future events in a family (e.g., new
transfer.*oraccount.*events), subscribe with a namespace wildcard liketransfer.*oraccount.*when you create or update a subscription. Wildcards work for every event family.