Skip to main content
BVNK

Send stablecoin payouts

Beta

The Payments v.3 API endpoints are in beta and may change as we enhance the product. For the concepts this guide builds on, see Payments API v.3.

BVNK's v.3 payouts endpoint, POST /payment/v3/payouts, is a single resource that creates, prices, and, when possible, sends a payout. The response depends on the request parameters, not a flag.

  • Send a destination currency and a crypto address, and BVNK creates the payout and sends it immediately.
  • Leave the destination currency out, and BVNK returns an unpriced payout plus a list of currencies and networks your payee can choose from.
  • Send the destination currency and network but not the address, and BVNK returns a priced quote you can still adjust before sending.
  • Add "confirmationRequired": true to an otherwise complete payout, and BVNK creates it fully priced and accepts the quote, but holds it at PENDING_VERIFICATION for your customer to confirm instead of sending it.

Each payout involves three currencies: the originator (your wallet), the beneficiary

(your payee), and an optional display currency on checkout to show the payment value in familiar terms. You set the amount for one currency; BVNK calculates the others and updates them with each reprice.

You can create a v.3 payout using the following approaches:

  • With details known upfront: send everything in one call, and BVNK sends the payout immediately. See Payout with details.
  • Without details: send an amount but not the destination, and let your payee choose the currency and network afterwards, either through your own integration or BVNK's hosted page. See Payout without details.
  • Quote, then accept: fix one currency, add the beneficiary and destination once you have them, show your customer a firm price, then accept. See Quote flow.
  • Pending verification: create a fully priced payout and hold it for your customer to confirm before it sends. See Pending verification.

Create a payout​

Both approaches below call the same POST /payment/v3/payouts endpoint. What you get back depends on what you send it, not on a flag:

Payout with details​

"With details" means the destination is provided upfront: the beneficiary's currency and crypto address are included in the create call, so BVNK prices and sends the payout in a single step. BVNK still prices the payout as a quote, but accepts it on your behalf at the current rate, so there is no quote for you to accept and no second call. The response shows conversion.type: "QUOTE", and statusHistory[] records PENDING_QUOTE_ACCEPTANCE and PROCESSING at the same timestamp. The rate applied is conversion.allInRate: the mid-market conversion.baseRate adjusted by BVNK's spread, which is disclosed separately in conversion.spread. Use this flow for payroll or similar cases where the destination is already known.

Send the full destination in one call:

Request: POST /payment/v3/payouts (with details)
{
"reference": "REF46730",
"expiryDuration": "PT30M",
"originator": {
"walletId": "a:25022613287255:zmHs0pg:1"
},
"beneficiary": {
"currency": "USDT",
"requesterIpAddress": "77.71.188.87",
"entity": {
"type": "INDIVIDUAL",
"firstName": "John",
"lastName": "Mirra",
"dateOfBirth": "1984-06-30",
"relationshipType": "THIRD_PARTY",
"address": {
"country": "DE"
}
},
"paymentInstrument": {
"type": "CRYPTO_ADDRESS",
"network": "ETHEREUM",
"address": "0x02ae6765C6991813a3EAa86fe63ebBCA1c9EC156",
"tag": null
}
},
"checkout": {
"amount": "10",
"currency": "USD",
"returnUrl": "https://merchant.com/status"
}
}

Key considerations:

  • reference must be unique for each payout. Reusing a reference returns a unique validation error and does not create a duplicate.
  • beneficiary.paymentInstrument.type is required. Since v.3 shares this endpoint with fiat payouts, BVNK infers whether the payout is crypto or fiat from this field. There is no request-side method; it is returned on the response.
  • Only originator.walletId is required for the originator. BVNK derives originator.currency from the wallet.
  • Including a network and address in beneficiary.paymentInstrument enables immediate sending. When you provide a network, address, and, if required, tag with a currency, the payout is complete. address is write-only: the response returns it in beneficiary.paymentInstrument.addresses[].
  • beneficiary.entity identifies the payee. Provide either this or beneficiary.contactId, not both.
  • Set only one of originator.amount, beneficiary.amount, or checkout.amount. In this example, the display amount is set to 10 USD; the wallet debit (EUR) and payee's USDT amount are derived from this value. The response returns that ask as checkout.required. originator.actual shows the debit as soon as the payout is sent, and checkout.actual and every other actual have an amount of 0.00 until the payout reaches COMPLETED.
Reuse a stored contact instead of an inline entity

You can create a reusable contact and reference it with beneficiary.contactId instead of passing entity for each payout. For more information, see Manage contacts. BVNK rejects requests that include both entity and contactId.

When all required information is provided, BVNK creates and sends the payout in a single call:

Response: POST /payment/v3/payouts (with details)
{
"id": "b9bf94a8-2e2e-4d29-b8d1-5cdf658e6731",
"reference": "REF46730",
"type": "payment:payout",
"status": "PROCESSING",
"direction": "OUT",
"method": "CRYPTO",
"fees": {
"customerFee": null,
"processingFee": { "estimated": { "currency": "EUR", "amount": "0.09" }, "actual": { "currency": "EUR", "amount": "0.00" } },
"networkFee": { "estimated": { "currency": "EUR", "amount": "1.00" }, "actual": { "currency": "EUR", "amount": "0.00" } }
},
"originator": {
"required": { "currency": "EUR", "amount": "9.21" },
"actual": { "currency": "EUR", "amount": "9.21" },
"walletId": "a:25022613287255:zmHs0pg:1"
},
"beneficiary": {
"required": { "currency": "USDT", "amount": "9.997368" },
"actual": { "currency": "USDT", "amount": "0.00" },
"customerId": null,
"customerReference": null,
"requesterIpAddress": "77.71.188.87",
"entity": {
"type": "INDIVIDUAL",
"firstName": "John",
"lastName": "Mirra",
"dateOfBirth": "1984-06-30",
"relationshipType": "THIRD_PARTY",
"address": {
"country": "DE"
}
},
"paymentInstrument": {
"type": "CRYPTO_ADDRESS",
"network": "ETHEREUM",
"addresses": ["0x02ae6765C6991813a3EAa86fe63ebBCA1c9EC156"],
"tag": null
}
},
"conversion": {
"type": "QUOTE",
"baseRate": "1.090945",
"allInRate": "1.085490537307",
"spread": { "percentage": "0.005", "amount": "0.05", "currency": "EUR" },
"validityDuration": "PT30M",
"expiresAt": "2026-03-14T21:13:23Z",
"priceLockDuration": "PT30S",
"priceLockExpiresAt": "2026-03-14T20:43:53Z"
},
"checkout": {
"required": { "currency": "USD", "amount": "10" },
"actual": { "currency": "USD", "amount": "0.00" },
"displayRate": {
"from": "USDT",
"to": "USD",
"rate": "1.000263269292"
},
"redirectUrl": "https://pay.bvnk.com/payout/b9bf94a8-2e2e-4d29-b8d1-5cdf658e6731",
"returnUrl": "https://merchant.com/status"
},
"instructions": [],
"trackingDetails": [],
"labels": [],
"relatedPayments": [],
"statusHistory": [
{ "status": "PENDING_QUOTE_ACCEPTANCE", "timestamp": "2026-03-14T20:43:23Z" },
{ "status": "PROCESSING", "timestamp": "2026-03-14T20:43:23Z" }
],
"flow": "default",
"metadata": {},
"expiryDuration": "PT30M",
"expiresAt": "2026-03-14T21:13:23Z",
"updatedAt": "2026-03-14T20:43:23Z",
"createdAt": "2026-03-14T20:43:23Z"
}

A payout with details doesn't wait at PENDING_QUOTE_ACCEPTANCE: BVNK accepts the quote at creation, and the payout moves straight to PROCESSING. Conditional statuses are shown as dashed boxes. For what each status means, see Payment statuses.

Payout status flow in v.3. A payout starts at PENDING_QUOTE_ACCEPTANCE and passes through two conditional gates, PENDING_VERIFICATION and PENDING_APPROVAL, before reaching PROCESSING and then COMPLETED. Either gate can end at REJECTED, and anything before PROCESSING can end at EXPIRED. From PROCESSING, a payout can move to HELD, fail at FAILED, or enter the conditional PENDING_RECALL, which resolves to RECALL_REJECTED or RETURNED. A completed payout can also end at RETURNED.

Integrate advanced payout capabilities​

Two flows are available here:

  • Quote: present your customer with a firm, adjustable price before committing, fixing either side of the trade:
    • Use fixed debit to spend a specific amount without yet knowing the destination.
    • Use fixed credit to decide exactly what the payee receives and let your own cost float.
  • Pending verification: create a fully priced payout with the quote accepted, and hold it for your customer to confirm before it sends, instead of sending immediately.

Create an incomplete payout, refine the details, and accept when ready to send:

  1. Create the quote by sending the POST /payment/v3/payouts request.

    Fix an amount on either the originator or beneficiary side:

    • originator.amount creates a fixed debit quote, specifying the exact amount to debit from your wallet.
    • beneficiary.amount creates a fixed credit quote, ensuring the payee receives the specified amount.
Request: POST /payment/v3/payouts (fixed debit quote)
{
"reference": "REF88214",
"originator": {
"amount": "25.00",
"walletId": "a:24072237783989:Unkx9kW:1"
},
"beneficiary": {
"currency": "USDT",
"requesterIpAddress": "172.16.254.1",
"paymentInstrument": {
"type": "CRYPTO_ADDRESS",
"network": "ETHEREUM"
}
}
}
Response: POST /payment/v3/payouts (fixed debit quote)
{
"id": "019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd",
"reference": "REF88214",
"type": "payment:payout",
"status": "PENDING_QUOTE_ACCEPTANCE",
"direction": "OUT",
"method": "CRYPTO",
"fees": {
"customerFee": null,
"processingFee": { "estimated": { "currency": "USD", "amount": "0.25" }, "actual": { "currency": "USD", "amount": "0.00" } },
"networkFee": { "estimated": { "currency": "USD", "amount": "1.62" }, "actual": { "currency": "USD", "amount": "0.00" } }
},
"originator": {
"required": { "currency": "USD", "amount": "25.00" },
"actual": { "currency": "USD", "amount": "0.00" },
"walletId": "a:24072237783989:Unkx9kW:1"
},
"beneficiary": {
"required": { "currency": "USDT", "amount": "23.742875" },
"actual": { "currency": "USDT", "amount": "0.00" },
"customerId": null,
"customerReference": null,
"requesterIpAddress": "172.16.254.1",
"paymentInstrument": {
"type": "CRYPTO_ADDRESS",
"network": "ETHEREUM",
"addresses": [],
"tag": null
}
},
"conversion": {
"type": "QUOTE",
"baseRate": "0.954488",
"allInRate": "0.949715",
"spread": { "percentage": "0.005", "amount": "0.13", "currency": "USD" },
"digest": "c41f8ae2b93d07f5",
"validityDuration": "PT1H",
"expiresAt": "2026-08-18T15:25:00Z",
"priceLockDuration": "PT30S",
"priceLockExpiresAt": "2026-08-18T14:25:30Z"
},
"instructions": [],
"trackingDetails": [],
"labels": [],
"relatedPayments": [],
"statusHistory": [
{ "status": "PENDING_QUOTE_ACCEPTANCE", "timestamp": "2026-08-18T14:25:00Z" }
],
"flow": "default",
"metadata": {},
"expiryDuration": "PT1H",
"expiresAt": "2026-08-18T15:25:00Z",
"updatedAt": "2026-08-18T14:25:00Z",
"createdAt": "2026-08-18T14:25:00Z"
}
Supply the beneficiary upfront instead

If you already know who you're paying, send beneficiary.entity or contactId and beneficiary.paymentInstrument.network in step 1 instead of waiting until step 4. This is the same fixed debit sequence, but only the destination address stays outstanding. Resolving the entity early screens compliance and confirms fees before your customer sees a price. relationshipType can change crypto pricing, so an entity added later may reprice the fee. On fiat, it is the only way to get fees at all, since fiat cannot price a fee without a beneficiary.

  1. If the quote is about to expire and neither the amount nor the beneficiary needs to change, refresh it. Include the last conversion.digest so BVNK reprices the correct quote:

    Request: POST /payment/v3/payouts/019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd/action
    {
    "type": "REFRESH_QUOTE",
    "digest": "c41f8ae2b93d07f5"
    }

    The payout remains in PENDING_QUOTE_ACCEPTANCE, with a new rate and a new digest:

    Response: key fields after REFRESH_QUOTE
    {
    "id": "019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd",
    "reference": "REF88214",
    "type": "payment:payout",
    "status": "PENDING_QUOTE_ACCEPTANCE",
    "direction": "OUT",
    "method": "CRYPTO",
    "fees": {
    "customerFee": null,
    "processingFee": { "estimated": { "currency": "USD", "amount": "0.25" }, "actual": { "currency": "USD", "amount": "0.00" } },
    "networkFee": { "estimated": { "currency": "USD", "amount": "1.62" }, "actual": { "currency": "USD", "amount": "0.00" } }
    },
    "originator": {
    "required": { "currency": "USD", "amount": "25.00" },
    "actual": { "currency": "USD", "amount": "0.00" },
    "walletId": "a:24072237783989:Unkx9kW:1"
    },
    "beneficiary": {
    "required": { "currency": "USDT", "amount": "23.705250" },
    "actual": { "currency": "USDT", "amount": "0.00" },
    "customerId": null,
    "customerReference": null,
    "requesterIpAddress": "172.16.254.1",
    "paymentInstrument": {
    "type": "CRYPTO_ADDRESS",
    "network": "ETHEREUM",
    "addresses": [],
    "tag": null
    }
    },
    "conversion": {
    "type": "QUOTE",
    "baseRate": "0.952975",
    "allInRate": "0.948210",
    "spread": { "percentage": "0.005", "amount": "0.13", "currency": "USD" },
    "digest": "7b21e0c4d95a3f18",
    "validityDuration": "PT59M18S",
    "expiresAt": "2026-08-18T15:25:00Z",
    "priceLockDuration": "PT30S",
    "priceLockExpiresAt": "2026-08-18T14:26:12Z"
    },
    "instructions": [],
    "trackingDetails": [],
    "labels": [],
    "relatedPayments": [],
    "statusHistory": [
    { "status": "PENDING_QUOTE_ACCEPTANCE", "timestamp": "2026-08-18T14:25:00Z" }
    ],
    "flow": "default",
    "metadata": {},
    "expiryDuration": "PT1H",
    "expiresAt": "2026-08-18T15:25:00Z",
    "updatedAt": "2026-08-18T14:25:42Z",
    "createdAt": "2026-08-18T14:25:00Z"
    }
    conversion.digest

    digest is a fingerprint of the current quote. BVNK issues a new one on create, on every REFRESH_QUOTE, and on every PATCH, because a PATCH reprices the whole quote whether or not you change an amount. REFRESH_QUOTE requires the latest digest and rejects a stale one, so a reprice always acts on the quote your customer last saw. ACCEPT_QUOTE doesn't need a digest and doesn't issue a new one.

  2. If your customer wants to adjust the amount, update it by sending a PATCH /payment/v3/payouts/{id} request with the same field you originally fixed. A PATCH reprices the entire quote, even if the amount remains unchanged:

    Request: PATCH /payment/v3/payouts/019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd
    {
    "originator": {
    "amount": "30.00"
    }
    }
    Response: key fields after changing the amount
    {
    "id": "019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd",
    "reference": "REF88214",
    "type": "payment:payout",
    "status": "PENDING_QUOTE_ACCEPTANCE",
    "direction": "OUT",
    "method": "CRYPTO",
    "fees": {
    "customerFee": null,
    "processingFee": { "estimated": { "currency": "USD", "amount": "0.30" }, "actual": { "currency": "USD", "amount": "0.00" } },
    "networkFee": { "estimated": { "currency": "USD", "amount": "1.62" }, "actual": { "currency": "USD", "amount": "0.00" } }
    },
    "originator": {
    "required": { "currency": "USD", "amount": "30.00" },
    "actual": { "currency": "USD", "amount": "0.00" },
    "walletId": "a:24072237783989:Unkx9kW:1"
    },
    "beneficiary": {
    "required": { "currency": "USDT", "amount": "28.446300" },
    "actual": { "currency": "USDT", "amount": "0.00" },
    "customerId": null,
    "customerReference": null,
    "requesterIpAddress": "172.16.254.1",
    "paymentInstrument": {
    "type": "CRYPTO_ADDRESS",
    "network": "ETHEREUM",
    "addresses": [],
    "tag": null
    }
    },
    "conversion": {
    "type": "QUOTE",
    "baseRate": "0.952975",
    "allInRate": "0.948210",
    "spread": { "percentage": "0.005", "amount": "0.15", "currency": "USD" },
    "digest": "e5c8a7f2b1904d63",
    "validityDuration": "PT58M25S",
    "expiresAt": "2026-08-18T15:25:00Z",
    "priceLockDuration": "PT30S",
    "priceLockExpiresAt": "2026-08-18T14:27:05Z"
    },
    "instructions": [],
    "trackingDetails": [],
    "labels": [],
    "relatedPayments": [],
    "statusHistory": [
    { "status": "PENDING_QUOTE_ACCEPTANCE", "timestamp": "2026-08-18T14:25:00Z" }
    ],
    "flow": "default",
    "metadata": {},
    "expiryDuration": "PT1H",
    "expiresAt": "2026-08-18T15:25:00Z",
    "updatedAt": "2026-08-18T14:26:35Z",
    "createdAt": "2026-08-18T14:25:00Z"
    }
  3. Provide the beneficiary and destination address by sending a PATCH /payment/v3/payouts/{id} request while the payout is still a quote. This allows your customer to review a final priced quote before committing. When updating a nested object, include the entire object rather than a single field:

    Request: PATCH /payment/v3/payouts/019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd
    {
    "beneficiary": {
    "entity": {
    "type": "INDIVIDUAL",
    "firstName": "John",
    "lastName": "Doe",
    "dateOfBirth": "1984-06-30",
    "relationshipType": "THIRD_PARTY",
    "address": {
    "addressLine1": "Hauptstrasse 42",
    "addressLine2": null,
    "city": "Munich",
    "region": "Bavaria",
    "postCode": "80331",
    "country": "DE"
    }
    },
    "paymentInstrument": {
    "type": "CRYPTO_ADDRESS",
    "network": "ETHEREUM",
    "address": "0x02ae4f91C7B3d0521a5E9c3b7F84D6a1c9E1C156",
    "tag": null
    }
    }
    }
    Response: PATCH /payment/v3/payouts/019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd
    {
    "id": "019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd",
    "reference": "REF88214",
    "type": "payment:payout",
    "status": "PENDING_QUOTE_ACCEPTANCE",
    "direction": "OUT",
    "method": "CRYPTO",
    "fees": {
    "customerFee": null,
    "processingFee": { "estimated": { "currency": "USD", "amount": "0.30" }, "actual": { "currency": "USD", "amount": "0.00" } },
    "networkFee": { "estimated": { "currency": "USD", "amount": "1.62" }, "actual": { "currency": "USD", "amount": "0.00" } }
    },
    "originator": {
    "required": { "currency": "USD", "amount": "30.00" },
    "actual": { "currency": "USD", "amount": "0.00" },
    "walletId": "a:24072237783989:Unkx9kW:1"
    },
    "beneficiary": {
    "required": { "currency": "USDT", "amount": "28.446300" },
    "actual": { "currency": "USDT", "amount": "0.00" },
    "customerId": null,
    "customerReference": null,
    "requesterIpAddress": "172.16.254.1",
    "entity": {
    "type": "INDIVIDUAL",
    "firstName": "John",
    "lastName": "Doe",
    "dateOfBirth": "1984-06-30",
    "relationshipType": "THIRD_PARTY",
    "address": {
    "addressLine1": "Hauptstrasse 42",
    "addressLine2": null,
    "city": "Munich",
    "region": "Bavaria",
    "postCode": "80331",
    "country": "DE"
    }
    },
    "paymentInstrument": {
    "type": "CRYPTO_ADDRESS",
    "network": "ETHEREUM",
    "addresses": ["0x02ae4f91C7B3d0521a5E9c3b7F84D6a1c9E1C156"],
    "tag": null
    }
    },
    "conversion": {
    "type": "QUOTE",
    "baseRate": "0.952975",
    "allInRate": "0.948210",
    "spread": { "percentage": "0.005", "amount": "0.15", "currency": "USD" },
    "digest": "a90d4e17c6b52f8a",
    "validityDuration": "PT57M20S",
    "expiresAt": "2026-08-18T15:25:00Z",
    "priceLockDuration": "PT30S",
    "priceLockExpiresAt": "2026-08-18T14:28:10Z"
    },
    "instructions": [],
    "trackingDetails": [],
    "labels": [],
    "relatedPayments": [],
    "statusHistory": [
    { "status": "PENDING_QUOTE_ACCEPTANCE", "timestamp": "2026-08-18T14:25:00Z" }
    ],
    "flow": "default",
    "metadata": {},
    "expiryDuration": "PT1H",
    "expiresAt": "2026-08-18T15:25:00Z",
    "updatedAt": "2026-08-18T14:27:40Z",
    "createdAt": "2026-08-18T14:25:00Z"
    }

    A PATCH reprices the whole quote, so the response carries a new conversion.digest and a new rate lock, conversion.priceLockExpiresAt, even when the price doesn't change. Adding the address doesn't send the payout: only accepting the quote does. Present this response to your customer so they can review the final rate, fees, and payee details before proceeding.

  4. Once your customer confirms, accept the quote by sending the POST /payment/v3/payouts/{id}/action request with "type": "ACCEPT_QUOTE".

    Request: POST /payment/v3/payouts/019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd/action
    {
    "type": "ACCEPT_QUOTE"
    }
    Response: POST /payment/v3/payouts/019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd/action
    {
    "id": "019f2a3b-7c41-7e2a-9b55-4d1e8f0aabcd",
    "reference": "REF88214",
    "type": "payment:payout",
    "status": "PROCESSING",
    "direction": "OUT",
    "method": "CRYPTO",
    "fees": {
    "customerFee": null,
    "processingFee": { "estimated": { "currency": "USD", "amount": "0.30" }, "actual": { "currency": "USD", "amount": "0.00" } },
    "networkFee": { "estimated": { "currency": "USD", "amount": "1.62" }, "actual": { "currency": "USD", "amount": "0.00" } }
    },
    "originator": {
    "required": { "currency": "USD", "amount": "30.00" },
    "actual": { "currency": "USD", "amount": "30.00" },
    "walletId": "a:24072237783989:Unkx9kW:1"
    },
    "beneficiary": {
    "required": { "currency": "USDT", "amount": "28.446300" },
    "actual": { "currency": "USDT", "amount": "0.00" },
    "customerId": null,
    "customerReference": null,
    "requesterIpAddress": "172.16.254.1",
    "entity": {
    "type": "INDIVIDUAL",
    "firstName": "John",
    "lastName": "Doe",
    "dateOfBirth": "1984-06-30",
    "relationshipType": "THIRD_PARTY",
    "address": {
    "addressLine1": "Hauptstrasse 42",
    "addressLine2": null,
    "city": "Munich",
    "region": "Bavaria",
    "postCode": "80331",
    "country": "DE"
    }
    },
    "paymentInstrument": {
    "type": "CRYPTO_ADDRESS",
    "network": "ETHEREUM",
    "addresses": ["0x02ae4f91C7B3d0521a5E9c3b7F84D6a1c9E1C156"],
    "tag": null
    }
    },
    "conversion": {
    "type": "QUOTE",
    "baseRate": "0.952975",
    "allInRate": "0.948210",
    "spread": { "percentage": "0.005", "amount": "0.15", "currency": "USD" },
    "digest": "a90d4e17c6b52f8a",
    "validityDuration": "PT57M20S",
    "expiresAt": "2026-08-18T15:25:00Z",
    "priceLockDuration": "PT30S",
    "priceLockExpiresAt": "2026-08-18T14:28:10Z"
    },
    "instructions": [],
    "trackingDetails": [],
    "labels": [],
    "relatedPayments": [],
    "statusHistory": [
    { "status": "PENDING_QUOTE_ACCEPTANCE", "timestamp": "2026-08-18T14:25:00Z" },
    { "status": "PROCESSING", "timestamp": "2026-08-18T14:28:02Z" }
    ],
    "flow": "default",
    "metadata": {},
    "expiryDuration": "PT1H",
    "expiresAt": "2026-08-18T15:25:00Z",
    "updatedAt": "2026-08-18T14:28:02Z",
    "createdAt": "2026-08-18T14:25:00Z"
    }

    There is no separate confirmation or send step; accepting the quote initiates the payout. Ensure beneficiary.entity or contactId is set before calling ACCEPT_QUOTE, either during creation or via a prior PATCH, or the action will fail.

    Quotes expire with the payout

    conversion.expiresAt matches the payout's own expiresAt, set from expiryDuration when you create the payout, and conversion.validityDuration shows the time left. Accepting or refreshing after it has passed fails. The rate itself is locked for conversion.priceLockDuration, 30 seconds, until conversion.priceLockExpiresAt. Every reprice starts a new rate lock.

Check payout status​

Send a GET request to /payment/v3/payouts/{id} at any point to see the payout's current state:

Response: GET /payment/v3/payouts/b9bf94a8-2e2e-4d29-b8d1-5cdf658e6731
{
"id": "b9bf94a8-2e2e-4d29-b8d1-5cdf658e6731",
"reference": "REF46730",
"type": "payment:payout",
"status": "COMPLETED",
"direction": "OUT",
"method": "CRYPTO",
"fees": {
"customerFee": null,
"processingFee": { "estimated": { "currency": "EUR", "amount": "0.09" }, "actual": { "currency": "EUR", "amount": "0.09" } },
"networkFee": { "estimated": { "currency": "EUR", "amount": "1.00" }, "actual": { "currency": "EUR", "amount": "1.00" } }
},
"originator": {
"required": { "currency": "EUR", "amount": "9.21" },
"actual": { "currency": "EUR", "amount": "9.21" },
"walletId": "a:25022613287255:zmHs0pg:1"
},
"beneficiary": {
"required": { "currency": "USDT", "amount": "9.997368" },
"actual": { "currency": "USDT", "amount": "9.997368" },
"customerId": null,
"customerReference": null,
"requesterIpAddress": "77.71.188.87",
"entity": {
"type": "INDIVIDUAL",
"firstName": "John",
"lastName": "Mirra",
"dateOfBirth": "1984-06-30",
"relationshipType": "THIRD_PARTY",
"address": {
"country": "DE"
}
},
"paymentInstrument": {
"type": "CRYPTO_ADDRESS",
"network": "ETHEREUM",
"addresses": ["0x02ae6765C6991813a3EAa86fe63ebBCA1c9EC156"],
"tag": null
}
},
"conversion": {
"type": "QUOTE",
"baseRate": "1.090945",
"allInRate": "1.085490537307",
"spread": { "percentage": "0.005", "amount": "0.05", "currency": "EUR" },
"validityDuration": "PT30M",
"expiresAt": "2026-03-14T21:13:23Z",
"priceLockDuration": "PT30S",
"priceLockExpiresAt": "2026-03-14T20:43:53Z"
},
"checkout": {
"required": { "currency": "USD", "amount": "10" },
"actual": { "currency": "USD", "amount": "10" },
"displayRate": {
"from": "USDT",
"to": "USD",
"rate": "1.000263269292"
},
"redirectUrl": "https://pay.bvnk.com/payout/b9bf94a8-2e2e-4d29-b8d1-5cdf658e6731",
"returnUrl": "https://merchant.com/status"
},
"instructions": [],
"trackingDetails": [
{
"amount": "9.997368",
"currency": "USDT",
"hash": "0x8f14c7b3e5a92d0641fbe7c3a8d15e29047b6c1f9e3a52d8b704c6fa1e93b528",
"network": "ETHEREUM",
"createdAt": "2026-03-14T20:45:36Z",
"confirmedAt": "2026-03-14T20:47:11Z"
}
],
"labels": [],
"relatedPayments": [],
"statusHistory": [
{ "status": "PENDING_QUOTE_ACCEPTANCE", "timestamp": "2026-03-14T20:43:23Z" },
{ "status": "PROCESSING", "timestamp": "2026-03-14T20:43:23Z" },
{ "status": "COMPLETED", "timestamp": "2026-03-14T20:47:11Z" }
],
"flow": "default",
"metadata": {},
"expiryDuration": "PT30M",
"expiresAt": "2026-03-14T21:13:23Z",
"updatedAt": "2026-03-14T20:47:11Z",
"createdAt": "2026-03-14T20:43:23Z"
}

Once the payout reaches COMPLETED, every actual is filled in, on originator, beneficiary, checkout, and each fee, and trackingDetails[] carries the on-chain transaction. Refer to statusHistory[] for a complete audit trail instead of relying on a single status value. For every status a payout can pass through, see Payment statuses.


Provide additional party information​

You may also need to provide additional party information to comply with the Travel Rule. See the Provide additional party information guide for more details.


Handle errors​

A validation error, such as a duplicate reference, is rejected before the payout is created:

Error response: duplicate reference
{
"requestId": "fd3b64b97db85a7ebab39d5a1fb20867",
"errorList": [
{
"parameter": "reference",
"code": "unique",
"message": "Duplicate Reference"
}
]
}

The same error format is returned if beneficiary includes both entity and contactId in the same request. Provide one or the other, not both.

If a payout fails after processing begins, for example due to insufficient wallet balance after network fees are applied, it does not return an error response. Instead, it moves to status: "FAILED", which is visible via GET or the webhook. Refer to statusHistory[] for the transition timestamp.

Payout webhook​

To receive notifications when a payout status changes, subscribe to payment:v3:payout:status-change. This event covers all state transitions, including refunds (payouts with labels: ["REFUND"]). Read the current state from data.status and the full history from data.statusHistory[] instead of relying on a single value.

Was this page helpful?