Single-Use Wallets (Corporate Only)
Generate unique, one-time stablecoin deposit addresses so each incoming payment can be matched to a specific invoice, customer, or vendor.
Not yet available on the Stablecoin API.
POST /stablecoin/corporate/{corporate_uuid}/single-offramp-wallethas not finished migrating: it validates the request body and returns200with an empty object{}— no wallet is created and no address is returned. Do not build against this path yet. The feature is live on the legacy endpoint; contact [email protected] if you need access while the migration completes.
Current behaviour
Verified against sandbox:
| Request | Response |
|---|---|
Valid external_reference | 200 {} — no address, no wallet created |
Missing external_reference | 400 INPUT_VALIDATION_ERROR — external_reference is required |
| Corporate UUID that does not exist | 200 {} — not a 404 |
| Malformed corporate UUID | 400 INPUT_VALIDATION_ERROR — corporate_uuid Invalid UUID |
Only body and path validation run today. A 200 from this endpoint does not mean a wallet exists, and the empty response cannot be distinguished from a successful call — so do not treat 200 as confirmation.
The API reference documents a 201 with chain, address, and external_reference, plus 403 for non-whitelisted corporates and 404 for unknown corporates. That is the intended contract, not current behaviour on this path.
What they do
Each wallet is a one-time stablecoin deposit address. When stablecoins arrive, the funds automatically off-ramp to the corporate's main remote bank account — so a corporate can hand a distinct address to each payer instead of reconciling one shared address by amount and timing.
- A distinct deposit address per request
- Automatic off-ramp on receipt — no polling for balances
- Settles to the corporate's main remote bank account
- Corporates only, and whitelisted
external_referenceis retained for backwards compatibility. It is required and echoed back in the response, but it no longer influences the wallet that is created. Keep sending it, and keep your own mapping from the returnedaddressto your invoice or customer — do not rely on the reference to do that for you.
Use cases
The value is the address: give each payer a distinct one and inbound payments become self-identifying.
| Use case | What you map the address to | Why a unique address helps |
|---|---|---|
| Invoice payments | An invoice ID | Know which customer paid which invoice without asking them to attach a memo. |
| Batch settlements | A settlement batch | Process many payments at once, each reconciling independently. |
| Customer deposits | A customer account | Give each customer their own deposit address; auto-convert and sweep to treasury. |
| Vendor payments | A purchase order | Attribute inbound vendor settlements to a specific PO. |
| Marketplace payouts | A seller account | Attribute buyer payments to the correct seller before settlement. |
| Subscription billing | A billing period | Separate each period so partial or late payments are unambiguous. |
| Rent or recurring collections | A unit and month | Tie each inbound payment to a property and period. |
| Escrow / milestone releases | A deal milestone | Isolate funds per milestone so each release is independently auditable. |
Without single-use wallets every customer pays into the same corporate address and you reconcile by amount and timing — which breaks the moment two customers pay the same amount on the same day. That ambiguity is what this removes.
Prerequisites
- The corporate has completed KYB and is in
ACTIVEstatus — see KYC & KYB. - A main remote bank account is set for the corporate, since that is where funds settle. See Off-Ramp.
- The corporate is whitelisted for the feature by Bakkt.
Flow
- Create — request a wallet, optionally specifying a
chain. - Store the mapping — record the returned
addressagainst your invoice, customer, or PO. This mapping is yours to keep. - Share — present the
addressandchainon the invoice or checkout page. - Customer pays — the customer sends stablecoins to that address.
- Auto off-ramp — Bakkt converts and sends fiat to the corporate's main remote bank account.
- Reconcile — a
cryptoToFiatwebhook fires; resolve the address back to your record. See Webhooks.
Chains and address formats
The wallet is created on the requested chain, defaulting to Polygon when omitted.
Tron addresses are returned in base58. For
chain: "tron"the address comes back in the user-facing base58 form (TXyTzf3Exu…) rather than the hex form used elsewhere in the API. Present it exactly as returned — a hex address is not usable as a Tron deposit address, and funds sent to a mis-formatted address cannot be recovered.
| Chain | Example address format |
|---|---|
polygon, base, and other EVM chains | 0x316aD179a8db32EEBA367479596e3c5511f3F093 |
tron | TXyTzf3ExuXrKMEAFM916MraoKB6MGdxgK |
Request
This is the contract the endpoint will honour once the migration completes. On the Stablecoin path it returns {} today — see Current behaviour.
// Note the `API-Key ` prefix — the raw key on its own returns 401.
const API_KEY = 'API-Key YOUR_API_KEY';
const response = await fetch(
`https://sandbox.api.bakkt.com/stablecoin/corporate/${corporateUuid}/single-offramp-wallet`,
{
method: 'POST',
headers: {
'Authorization': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
external_reference: 'invoice-12345', // required; echoed back, retained for compatibility
chain: 'polygon' // optional; defaults to polygon
})
}
);
const { chain, address, external_reference } = await response.json();
// { chain: 'polygon', address: '0x316aD179…', external_reference: 'invoice-12345' }
// Persist YOUR mapping — the reference does not drive the wallet.
await db.saveDepositAddress({ invoiceId: 'invoice-12345', chain, address });Authenticated with your merchant API key only — no bakkt-session-id required.
While the migration completes
If you need this today, contact [email protected] — the feature is available on the legacy endpoint. Otherwise, the options for attributing inbound payments are:
| Approach | How | Trade-off |
|---|---|---|
| Corporate off-ramp address per bank account | GET /corporate/{corporate_uuid}/wallet/{chain} — see Off-Ramp | One address for all payers; reconcile by amount and timestamp. |
| Per-payer remote bank accounts | Create a remote bank account per destination | Separates settlement, not inbound attribution. |
| Off-chain reference | Ask payers to quote an invoice ID out of band | Relies on the payer; not enforceable on-chain. |
Updated 19 days ago
