Skip to main content
POST
Create Address
Mint a deposit address. Two modes:
  • external_ref set — mint a per-user address. The partner-user’s profile is lazy-provisioned on first sight.
  • external_ref absent — mint a tenant-treasury address (pooled / no per-user attribution). Response omits external_ref.
Idempotency: the Idempotency-Key header is required for this endpoint (enforced by idempotency_required). Same key + same body within 24h replays the original response verbatim — partner retries are safe. Same key + different body returns 409 idempotency_conflict. Beneath the Connect-side idempotency the underlying AddressService.ensure_primary_address is itself idempotent on (user, asset, network) via a Postgres advisory lock. Two paths to the same address therefore converge whether the partner sends the Idempotency-Key or not — the header is the cleaner contract, the lock is the safety net. Note idem is taken before auth in the dependency order above so the Idempotency-Key header is parsed (which can 400 on its own) before the DB hit for the approval gate. Both happen long before we touch BitGo / Rhino. Sandbox routing: sk_test_… keys (auth.mode == "sandbox") are routed to ConnectAddressesSandboxService, which makes no downstream calls and returns a deterministic fake address. This is the compliance-relevant boundary — the single point in the codebase where sandbox traffic is prevented from reaching real custody infrastructure. sk_live_… keys continue to hit the real path.

Authorizations

Authorization
string
header
required

Paste your Connect API key (sk_live_… for production, sk_test_… for sandbox) without the Bearer prefix. Mint and rotate keys from the admin panel.

Headers

Idempotency-Key
string | null

Body

application/json

Request body for POST /connect/v1/addresses.

Two modes, switched on the presence of external_ref:

  • external_ref set → mint a deposit address for that partner-user (lazy-provisioning the underlying profile + mapping on first sight).
  • external_ref absent → mint a tenant-treasury / pooled address (no per-user attribution).

The partner-facing semantics are identical in both modes; the only difference is whether the response echoes external_ref.

Forbidden extras: typo'd field names surface as a 422 rather than being silently ignored. Partners can recover by checking their request body shape against the docs.

asset
string
required

Asset code (USDT, XAUT). Case-insensitive.

Required string length: 2 - 16
network
string
required

Network code (solana, ethereum). Case-insensitive.

Required string length: 2 - 16
external_ref
string | null

Partner's user identifier. Omit to mint a tenant-treasury address (no per-user attribution).

Maximum string length: 255
label
string | null

Optional partner-supplied label persisted on the address row for the partner's own records.

Maximum string length: 255

Response

Successful Response

A single deposit-address row in the partner-facing shape.

id
string
required

Opaque partner-stable identifier (e.g. addr_VVNEVDpzb2xhbmE6RWhKO...). Pass to GET /addresses/:id to retrieve this row again.

asset
string
required

Asset code, uppercase (USDT, XAUT).

network
string
required

Network code, lowercase (solana, ethereum).

address
string
required

On-chain wallet address. Partner shares this with the user.

created_at
string<date-time>
required
external_ref
string | null

The partner's user id this address belongs to. Absent (key omitted from the JSON, not null) when the address is a tenant-treasury / pooled address minted by calling POST /addresses without external_ref.

memo
string | null

Memo / destination tag for chains that require one (XRP, XLM, COSMOS). NULL on chains that don't.