Skip to content

Spend the link and send the money.

POST

Path parameters

  • tokenstringRequired

    Opaque signed bearer token from the link URL.

Body

This endpoint expects a JSON object.

Register a new destination
  • esignConsentbooleanRequired

    The payer's consent to receive the Reg E receipt electronically. Required; the receipt is emailed on submit.

  • senderEmailstring<email>Optional

    Where the receipt is sent when the link itself carries no payer email. Required in that case.

  • namestringRequired

    Recipient legal or commonly used name.

  • emailstring<email>Required

    Recipient email address in standard email format.

  • detailsobjectRequired

    The fields named by requirements on the read, keyed by id.

  • paymentReasonstringOptional

    From the corridor's own list, served on the resolve.

  • recipientTypestringOptional

    The account holder's kind, never the sending organization's.

    Allowed values:individualbusiness
Use a saved destination
  • esignConsentbooleanRequired

    The payer's consent to receive the Reg E receipt electronically. Required; the receipt is emailed on submit.

  • senderEmailstring<email>Optional

    Where the receipt is sent when the link itself carries no payer email. Required in that case.

  • destinationAccountIdstringRequired

    An id returned in savedDestinations by the resolve call.

  • paymentReasonstringOptional

    From the corridor's own list, served on the resolve.

  • recipientTypestringOptional

    The account holder's kind, never the sending organization's.

    Allowed values:individualbusiness

Behavior

Takes no credential, as above.

Submitting twice returns the original payout with status: "already_submitted", so a worker who double-taps on a slow connection gets the payout they already have, never a second one.

A validation failure leaves the link spendable, so a mistyped account number is correctable rather than fatal. A link that is already being spent or has failed is 400 PAYOUT_LINK_UNUSABLE: it is not spendable, so ask for a new one.

Responses

201The payout.

Body

  • payoutIdstringOptional
  • statusone ofRequired
    Show one of 2 shapes
    PayoutStatus

    A payout moves only along these transitions:

    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 on failureCode.
    • canceled: you ended it before it was sent. It is not a failure, so it carries no failureCode; fundsReturned is true because the money never left.

    completed can still become failed. A receiving bank can return a payment days after settlement, giving failureCode: returned_by_bank and fundsReturned: true. Do not write a ledger that treats completed as immutable, and keep processing webhooks for a payout after it completes.

    "already_submitted"
  • requiresFundingbooleanOptional

    Present and true when the payout this link created is waiting on you to fund it. The worker has done everything they can; nothing reaches their account until you read the funding instructions for that payout and send the money. Absent where accepting the payout was already paying it.

  • disclosureobjectOptional

    The US Regulation E remittance disclosure, as the hosted page renders it: prepayment on the resolve (estimated, omitted when the corridor cannot be priced), receipt on the submit. Render lines in order.

    Show 4 properties
    • kindstringRequired
      Allowed values:prepaymentreceipt
    • estimatedbooleanRequired
    • linesarray of objectRequired
      Show 3 properties
      • labelstringRequired
      • valuestringRequired
      • signstringOptional
        Allowed values:+-
    • otherFeesDisclaimerstringRequired

Errors

  • 400

    VALIDATION_ERROR: the details did not match what the corridor requires. The link is still spendable; correct and resubmit. PAYOUT_LINK_UNUSABLE: the link is being spent or has failed. It is not spendable; ask the sender for a new one.

  • 403

    USE_POST_PAYOUTS: this organization has an approval threshold or a velocity cap, and only POST /payouts enforces them. Nothing was sent; send the payout through POST /payouts, which may answer 202 pending_approval.

  • 404

    NOT_FOUND: the token is not valid (expired, spent, forged). DESTINATION_ACCOUNT_NOT_FOUND: the saved destination is outside this link's end-user scope, or does not exist.

  • 429

    Too 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 same Idempotency-Key.

  • 503

    PAYOUT_LINKS_UNAVAILABLE: hosted payout links are not configured on this environment. Nothing was sent; contact support.

Error body · Error
  • typestringRequired

    Stable machine-readable code.

  • detailstringRequired

    What went wrong, in a sentence. Always a string, so detail.toLowerCase() is safe.

    More

    This is the field to read on BAD_REQUEST and PROVIDER_REJECTED, where the type alone does not name the condition.

  • messagestringRequired

    The same text as detail, kept for integrations written before detail existed. Read detail.

  • resolutionstringOptional

    What 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_CANCELABLE and DESTINATION_ACCOUNT_NOT_FOUND), so treat it as optional and fall back to detail.

  • statusintegerRequired

    HTTP status, repeated in the body.

  • statusCodeintegerRequired

    The same value as status, kept for integrations written before status existed. Read status.

  • requestIdstringRequired

    Quote this to support and we can find the exact request. Also sent as the x-request-id response header, which is the only place it appears on a successful response. Success bodies do not carry it. Send your own x-request-id on the request and we use it, so your trace and ours share one identifier; otherwise we mint one.

  • errorsarray of stringOptional

    Present on VALIDATION_ERROR; names each field that failed.

  • originalIdempotencyKeystringOptional

    On DUPLICATE_REQUEST_DETECTED only. 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.

  • originalPayoutIdstringOptional

    On DUPLICATE_REQUEST_DETECTED only. The payout the first request created.

  • originalBatchIdstringOptional

    On a batch DUPLICATE_REQUEST_DETECTED. The run the first request created.

  • originalRequestIdstringOptional

    On a 409 PAYOUT_OUTCOME_UNKNOWN replay. The requestId of the call whose outcome is unknown; quote it to support.

  • existingRecipientIdstringOptional

    On BANK_ACCOUNT_ALREADY_LINKED. The recipient in your organization that already holds this account.

  • existingMethodIdstringOptional

    On 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.

Avvio Partner Payouts · Payout links · operation submitPayoutLink

Try it: Submit a payout link

POST https://api.avvio.xyz/business/api/v1/payout-links/{token}/submit

Was this page helpful?