Customers
Create a customer and deposit wallets
Create a Customer with a UUID ref_id, receive long-lived deposit addresses, and credit your own user ledger on deposit.credited.
Create a customer and deposit wallets
Use the Customers rail when end users in your app need long-lived deposit addresses (and later withdrawals). This is different from Checkout, which creates a one-time payment session.
Prerequisites: Getting started (Secret key + Request-Id) and Webhooks.
User signs up in your app
→ your server POST /v1/customers
→ show wallets[].address to the user
User sends USDC/USDT on-chain
→ Doggie Pay credits your merchant pool
→ webhook deposit.credited
→ you credit that user's balance in your system
1. Who does what
| Concern | Doggie Pay | Your system |
|---|---|---|
| User identity | Customer with ref_id (your user UUID) | Accounts, KYC, risk |
| Deposit address | Long-lived per chain (issued on create) | Display & bind to your user |
| On-chain credit | Credits your merchant crypto balance | — |
| End-user balance | Not stored | Credit from deposit.credited |
| Secret / webhook | Secret key + merchant webhook | Keep Secret server-side; verify HMAC |
Do not use Publishable keys or Portal JWT on these Integration routes.
2. Create a customer
POST /v1/customers
Auth: Secret + X-DoggiePay-Request-Id
Request body
| Field | Type | Required | Description |
|---|---|---|---|
ref_id | string (UUID) | Yes | Your user id. Immutable after create. Must be a UUID. |
name | string | Yes | Display name |
email | string | Yes | |
description | string | No | Free-form note |
blockchains | string[] | No | Chain aliases to open; omit = all platform-enabled chains |
Example
curl -sS https://api.doggiepay.com/v1/customers \
-H "Authorization: Bearer dp_live_sk_YOUR_SECRET" \
-H "X-DoggiePay-Request-Id: $(uuidgen | tr '[:upper:]' '[:lower:]')" \
-H "Content-Type: application/json" \
-d '{
"ref_id": "6ded84e7-5449-4a2b-9c1d-0afcce047b29",
"name": "John Doe",
"email": "[email protected]",
"description": "optional note"
}'
Response data (shape)
| Field | Type | Description |
|---|---|---|
id | uuid | Doggie Pay customer id |
ref_id | uuid | Your user id (echo) |
name / email / description | string | Profile fields |
wallets | array | Deposit addresses (see below) |
created_at / updated_at | string | ISO-8601 |
Each wallets[] item:
| Field | Type | Description |
|---|---|---|
blockchain | string | Chain alias, e.g. ETH |
currency | string | e.g. USDC, USDT |
address | string | Deposit address |
status | string | e.g. active |
Same chain often shares one address across currencies; rows are expanded per currency for UI convenience.
Wallets are created in the same request. If wallet provisioning fails, the whole create fails (no half-created customer without addresses).
Persist id, ref_id, and the addresses you show to users. For all later deposit/withdraw APIs, pass this ref_id string as external_user_id.
Create errors
| HTTP | error.code | Meaning |
|---|---|---|
| 400 | ref_id_required | Missing ref_id |
| 400 | invalid_ref_id | Not a UUID |
| 409 | customer_ref_id_conflict | ref_id already used for this merchant |
| 400 | no_enabled_chains | No enabled chains |
| 429 | rate_limited | Create rate limit (check Retry-After) |
| 429 | customer_quota_exceeded | Merchant customer quota exceeded |
| 503 / 502 | circle_not_configured / circle_error | Wallet rail unavailable |
3. Look up and update
All require Secret + Request-Id.
| Method | Path | Notes |
|---|---|---|
GET | /v1/customers/{id} | Detail + existing wallets (does not auto-open new chains) |
GET | /v1/customers?ref_id={uuid} | Lookup when you only stored ref_id |
GET | /v1/customers?page=1&per_page=20 | List; per_page max 100; list items usually omit full wallets |
PATCH | /v1/customers/{id} | Update name / email / description only — not ref_id |
List data shape:
{
"items": [{ "id": "…", "ref_id": "…", "name": "…", "email": "…" }],
"meta": { "page": 1, "per_page": 20, "total": 42 }
}
4. Ensure / list wallets later
When the platform enables a new chain, or you need to refresh addresses:
| Method | Path | Body / notes |
|---|---|---|
POST | /v1/wallets/ensure | { "external_user_id": "<ref_id>", "blockchains": ["ETH"] } — blockchains optional |
GET | /v1/wallets/{external_user_id} | List existing long-lived addresses (external_user_id = ref_id) |
5. When the user deposits
User transfers to wallets[].address
→ Doggie Pay matches the address
→ credits your merchant pool
→ POST your webhook: type = deposit.credited
→ you credit local balance for data.external_user_id (= ref_id)
| Practice | Detail |
|---|---|
| Source of truth | Treat deposit.credited as credited — not only a block explorer |
| Idempotency | Deliveries may retry; key off event id or business keys |
| Circle webhooks | You do not receive Circle webhooks directly |
Typical deposit.credited data fields: deposit_id, amount, currency, external_user_id, blockchain, tx_hash, circle_tx_id, …
Signature and envelope: see Webhooks.
6. Recommended habits
- Use a stable UUID as
ref_id(maintain a mapping table if your internal id is not a UUID). - On timeout, retry create with the same Request-Id and same JSON body.
- Never put Secret keys in the client.
- Next: Withdraw for a user.
