Events
List events
Everything that has happened to your payouts, in order.
Path parameters
orgIdstringRequiredThe opaque organization id issued to you, normally CUID-shaped (for example
cmsx…). It is not anorg_-prefixed alias. Pass it unchanged in every organization-scoped path.
Query parameters
sincestringOptionalReturn events after this
sequence. Omit it on the first call (or send0) to start from the first row; afterwards feed back the page'snextSince.payoutIdstringOptionalEverything that ever happened to one payout.
typestringOptionalComma-separated event types to include. Each must be a known type (see
PayoutEventType); an unknown one is a400 VALIDATION_ERROR, never an empty page.limitintegerOptionalDefaults to100Maximum events to return, from 1 to 500. Defaults to 100.
Behavior
Reconcile from this feed. Each event is written once and never
changes, carries its own id, and sequence is a total order you cursor on, so "page forward from where I left off" is expressible, which
listing payouts cannot do: that is ordered by creation and can never
surface a change to one you have already read.
payout.returned is its own type rather than a flavour of
payout.failed, because a bank returning a settled payment days later is
the one event that reverses something you already booked.
At-least-once: dedupe on id. And the cursor is sequence, not a
timestamp: timestamps tie, and a tie is indistinguishable from a
boundary.
The row is the webhook body. data is the same object a delivery
carries, so the amounts, fee, rate, your reference and endUserId
are here, so you can book from the feed alone. id is the delivery's
svix-id, so a webhook is the trigger to read GET /events?since=
from the right place.
Every family is here, type-discriminated: payout.*,
payout_batch.* (with batchId set and payoutId null),
payout_approval.*, webhook_endpoint.* and checkout_payment.*
(whose data is the checkout payment: fee is a decimal string and
there is no payoutId). A reconciler that wants only payouts passes
type=; otherwise skip types it does not recognize.
An event becomes readable about two seconds after it happens. That
lag is what makes since safe against a write that commits out of
order.
Responses
200Events, oldest first.
Body
dataarray of objectRequiredShow 11 properties
idstringRequiredStable. Dedupe on this, because the feed is at-least-once. Equals the webhook
svix-id.sequencestringRequiredThe cursor, as a decimal string. A 64-bit sequence past 2^53 is not representable as a JSON number, and silently losing precision on a cursor is unrecoverable.
typestringRequiredEvery event the feed and the webhooks can carry. New types are added without a major version. Ignore ones you do not handle, and return 2xx for them on a webhook.
Allowed values:payout.pendingpayout.processingpayout.completedpayout.failedShow 19 more values
payout.returnedpayout.canceledpayout_batch.awaiting_confirmationpayout_batch.completedpayout_batch.canceledpayout_batch.failedpayout_approval.pendingpayout_approval.approvedpayout_approval.rejectedpayout_approval.expiredpayout_approval.executedpayout_approval.execution_failedwebhook_endpoint.disabledcheckout_payment.paidcheckout_payment.failedcheckout_payment.refundedcheckout_payment.reversedcheckout_payment.partially_refundeddeposit.completedpayoutIdstring | nullRequiredThe payout this event is about. Also set on
payout_approval.executed, naming the payout the approval became, so?payoutId=returns that event too. Null on the other batch, approval, endpoint and checkout events; a checkout payment is identified bydata.paymentId.batchIdstring | nullRequiredSet on
payout_batch.*rows and on a batch approval.statusstringRequiredThe resource's status after this transition, a payout status on
payout.*, a batch status onpayout_batch.*, an approval status onpayout_approval.*.failureCodestringOptionalNew codes may be added; treat an unknown one as
execution_failed.Allowed values:quote_expiredinsufficient_fundslimit_exceededaccount_invalidShow 6 more values
account_cannot_receivecompliance_rejectedauthorization_not_completedreturned_by_bankexecution_failedunknownfundsReturnedbooleanOptionalcreatedAtstring<date-time>RequiredapiVersionintegerRequiredThe
datashape version. Same value as theAvvio-Webhook-Versionheader on a delivery.dataone ofRequiredThe body, built once when the event was recorded and never rebuilt. Discriminate on the row's
type:payout.*carries aWebhookPayout,payout_batch.*aPayoutBatch,payout_approval.*aPayoutApprovalEvent,webhook_endpoint.*aWebhookEndpointDisabled,checkout_payment.*aWebhookCheckoutPayment, documented in the Checkout spec (partner-checkout.openapi.yaml), not in this file.Show one of 4 shapes
WebhookPayout
The payout inside a webhook body has a different shape from
Payout. Here the amounts are flat decimal strings beside separate currency fields; on the REST surface they areMoneyobjects. Code written against the wrong one throws on the first delivery, which is why they are two schemas rather than one.payoutIdstringOptionalstatusstringOptionalA payout moves only along these transitions:
More
pending -> processing | completed | failed | canceled processing -> completed | failed completed -> failed (returned by the recipient's bank)pending: accepted, reserved, not sent. The only cancelable state.processing: handed to the payment network. Committed.completed: the recipient was paid. Not absolutely final.failed: not paid, or paid and then returned. Branch onfailureCode.canceled: you ended it before it was sent. It is not a failure, so it carries nofailureCode;fundsReturnedistruebecause the money never left.
completedcan still becomefailed. A receiving bank can return a payment days after settlement, givingfailureCode: returned_by_bankandfundsReturned: true. Do not write a ledger that treatscompletedas immutable, and keep processing webhooks for a payout after it completes.Allowed values:pendingprocessingcompletedfailedShow 1 more value
canceledrequiresFundingbooleanOptionalPresent and true when this payout is waiting on you to fund it from your own wallet, right now.
pendingalone cannot tell you this. On a routing that settles from a held balance the same word means the network is working on it.More
This is the delivery that matters for a hosted payout link: the worker fills in their details, the payout is created, and you are the only party who can release it. Absent once the funds are on their way, and absent entirely on routings where it cannot happen.
stagestringOptionalfailureCodestringOptionalNew codes may be added; treat an unknown one as
execution_failed.Allowed values:quote_expiredinsufficient_fundslimit_exceededaccount_invalidShow 6 more values
account_cannot_receivecompliance_rejectedauthorization_not_completedreturned_by_bankexecution_failedunknownfundsReturnedbooleanOptionalreferencestring | nullOptionalYour own reference, echoed on every delivery. This is the field a ledger joins on.
sourceCurrencystring | nullOptionalsourceAmountstring | nullOptionaldestinationCurrencystring | nullOptionaldestinationAmountstring | nullOptionaldestinationAccountIdstring | nullOptionalratestring | nullOptionalSame value and precision as
Payout.rate(destinationAmount / sourceAmount, six decimal places), not the pre-fee market rate fromGET /rates.feeobject | nullOptionalThe rail's fee for this payout, enough to book the movement from the delivery alone. Null until the rail reports one.
currencyfalls back tosourceCurrencywhen the rail did not name one.Show 2 properties
currencystringRequiredamountstringRequiredDecimal string, as the rail reported it.
endUserobject | nullOptionalYour own customer this payout is sent for: the party sending through your platform, never the recipient. In payroll the employer is the end user and the worker is the recipient; omit the object when you are the sender yourself. Attribution and the per-end-user daily cap key on
id; it is not forwarded to the payment network and does not change the sender of record, which stays your organization. Echoed on the payout and in every webhook, so a support question is answerable without your own id map.Show 3 properties
idstringOptionalYour stable identifier for the sending customer (not the recipient).
namestringOptionalPerson's display or legal name.
emailstring<email>OptionalPerson's email address in standard email format.
endUserIdstring | nullOptionalYour id for the end user this payout was for, the same value as
endUser.id. Null when you sent none.createdAtstring<date-time>OptionalcompletedAtstring<date-time> | nullOptional
PayoutBatch
batchIdstringRequiredexternalReferenceIdstring | nullRequiredYour own run id, echoed back.
statusstringRequiredA batch moves
received->validating-> eithercreating(autoCommit, zero errors) orawaiting_confirmation(anything else) ->creating->completed.canceledis reachable until creation starts;failedmeans the run broke, not a line. The batch tracks creation; payouts settle on their own lifecycle afterwards.Allowed values:receivedvalidatingawaiting_confirmationcreatingShow 3 more values
completedcanceledfailedautoCommitbooleanRequiredcountsobjectRequiredLines by status. The values sum to the number of lines submitted.
Show 8 properties
receivedintegerRequiredinvalidintegerRequiredvalidatedintegerRequiredcreatingintegerRequiredcreatedintegerRequiredcreate_failedintegerRequiredcanceledintegerRequiredrequires_reviewintegerRequired
estimatedSourceTotalstring | nullRequiredAdvisory. What the valid lines will draw from your balance, set when validation finishes; null while validation is running and when any line locks the destination side (no rate is consulted at validation time). Compare against
GET /balancebefore confirming. Half a paid payroll is worse than none.errorobjectOptionalPresent only when
statusisfailed, with the run-level reason.createdAtstring<date-time>RequiredupdatedAtstring<date-time>RequiredcompletedAtstring<date-time> | nullRequired
PayoutApprovalEvent
The
dataof apayout_approval.*event.approvalobjectRequiredOne request for M-of-N human approval of an API payout or a batch run. Not a payout status: the payout does not exist until this executes.
Show 14 properties
idstringRequiredkindstringRequiredAllowed values:payoutpayout_batchstatusstringRequiredThe lifecycle of an approval, a pre-payout resource.
executingandexecution_unknownare visible on the resource but emit no event.Allowed values:pendingapprovedrejectedexpiredShow 4 more values
executingexecutedexecution_failedexecution_unknownrequiredApprovalsintegerRequiredM, snapshotted when the request was created.
approvalsintegerRequiredApprove votes so far.
payoutIdstringOptionalPresent once a
payoutapproval isexecuted, naming the payout it became.batchIdstringOptionalPresent on
payout_batchapprovals.amountstring | nullRequiredSource-currency amount; for a batch, the run's estimated total.
currencystring | nullRequireddestinationAccountIdstringOptionalerrorobject | nullOptionalPresent on
execution_failedandexecution_unknown: why the approved payout could not be sent, or why its outcome is unknown.Show 2 properties
codestringOptionalThe error
typethe send answered with, when there was one (PAYOUT_OUTCOME_UNKNOWNonexecution_unknown).messagestringRequired
createdAtstring<date-time>RequiredexpiresAtstring<date-time> | nullRequiredA
pendingrequest expires 24 hours after creation.updatedAtstring<date-time>Required
payoutIdstringOptionalOn
payout_approval.executed, the payout the approval became.
WebhookEndpointDisabled
The
dataofwebhook_endpoint.disabled, delivered to your other endpoints that subscribe to every type (an emptyeventslist).endpointIdstringRequiredurlstring<uri>RequiredreasonstringRequiredconsecutiveFailuresintegerRequiredlastSuccessAtstring<date-time> | nullRequireddisabledAtstring<date-time>Required
hasMorebooleanRequirednextSincestring | nullOptional
Errors
401The key was refused. Nothing ran.
UNAUTHORIZED: missing, invalid or revoked, or a key on a route that does not accept one.KEY_EXPIRED: the key passed the expiry it was issued with. Issue a new one; an expired key cannot be rotated.KEY_IP_NOT_ALLOWED: the key is pinned to source addresses and this request came from another.
403A valid key that may not make this call. Nothing ran.
FORBIDDEN: the key belongs to a different organization.ACCOUNT_BLOCKED: API access for your organization is suspended, and every key is refused until we lift it. Contact support.
429Too many requests. The default ceiling is 100 requests per minute per API credential on a 60-second window. High-volume payout and reconciliation routes declare a 600/minute override, and batch submission a 30/minute ceiling. A separate 2,000/minute per-source-IP abuse ceiling always applies.
Obey
Retry-After; it is in seconds and is authoritative. A 429 means the request was refused before the handler ran. Retry reads normally; retry an idempotent mutation with its sameIdempotency-Key.
Error body · Error
typestringRequiredStable machine-readable code.
detailstringRequiredWhat went wrong, in a sentence. Always a string, so
detail.toLowerCase()is safe.More
This is the field to read on
BAD_REQUESTandPROVIDER_REJECTED, where the type alone does not name the condition.messagestringRequiredThe same text as
detail, kept for integrations written beforedetailexisted. Readdetail.resolutionstringOptionalWhat to do about it, when there is a specific answer. It is not on every error (it is absent on
BAD_REQUEST,NOT_FOUND,PAYOUT_NOT_CANCELABLEandDESTINATION_ACCOUNT_NOT_FOUND), so treat it as optional and fall back todetail.statusintegerRequiredHTTP status, repeated in the body.
statusCodeintegerRequiredThe same value as
status, kept for integrations written beforestatusexisted. Readstatus.requestIdstringRequiredQuote this to support and we can find the exact request. Also sent as the
x-request-idresponse header, which is the only place it appears on a successful response. Success bodies do not carry it. Send your ownx-request-idon the request and we use it, so your trace and ours share one identifier; otherwise we mint one.errorsarray of stringOptionalPresent on VALIDATION_ERROR; names each field that failed.
originalIdempotencyKeystringOptionalOn
DUPLICATE_REQUEST_DETECTEDonly. Send the request again with this to receive the original payout instead of making a second one. Without it there is no way to recover except by risking a double payment.originalPayoutIdstringOptionalOn
DUPLICATE_REQUEST_DETECTEDonly. The payout the first request created.originalBatchIdstringOptionalOn a batch
DUPLICATE_REQUEST_DETECTED. The run the first request created.originalRequestIdstringOptionalOn a
409 PAYOUT_OUTCOME_UNKNOWNreplay. TherequestIdof the call whose outcome is unknown; quote it to support.existingRecipientIdstringOptionalOn
BANK_ACCOUNT_ALREADY_LINKED. The recipient in your organization that already holds this account.existingMethodIdstringOptionalOn
BANK_ACCOUNT_ALREADY_LINKED. The payment method on that recipient.
Branch on type, never on the status or the message. Every error type is listed with what to do about it.
Was this page helpful?