DcWallet Developer Docs

Integration Guide

Use the sandbox API console in this order: assets and accounts, receiving and receivables, then card issuing. OpenAPI remains the exact request and response contract.

Quick Start

  1. Open Developer Center and create an enterprise sandbox API key with the scope preset for the module you are testing. Copy the secret when it is shown; it cannot be viewed later.
  2. Open API Console, click Authorize, fill only apiKeyAuth and apiSecret, then close the dialog.
  3. Run GET /api/v1/assets, then GET /api/v1/accounts. A successful response has code: 0.
  4. Verify receiving instruments and account transactions.
  5. Create or reuse a contact, then test an invoice or payment link and its webhook.
  6. Upload the contact's identity documents, confirm card readiness, and submit a card application.
  7. Copy the working requests into your server integration.

The domain resolves the brand. The API key resolves the enterprise and sandbox/live environment. Do not send those identifiers in a machine request.

Use an sk_sandbox_ key on the sandbox domain and an sk_live_ key on the live domain. The console follows the same module and endpoint order as this guide.

Authentication

Public documentation is available on every plan. API keys, authenticated online debugging, and developer webhooks require an active Starter or higher subscription. Quotas are shared by the enterprise and separated between sandbox and live.

Every route under /api/v1/** requires these signed headers:

X-API-Key: sk_sandbox_... or sk_live_...
X-Timestamp: <unix_seconds>
X-Request-ID: <new_16_to_128_character_value>
X-Content-SHA256: <sha256_of_exact_body_bytes>
X-Signature: v1=<hmac_sha256>

Signing

  1. Serialize the final request body first, then calculate the lowercase hexadecimal SHA-256 digest of those exact bytes. Do not hash a parsed or reformatted JSON object. For a request without a body, hash zero bytes: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
  2. Set X-Timestamp to the current Unix time in seconds. Generate a new X-Request-ID containing 16 to 128 letters, digits, dots, underscores, colons, or hyphens.
  3. Build the UTF-8 canonical value below with exactly seven lines and no trailing newline. Uppercase the HTTP method. Use the request path only, followed by the raw query string exactly as transmitted, including its original encoding and parameter order; use an empty fourth line when there is no query string.
<X-Timestamp>
<UPPERCASE_METHOD>
<REQUEST_PATH>
<RAW_QUERY_OR_EMPTY>
<LOWERCASE_X-CONTENT-SHA256>
<X-Request-ID>
<X-API-Key>

Calculate HMAC-SHA256 over the canonical UTF-8 bytes using the API Secret as the UTF-8 key. Lowercase the hexadecimal result, prefix it with v1=, and send it as X-Signature. The API Secret is used only by the signing client and must never be sent as an HTTP header or stored in application logs.

Verification and replay protection

  1. The server requires all five headers, accepts only timestamps within 300 seconds of server time, and validates the request ID format.
  2. It hashes the exact body bytes received and compares the digest with X-Content-SHA256 using a constant-time comparison.
  3. It resolves an active, unexpired API key for the current brand and sandbox/live environment, then verifies the enterprise binding, source-IP allowlist, and required route scope.
  4. It reconstructs the same canonical value and verifies X-Signature with a constant-time comparison. A valid request ID is then reserved for 10 minutes; reusing it with the same API key is rejected as a replay.
  5. After authentication succeeds, per-minute rate limits and the enterprise's monthly API quota are applied.

Every HTTP retry must use a new timestamp and request ID, then recompute the content hash and signature. For a retry of the same logical write, keep the same Idempotency-Key so the operation is not created twice.

The API console follows this same algorithm in browser memory and removes X-API-Secret before transmission.

X-Environment is unnecessary. If supplied, it must match the API key.

Send Accept-Language: en-US, zh-CN, or zh-TW to localize human-readable error messages. Machine API errors default to English when the header is omitted. Numeric code values remain the stable integration contract.

1. Assets and Accounts

GET /api/v1/assets returns enabled currencies and networks. GET /api/v1/accounts returns customer-facing balances, capabilities, and status. /accounts/{id}/transactions supports filters and transaction detail with before/after balances. Internal Wallet, custody, provider, seed, and vault identifiers are deliberately hidden.

2. Receiving Accounts

Use GET /api/v1/bank-accounts and GET /api/v1/blockchain-addresses to list receiving instruments. Their IDs can be supplied when creating an invoice; custody and provider metadata remain hidden.

3. Contacts

Use the same contact IDs shown in the dashboard for invoices, payment links, and cardholders. Start with GET /api/v1/contacts to reuse an existing contact, or create one with POST /api/v1/contacts and Idempotency-Key. OpenAPI shows accepted values and meanings for contact role, entity type, identity type, and document role. Use /contacts/{contactId}/accounts to maintain the contact's bank accounts and blockchain addresses.

For card products that require identity verification, upload the passport file or identity-document images to the contact first. The platform reads those stored files and securely submits them to the issuing channel during the card application; the card-application body therefore does not contain image or document fields.

4. Receivables

Receivables are the single creation and management concept. Use /api/v1/receivables with a contact ID for business billing, or omit the contact and receiving account for a consumer stablecoin checkout with an automatically leased collection address. The returned checkout URL is the payment link; /api/v1/invoices remains a compatibility alias. Query received funds with GET /api/v1/payment-records or /payment-records/{paymentRecordId}. Creation requires Idempotency-Key.

5. Transfers

Use transfer:write with POST /api/v1/transfers or /api/v1/transfer-batches and send an Idempotency-Key. The normal signed API-key headers authorize unattended server-to-server submission; X-2FA-Code is not used on machine requests. Creating, changing, rotating, or revoking a Live API key remains protected by interactive 2FA.

Transfer business integration flow

  1. GET /api/v1/accounts and select an account whose availableBalance covers the transfer; retain its accountId.
  2. POST /api/v1/transfers with that sourceAccountId and an Idempotency-Key; retain the returned transfer id.
  3. GET /api/v1/transfers/{transferId} until the status is succeeded, failed, rejected, or cancelled.
  4. Register POST /api/v1/webhook-endpoints once with the four terminal transfer events to receive asynchronous final results.
{
  "sourceAccountId": "337893979704000514",
  "amount": 100,
  "recipientCurrency": "CNY",
  "recipientBankName": "Bank of China",
  "recipientAccountHolder": "Zhang San",
  "recipientAccountNo": "6217000012345678901"
}

This direct CNY request does not require a contact, destinationAccountId, or beneficiary identity number. The source account must be CNH. When omitted, recipientBankCountryCode defaults to CN and recipientBeneficiaryType defaults to individual.

For a business beneficiary, set recipientBeneficiaryType to business. You may also provide recipientRegistrationNumber and recipientRegistrationType; supported examples include Hong Kong brn, European business_registration or vat, UK company_number, and US ein. The registration identifier is optional unless the selected payout channel requires it.

  • Hong Kong local account: recipientBankCountryCode=HK, recipientPaymentRail=CHATS, and a bank_code.
  • Euro-area account: an ISO country code, a valid IBAN, and recipientPaymentRail=SEPA.
  • UK local account: recipientBankCountryCode=GB, recipientPaymentRail=CHAPS, and a six-digit sort_code.
  • US local account: recipientBankCountryCode=US, recipientPaymentRail=FEDWIRE, and a valid nine-digit aba routing number.
{
  "url": "https://example.com/webhooks/transfers",
  "events": [
    "transfer.succeeded",
    "transfer.failed",
    "transfer.rejected",
    "transfer.cancelled"
  ]
}

Webhook delivery is asynchronous and may be retried, so deduplicate by eventId and verify its signature. Keep status polling as the reconciliation fallback. API submissions use the same balance locking, compliance, approval, and channel-availability gates as the dashboard.

A saved destinationAccountId remains supported. A batch contains 1–500 rows and every row must use the same payment method, currency, and blockchain network; subscribe to batch_transfer.* for batch results.

6. Card Issuing

Use account:read, contact:read, contact:write, card:read, and card:write. Add card_sensitive:read for hosted card reveal and webhook:read/webhook:write for callbacks.

  1. GET /api/v1/assets and /accounts, then select a funding account.
  2. POST /api/v1/contacts with Idempotency-Key, externalReference, identity, and residential-address data.
  3. Upload raw file bytes to POST /api/v1/contacts/{contactId}/documents/identity_front and identity_back, or use passport. The platform forwards these stored files to the issuing channel when you submit the application.
  4. GET /api/v1/contacts/{contactId} and resolve every item in missingFields.
  5. GET /api/v1/card-products and choose one whose entity type, country, KYB requirement, and remaining inventory match the contact and enterprise.
  6. POST /api/v1/card-applications with an Idempotency-Key header.
  7. Query /api/v1/card-applications/{id}, /api/v1/cards/{id}, and card transactions.
  8. After the cardholder completes second-factor verification in your own application, create POST /api/v1/cards/{cardId}/reveal-sessions and open its one-use hosted URL in a top-level browser window.
  9. Use /top-ups, /freeze, /unfreeze, /cancel, or PATCH /cards/{id} for card management. In sandbox, call POST /cards/{id}/sandbox/purchases, then verify the result under /api/v1/card-transactions.
{
  "contactId": "337893979704000512",
  "productId": "337893979704000513",
  "sourceAccountId": "337893979704000514",
  "initialAmount": 100,
  "nickname": "Marketing",
  "purpose": "Online advertising"
}

Keep IDs as strings. Every card write uses an Idempotency-Key. AML must be clear and required enterprise KYB must be approved. Identity cards need front and back files; passports need only the passport file. Card top-ups also require the original sourceAccountId and return a top-up transaction. Sandbox purchase simulation accepts amount, merchantName, and merchantCategoryCode; live keys are rejected.

Hosted card reveal

Your application performs the cardholder's second-factor verification. Then send an opaque verificationSubject, the method, your audit reference, and a UTC verifiedAt no older than five minutes to POST /api/v1/cards/{cardId}/reveal-sessions. The URL expires after two minutes and works once; it shows PAN, expiry, and CVV for 60 seconds. Do not embed, log, persist, or instrument the URL. Your backend never receives raw card data.

{
  "verificationSubject": "customer-user-42",
  "verificationMethod": "passkey",
  "verificationReference": "verify_01J5Z8YQABCD",
  "verifiedAt": "2026-08-20T19:15:00Z"
}

If a channel write returns code 9003, keep the same idempotency key and query its resource until reconciliation completes. Do not create a second operation with a new key. Temporary states include reconciling, processing, freezing, unfreezing, and cancelling.

7. Webhooks

Register /api/v1/webhook-endpoints with subscriptions such as transfer.*, batch_transfer.*, card.*, invoice.*, payment.link.*, and agent.*. Save the secret returned at creation and verify v1=HMAC-SHA256(timestamp + "." + rawBody) using the brand-neutral X-Webhook-Timestamp and X-Webhook-Signature headers.

  1. Call GET /api/v1/webhook-endpoints/event-types to see the concrete events available in the sandbox test selector.
  2. Create an endpoint whose subscription matches the event, for example card.*.
  3. Call POST /api/v1/webhook-endpoints/{endpointId}/test and select an eventType. HTTP 202 returns an eventId and deliveryId.
  4. Query GET /api/v1/webhook-endpoints/{endpointId}/deliveries?deliveryId=... until the status is delivered or dead_letter.

A test event uses the production signing, asynchronous queue, retry policy, and delivery log, but is sandbox-only, contains test: true, targets only the selected endpoint, and never creates a card, transaction, invoice, payment, or balance change. Every payload includes eventVersion and createdAt; deduplicate deliveries by eventId.

Request IDs and Retries

X-Request-ID identifies one signed HTTP attempt and must change on every retry. Idempotency-Key identifies one intended write, including contact, transfer, transfer batch, card, invoice, payment-link, and Agent-task creation, and must stay the same for retries of the same body. Same key plus different body returns HTTP 409.

Troubleshooting

ResultCheck first
401The API-key prefix matches the domain, the secret belongs to this key, the key is active, and your server signs the exact path, query, and body bytes.
403The key has the required scope, the subscription includes API access, and any required KYB, AML, or business approval is complete.
409The same Idempotency-Key was not reused with a different request body, and the resource allows the requested state change.
429Read X-Quota-Remaining and X-Quota-Reset, then wait before retrying.
503The signing or quota service is temporarily unavailable. Retry later with a new X-Request-ID and the same idempotency key for the same write.

If a secret was not saved, rotate the key in Developer Center. Include the response X-Request-ID when reporting an issue.

Errors

{ "code": 0, "message": "OK", "data": {} }

HTTP statuses: 400 validation, 401 credential/signature, 403 scope/subscription/policy, 404 resource, 409 conflict, 422 business rejection, 429 rate or monthly quota, 500 internal failure, and 503 temporary quota-service failure. Responses echo X-Request-ID. Successful authenticated responses also return X-Quota-Limit, X-Quota-Remaining, and X-Quota-Reset.

8. Treasury Agent

Treasury Agent is the final optional module under /api/v1/agent and uses the same machine signing contract. Use agent:read to discover capabilities and query tasks/events; use agent:write to create or cancel tasks.

GET  /api/v1/agent/capabilities
POST /api/v1/agent/tasks
GET  /api/v1/agent/tasks/{taskId}
GET  /api/v1/agent/tasks/{taskId}/events
POST /api/v1/agent/tasks/{taskId}/cancel

Create JSON contains only capability, intent, and input; enterprise and environment come from the API key. HTTP 202 records the task but does not mean completion or funds movement. API keys cannot authorize tasks.