How to: Implement Passive Crypto Pay-In Addresses (API)

Issue a persistent crypto deposit address for a customer, list and manage existing addresses, and react to deposits via webhook.

This page is for advanced / cryptocurrency-native companies. If that’s not you, head back to the Quickstart for the standard flows.

This feature is opt-in only and must be enabled by Coinflow. Before any of the calls on this page will succeed, your merchant account needs enableStableDepositAddresses=true. Reach out to your Coinflow integrations contact — there is no self-serve toggle. Until the flag is flipped, the create endpoint returns 400 Passive crypto pay-in deposit addresses are disabled for this merchant.

This guide walks through the end-to-end integration of passive crypto pay-in addresses.

All merchant IDs, customer IDs, addresses, and emails on this page are examples only and are not reflective of real production values. Replace them with your own values when integrating.

Authentication & Base URLs

The passive-address endpoints are split into two sets:

  • Customer-facing (called from your end-user’s session with a Coinflow session key or wallet auth): POST /api/checkout/crypto-deposit-address/{merchantId}
  • Merchant-side (called from your backend with merchant credentials, or by a Coinflow admin): /api/merchant/customer-payin-addresses/...

Sandbox base URL: https://api-sandbox.coinflow.cash Production base URL: https://api.coinflow.cash

1. List Supported Chains

Returns the chain names accepted by the create endpoint. The list is environment-aware — sandbox returns testnets (e.g. Polygon Amoy, Solana Devnet, Base Sepolia), production returns mainnets.

Request
$curl --location 'https://api-sandbox.coinflow.cash/api/merchant/customer-payin-addresses/supported-chains' \
> --header 'Authorization: {api_key}'
Response
1{
2 "chains": ["Polygon Amoy", "Solana Devnet", "Base Sepolia"]
3}

Pass any returned name verbatim as the chain field in subsequent calls. The match is case- and whitespace-insensitive.

2. Create (or Retrieve) a Passive Address

The same call is used to mint a new address and to retrieve an existing one — the response shape is identical and the operation is idempotent on (merchantId, customerId, chain).

Use this when your server issues the address. customerId comes from your system; Coinflow uses it as the stable scoping key for the address.

Request
$curl --location --request POST \
> 'https://api-sandbox.coinflow.cash/api/merchant/customer-payin-addresses?customerId=cust_12345' \
> --header 'Authorization: {api_key}' \
> --header 'Content-Type: application/json' \
> --data '{
> "email": "alice@example.com",
> "chain": "Solana Devnet"
> }'
Response
1{
2 "depositAddress": "9XyZ...abc",
3 "chain": "Solana Devnet",
4 "status": "active",
5 "createdAt": "2026-05-20T12:34:56.000Z"
6}

Display depositAddress to your customer alongside the chain name. The customer can send USDC (or any supported stablecoin/asset on that chain — see Supported Tokens and Chains) in any amount. Coinflow swaps to USDC on settlement.

Errors

StatusCauseFix
400 Token USDC is not supported on chain {chain}The chain was recognized but doesn’t route stablecoins there.Pick a chain from the supported list.
400 Unsupported chain: {chain}The chain name doesn’t match any supported chain.Use a value returned by /supported-chains.
400 Passive crypto pay-in deposit addresses are disabled for this merchantenableStableDepositAddresses is false on the merchant.Contact Coinflow integrations. Returning an existing record is unaffected.
400 Merchant has reached its active passive pay-in address cap (N)The active count is at maxActivePassiveAddresses.Pause an existing address with POST /deactivate, or ask Coinflow to raise the cap.
400 A valid email is requiredemail failed RFC validation.Pass a valid email.
400 Merchant {id} does not have crypto pay-ins enabledcryptoPayinSettings.enabled is false.Contact Coinflow integrations.

3. List a Customer’s Addresses

Returns every address Coinflow has minted for a (merchant, customerId) pair, newest first, across all chains.

Request
$curl --location 'https://api-sandbox.coinflow.cash/api/merchant/customer-payin-addresses/cust_12345' \
> --header 'Authorization: {api_key}'
Response
1{
2 "addresses": [
3 {
4 "depositAddress": "0xAbc...123",
5 "chain": "Polygon Amoy",
6 "status": "active",
7 "createdAt": "2026-05-20T12:34:56.000Z"
8 },
9 {
10 "depositAddress": "9XyZ...abc",
11 "chain": "Solana Devnet",
12 "status": "paused",
13 "createdAt": "2026-05-15T08:00:00.000Z"
14 }
15 ]
16}

Use this to populate a customer’s “Crypto Funding” page in your app.

4. Pause an Address (Deactivate)

Pauses a single address. Once paused, any deposit that subsequently lands on the address will not produce a Payment record — funds may still clear on-chain briefly during reconciliation, but Coinflow will refuse them.

Request
$curl --location --request POST \
> 'https://api-sandbox.coinflow.cash/api/merchant/customer-payin-addresses/deactivate' \
> --header 'Authorization: {api_key}' \
> --header 'Content-Type: application/json' \
> --data '{ "depositAddress": "0xAbc...123" }'
Response
1{ "depositAddress": "0xAbc...123", "status": "paused" }

5. Resume an Address (Activate)

Re-activates a paused address. Subject to the same flag and cap checks as minting a new address.

Request
$curl --location --request POST \
> 'https://api-sandbox.coinflow.cash/api/merchant/customer-payin-addresses/activate' \
> --header 'Authorization: {api_key}' \
> --header 'Content-Type: application/json' \
> --data '{ "depositAddress": "0xAbc...123" }'
Response
1{ "depositAddress": "0xAbc...123", "status": "active" }
StatusCauseFix
404 No pay-in address {address} found for this merchantThe address isn’t owned by the authenticated merchant.Verify ownership; this endpoint never crosses merchant boundaries.
400 Passive crypto pay-in deposit addresses are disabled for this merchantFlag was turned off by an admin.Contact Coinflow integrations to re-enable.
400 Merchant has reached its active passive pay-in address cap (N)Activating would push the merchant past the cap.Pause another address first.

6. React to Deposits via Webhook

Every successful deposit on a passive address creates a Payment record and dispatches the standard Payment Settled webhook. There is no separate “passive deposit” event — passive deposits look like any other settled payment from your webhook handler’s perspective.

Example Payment Settled payload
1{
2 "eventType": "Payment Settled",
3 "category": "Purchase",
4 "created": "2026-05-20T12:40:00.000Z",
5 "data": {
6 "paymentId": "9d1f...example",
7 "merchantId": "merch_abc",
8 "customer": "cust_12345",
9 "totals": {
10 "subtotal": { "cents": 4980, "currency": "USD" },
11 "total": { "cents": 5000, "currency": "USD" },
12 "merchantPaidCreditCardFees": { "cents": 20, "currency": "USD" }
13 },
14 "cryptoInfo": {
15 "status": "settled",
16 "sessionId": "ses_session_example"
17 }
18 }
19}

paymentId, merchantId, customer, sessionId, and amounts shown are examples only and not reflective of real production values.

To enable: Coinflow Admin Dashboard → Developers → Webhooks, select the latest version, and ensure Payment Settled is subscribed. See Configuring Webhooks for setup.

Coinflow sends over/underpayment events (Crypto Overpayment, Crypto Underpayment) for fixed-amount token payments — but those events do not fire for passive addresses, because passive addresses have no preset amount to over- or underpay.

Best Practices

A passive address is only valid on the chain it was minted for. Always show the chain name (Polygon, Solana, etc.) next to the address in your UI so the customer doesn’t send funds on the wrong network.

The endpoint is idempotent on (merchantId, customerId, chain). Don’t cache stale addresses across redeploys — re-request and Coinflow will return the same record. If you support multiple chains per customer, mint each lazily on demand.

On-chain confirmation is necessary but not sufficient for funds to appear on your books — Coinflow only credits the merchant once the Payment Settled webhook fires, which is also when the address’s status was checked. Build your accounting around the webhook, not a chain listener.

There’s no delete operation. To stop accepting funds on an address, call POST /deactivate. The record stays so a future (merchant, customer, chain) lookup still works, but no Payment will be created from further deposits. Pausing also stops the per-active-address billing for that record — see Billing below.

maxActivePassiveAddresses is set per-merchant by Coinflow and cannot be raised or lowered via API or dashboard. If your model has high churn (many one-off customers minting addresses), ask Coinflow to set a cap so a sudden spike doesn’t fan out into a runaway number of active sessions — and a runaway invoice. Paused addresses don’t count against the cap or toward billing.

Billing

Coinflow charges a per-active-address fee for every (customer, chain) pair that is in the active state. A single customer with an active address on Polygon and another on Solana counts as two active addresses for billing purposes — the fee scales linearly with the number of active (customer, chain) combinations you keep open.

  • Only active addresses incur the per-address fee. The moment you POST /deactivate an address, billing stops for it. Calling POST /activate later resumes both the address and the fee.
  • Mint per chain only as needed. If a customer only ever deposits on one chain, don’t pre-mint on every supported chain — each pre-mint is another billable record.
  • Ask Coinflow to set a maxActivePassiveAddresses cap if you want a hard ceiling on your active-address fee exposure. See the Active Address Cap section of the overview for details.

Per-address pricing is defined in your merchant contract — contact your Coinflow account manager for current rates.