Skip to main content

POST /v1/deposit-addresses

Allocate a deposit address to receive an inbound transfer. The gateway either reserves a fresh address for the (user, currency, network) tuple or — for user_identifier-tagged calls — returns the same sticky address you've used before for that identifier.

Method & pathPOST /v1/deposit-addresses
Required permissionrequest_deposit_address
AuthHMAC — see Authentication

Request body

{
"currency": "USDT",
"network": "ETH",
"user_identifier": "user_8721",
"idempotency_key": "alloc_2026_05_24_abc123"
}
FieldTypeRequiredDescription
currencystringrequiredCurrency symbol (1–32 chars). The currency must be enabled for deposits on this account.
networkstringrequiredNetwork code (1–32 chars).
user_identifierstringoptionalFree-form string up to 256 chars. When set, the address is sticky — every call with the same user_identifier returns the same address forever. Omit to draw from the recyclable pool instead.
idempotency_keystringoptionalUp to 128 chars. See "Idempotency" below.

Response

{
"success": true,
"message": "OK",
"data": {
"request_id": 42,
"address": "0xabc...def",
"network": "ETH",
"currency": "USDT",
"contract_address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"user_identifier": "user_8721",
"display_expires_at": "2026-05-16T22:00:00+00:00",
"created_at": "2026-05-16T19:00:00+00:00"
}
}
FieldTypeDescription
request_idintegerUnique id for this allocation. Use it with GET /v1/deposit-requests/:request_id to poll for incoming transactions.
addressstringThe on-chain address to show the payer.
networkstringEchoes the request.
currencystringEchoes the request.
contract_addressstring | nullThe token contract address for tokens (e.g. ERC-20). null for native currency.
user_identifierstring | nullEchoes the request (or null if you didn't supply one).
display_expires_atISO-8601 UTCStop showing this address to your user after this timestamp (default: 3 hours). The gateway keeps monitoring the address after this, so late deposits still credit.
created_atISO-8601 UTCWhen the request was allocated.

Sticky vs pool addresses

The decision tree the gateway runs on every call:

You supplied user_identifier?Existing sticky address for this (account, network, user_identifier)?Result
YesYesReturn the existing sticky address. The same identifier always maps to the same address.
YesNoAllocate a fresh address, mark it sticky, and store the identifier.
NoReuse an idle pooled address belonging to your account (one past its monitoring window). If none is available, allocate a fresh one from the pool.

When to use a sticky identifier: any time you want a stable mapping (per-user deposit address, per-invoice address, per-order address). Sticky addresses are never recycled.

When to skip the identifier: one-off payments where the address only needs to live for a few hours. Pool addresses become reusable after 7 days, so the same address can serve many short-lived payments over time.

Idempotency

A network blip between client and server can leave you unsure whether your POST succeeded. Setting idempotency_key makes retries safe:

  • On the first call with a given (api_key, idempotency_key) pair, the gateway processes the request and remembers the result.
  • On any retry with the same (api_key, idempotency_key), the gateway returns the original allocation instead of allocating a new address.
// Initial POST — succeeds, returns request_id=42
{ "currency": "USDT", "network": "ETH", "idempotency_key": "order_99_attempt_1" }

// Retried after a timeout — same body — returns request_id=42 again
{ "currency": "USDT", "network": "ETH", "idempotency_key": "order_99_attempt_1" }

Keys are scoped per API key only — not per (currency, network). Two consequences:

  • The same idempotency_key under a different API key allocates a new address.
  • The same idempotency_key under the same API key returns the original allocation, even if you change currency or network on the retry. The new request's currency and network are ignored. Make each idempotency_key represent one specific allocation attempt — don't reuse it for a different deposit.

Display & monitoring windows

Every allocation comes with these windows (all configurable; defaults shown):

WindowDefaultWhat it controls
Display3 hHow long you should show the address in your UI. After this, status flips to expired_for_display.
Monitoring7 dThe gateway watches the address for inbound transfers. Deposits that arrive after the display window still confirm and credit.
Reuse7 dPool addresses become eligible for reuse for this same account after this point. Sticky addresses are never reused.

If a deposit arrives even after the display window, it still confirms and credits — the gateway keeps watching the address through the reuse window.

Errors

StatusCause
422Schema validation failed — missing/empty currency or network, user_identifier over 256 chars, idempotency_key over 128 chars. message lists the offending field(s).
401 / 403 / 429Standard middleware — see Errors page.
403 Deposits for this currency are not enabled for this accountThe currency exists and is active, but you haven't toggled deposits on for it via the dashboard's Currencies page.
404 Network not foundnetwork is not a known network code, or it's inactive.
404 Currency not foundcurrency is not a known currency symbol on the given network, or it's inactive.

Code samples

Examples assume sign_request(...) from the Authentication page.

METHOD="POST"
ENDPOINT="/v1/deposit-addresses"
QUERY=""
BODY='{"currency":"USDT","network":"ETH","user_identifier":"user_8721","idempotency_key":"alloc_2026_05_24_abc123"}'

# ... sign as on the Authentication page; remember the body hash is sha256($BODY) ...

curl -sS "${BASE_URL}${ENDPOINT}" \
-X POST \
-H "Content-Type: application/json" \
-H "X-CPG-Key: ${API_KEY}" \
-H "X-CPG-Timestamp: ${TS}" \
-H "X-CPG-Signature: ${SIG}" \
--data-raw "$BODY"

Notes

  • Addresses are a finite resource. Each fresh allocation consumes a new address. Sticky-by-identifier and idempotency-key reuse exist specifically to avoid allocating more than you need — set idempotency_key for every allocation you might retry, and pass a user_identifier when you want a stable per-user address.
  • contract_address is informational. You don't need it to receive funds (the address handles that), but it's useful for showing wallet-app QR codes that bake the token contract into the URI (e.g. EIP-681).
  • Cross-network reuse is not supported. An address allocated on ETH won't receive funds sent on BSC (even though the byte representation matches). You need separate allocations per network.