Integration guides
Quickstart
Five calls take a test key to a payout in Mexico. Everything runs in the sandbox, where no real money moves. The Mexico recipe runs the same calls one step at a time, with every response, the corridor’s fields and a failure on purpose.
export AVVIO_API_KEY=avvio_test_…export AVVIO_ORG_ID=cmsx… # a cuid, not an org_ prefixexport AVVIO_BASE_URL=https://api.avvio.xyz/business/api/v1For the Node tabs, install the SDK and create the client once; every Node tab
below uses this avvio:
// npm i @avvio/paymentsimport { PayoutsClient } from '@avvio/payments';
// Reads AVVIO_API_KEY, AVVIO_ORG_ID and AVVIO_BASE_URL from the environment.const avvio = new PayoutsClient();Send the key as x-api-key on every request; there is nothing to sign.
Production uses the same base URL, and the avvio_test_ prefix keeps these
calls in your sandbox.
-
Fund your sandbox balance
Section titled “Fund your sandbox balance”POST /payments/organizations/{orgId}/sandbox/fund IDEMPOTENCY_KEY=$(uuidgen) # new key per call; reuse it only to retry this exact requestcurl -s -X POST "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/sandbox/fund" \-H "x-api-key: $AVVIO_API_KEY" \-H "Idempotency-Key: $IDEMPOTENCY_KEY" \-H "content-type: application/json" \-d '{"amount": "5000.00"}'POST /payments/organizations/{orgId}/sandbox/fund await avvio.fund('5000.00'); // test keys onlyPOST /payments/organizations/{orgId}/sandbox/fund import osimport uuidimport requestsidempotency_key = str(uuid.uuid4()) # new key per call; reuse it only to retry this exact requestres = requests.post(f"{os.environ['AVVIO_BASE_URL']}/payments/organizations/{os.environ['AVVIO_ORG_ID']}/sandbox/fund",headers={"x-api-key": os.environ["AVVIO_API_KEY"],"Idempotency-Key": idempotency_key,},json={"amount": "5000.00"},)print(res.status_code, res.json())POST /payments/organizations/{orgId}/sandbox/fund avvio-payments fund --amount 5000.00Payouts draw on your USD balance. A
200also proves the key works. -
Register the recipient
Section titled “Register the recipient”POST /recipients/{orgId} IDEMPOTENCY_KEY=$(uuidgen) # new key per call; reuse it only to retry this exact requestcurl -s -X POST "$AVVIO_BASE_URL/recipients/$AVVIO_ORG_ID" \-H "x-api-key: $AVVIO_API_KEY" \-H "Idempotency-Key: $IDEMPOTENCY_KEY" \-H "content-type: application/json" \-d '{"type": "individual","name": "María González","country": "MX","externalId": "cust42_maria","endUserId": "customer_42","method": {"kind": "fiat","currency": "MXN","recipientDetails": {"clabeNumber": "012180000080004471"}}}' | tee response.jsonexport DESTINATION_ACCOUNT_ID=$(jq -r .method.destinationAccountId response.json)POST /recipients/{orgId} const beneficiary = await avvio.createBeneficiary({name: 'María González',country: 'MX',currency: 'MXN',endUserId: 'customer_42', // your id for the person sendingexternalId: 'cust42_maria', // makes a repeat create safedetails: { clabeNumber: '012180000080004471' },});const destinationAccountId = beneficiary.method.destinationAccountId; // you pay thisPOST /recipients/{orgId} import osimport uuidimport requestsidempotency_key = str(uuid.uuid4()) # new key per call; reuse it only to retry this exact requestres = requests.post(f"{os.environ['AVVIO_BASE_URL']}/recipients/{os.environ['AVVIO_ORG_ID']}",headers={"x-api-key": os.environ["AVVIO_API_KEY"],"Idempotency-Key": idempotency_key,},json={"type": "individual","name": "María González","country": "MX","externalId": "cust42_maria","endUserId": "customer_42","method": {"kind": "fiat","currency": "MXN","recipientDetails": {"clabeNumber": "012180000080004471"}}},)print(res.status_code, res.json())os.environ["DESTINATION_ACCOUNT_ID"] = res.json()["method"]["destinationAccountId"] # a later step reads itPOST /recipients/{orgId} avvio-payments beneficiary create \--currency MXN --country MX \--end-user customer_42 --external-id cust42_maria \--field clabeNumber=012180000080004471 | tee response.jsonexport DESTINATION_ACCOUNT_ID=$(jq -r .method.destinationAccountId response.json)Mexico needs one field,
clabeNumber. Each tab savesmethod.destinationAccountIdasDESTINATION_ACCOUNT_ID: you pay that id, not the recipientid. Other countries need other fields (Register recipients). -
Price it, then send it
Section titled “Price it, then send it”GET /payments/organizations/{orgId}/rates curl -s "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/rates?from=USD&to=MXN&amount=200.00" \-H "x-api-key: $AVVIO_API_KEY"GET /payments/organizations/{orgId}/rates const quote = await avvio.quote({amount: '200.00',to: 'MXN',});// → { sourceAmount, destinationAmount, fee, rate, limits, indicative: true }GET /payments/organizations/{orgId}/rates import osimport requestsres = requests.get(f"{os.environ['AVVIO_BASE_URL']}/payments/organizations/{os.environ['AVVIO_ORG_ID']}/rates",headers={"x-api-key": os.environ["AVVIO_API_KEY"],},params={"from": "USD","to": "MXN","amount": "200.00"},)print(res.status_code, res.json())GET /payments/organizations/{orgId}/rates avvio-payments quote --amount 200.00 --to MXNPOST /payments/organizations/{orgId}/payouts IDEMPOTENCY_KEY=$(uuidgen) # new key per call; reuse it only to retry this exact requestcurl -s -X POST "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/payouts" \-H "x-api-key: $AVVIO_API_KEY" \-H "Idempotency-Key: $IDEMPOTENCY_KEY" \-H "content-type: application/json" \-d '{"amount": "200.00","destinationAccountId": "'"$DESTINATION_ACCOUNT_ID"'","amountLeg": "source","expectDestination": "3384.65","maxDriftBps": 200,"reference": "ZZ-2026-0042","endUser": {"id": "customer_42"}}'POST /payments/organizations/{orgId}/payouts const idempotencyKey = crypto.randomUUID(); // persist it with the order so a retry reuses itconst payout = await avvio.payout({amount: '200.00',destinationAccountId,expectDestination: quote.destinationAmount.amount, // 3384.65endUser: { id: 'customer_42' },reference: 'ZZ-2026-0042',idempotencyKey,});POST /payments/organizations/{orgId}/payouts import osimport uuidimport requestsidempotency_key = str(uuid.uuid4()) # new key per call; reuse it only to retry this exact requestres = requests.post(f"{os.environ['AVVIO_BASE_URL']}/payments/organizations/{os.environ['AVVIO_ORG_ID']}/payouts",headers={"x-api-key": os.environ["AVVIO_API_KEY"],"Idempotency-Key": idempotency_key,},json={"amount": "200.00","destinationAccountId": os.environ["DESTINATION_ACCOUNT_ID"],"amountLeg": "source","expectDestination": "3384.65","maxDriftBps": 200,"reference": "ZZ-2026-0042","endUser": {"id": "customer_42"}},)print(res.status_code, res.json())POST /payments/organizations/{orgId}/payouts avvio-payments pay --amount 200.00 \--to "$DESTINATION_ACCOUNT_ID" \--expect 3384.65 \--end-user customer_42 --reference ZZ-2026-0042Send the amount you showed as
expectDestination. If the price drifts more thanmaxDriftBps(default 2%) from it, the call fails withRATE_DRIFT_EXCEEDEDand nothing is sent. KeeppayoutIdfor step 4. Sending the same payout again under a new key within 15 minutes (running a second tab, say) is refused with409 DUPLICATE_REQUEST_DETECTEDand nothing is sent; changereferenceto send another. -
Read the payout
Section titled “Read the payout”payoutId=sbx_pay_… # from step 3's responseGET /payments/organizations/{orgId}/orders/{payoutId} curl -s "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/orders/$payoutId" \-H "x-api-key: $AVVIO_API_KEY"GET /payments/organizations/{orgId}/orders/{payoutId} const order = await avvio.getPayout(payout.payoutId);// pending → processing → completed, and sometimes back to failedGET /payments/organizations/{orgId}/orders/{payoutId} import osimport requestspayoutId = "<payoutId>"res = requests.get(f"{os.environ['AVVIO_BASE_URL']}/payments/organizations/{os.environ['AVVIO_ORG_ID']}/orders/{payoutId}",headers={"x-api-key": os.environ["AVVIO_API_KEY"],},)print(res.status_code, res.json())GET /payments/organizations/{orgId}/orders/{payoutId} avvio-payments status $payoutIdIn the sandbox the payout reaches
completed10 to 20 seconds after you create it. To follow many payouts, use webhooks and the event feed rather than polling:GET …/orders/{payoutId}shares the default 100-requests-a-minute limit, so poll one payout no more than about every 5 seconds. The recipe shows each response and makes a payout fail on purpose; run that before you go live.
Other languages
Section titled “Other languages”The OpenAPI spec generates a client in any language, and our CI checks the
spec’s schemas against live responses (it does not build or run a generated
client). Swap -g python for java, go, ruby, csharp
or php.
npx @openapitools/openapi-generator-cli generate \ -i partner-payouts.openapi.yaml -g python -o ./avvio --package-name avvio_payoutsNext steps
Section titled “Next steps”Was this page helpful?