CARDAXO API

The CARDAXO API enables you to programmatically manage users, wallet addresses, balances, transactions, and withdrawals. All API requests require authentication via HMAC-SHA256 signatures.

Base URL

https://cardaxowallets.com/api/v1

Authentication

All API requests must include three authentication headers:

Header Description
X-Api-Key Your API key (starts with ck_)
X-Timestamp Unix timestamp (seconds). Must be within 5 minutes of server time.
X-Signature HMAC-SHA256 signature of the request

Generating the Signature

The signature is computed by concatenating the Unix timestamp and the raw request body with a period, then computing HMAC-SHA256 using your API secret:

Signature Payload Format:

{timestamp}.{request_body}

For GET requests with no body, use empty string: 1733659200.

// PHP Example $timestamp = time(); $body = json_encode(['external_id' => 'user123']); $payload = $timestamp . '.' . $body; $signature = hash_hmac('sha256', $payload, $apiSecret); // JavaScript/Node.js Example const crypto = require('crypto'); const timestamp = Math.floor(Date.now() / 1000); const body = JSON.stringify({ external_id: 'user123' }); const payload = `${timestamp}.${body}`; const signature = crypto.createHmac('sha256', apiSecret).update(payload).digest('hex'); // Python Example import hmac, hashlib, time, json timestamp = str(int(time.time())) body = json.dumps({"external_id": "user123"}) payload = f"{timestamp}.{body}" signature = hmac.new(api_secret.encode(), payload.encode(), hashlib.sha256).hexdigest()

Rate Limiting

API requests are rate-limited per API client. The default limit is 1000 requests per minute.

Response Header Description
X-RateLimit-Limit Maximum requests per minute
X-RateLimit-Remaining Remaining requests in current window
Retry-After Seconds until rate limit resets (only when exceeded)

API Endpoints

POST /user/register

Register a new user in the CARDAXO system. If the user already exists (by external_id), returns the existing user.

Permission: user.register

Request Body

{ "external_id": "your_unique_user_id", // Required: Your system's user ID "email": "[email protected]", // Optional "name": "John Doe" // Optional }

Success Response (201)

{ "success": true, "message": "User registered successfully", "data": { "user_id": 123, "external_id": "your_unique_user_id", "created_at": "2024-01-15T10:30:00+00:00" } }
POST /address/enable

Enable a wallet address for a user on a specific blockchain. Creates a new wallet if one doesn't exist.

Permission: address.enable

Request Body

{ "user_id": 123, // Required: CARDAXO user ID "chain": "BNB" // Required: Chain symbol (BNB, ETH, etc.) }

Success Response (201)

{ "success": true, "message": "Address enabled successfully", "data": { "address": "0x1234567890abcdef1234567890abcdef12345678", "chain": "BNB", "chain_id": 56, "created_at": "2024-01-15T10:30:00+00:00" } }
GET /user/{id}/addresses

Get all wallet addresses and balances for a user.

Permission: address.read

Success Response (200)

{ "success": true, "data": { "user_id": 123, "external_id": "your_unique_user_id", "addresses": [ { "address": "0x1234567890abcdef1234567890abcdef12345678", "chain": "BNB", "chain_id": 56, "balances": [ {"symbol": "BNB", "balance": "1.5", "type": "native"}, {"symbol": "USDT", "balance": "100.00", "type": "token", "contract": "0x55d398..."} ], "created_at": "2024-01-15T10:30:00+00:00" } ] } }
GET /user/{id}/balance

Get detailed balance information for a user across all chains or a specific chain.

Permission: balance.read

Query Parameters

chainOptional - Filter by chain symbol (BNB, ETH, etc.)

Success Response (200)

{ "success": true, "data": { "user_id": 123, "external_id": "your_unique_user_id", "balances": [ { "address": "0x1234567890abcdef1234567890abcdef12345678", "chain": "BNB", "chain_id": 56, "assets": [ { "symbol": "BNB", "balance": "1.5", "balance_wei": "1500000000000000000", "type": "native", "decimals": 18 }, { "symbol": "USDT", "balance": "100.00", "balance_wei": "100000000000000000000", "type": "token", "decimals": 18, "contract": "0x55d398..." } ] } ] } }
GET /user/{id}/transactions

Get transaction history for a user with optional filters.

Permission: transaction.read

Query Parameters

chainOptional - Filter by chain symbol
typeOptional - Filter by type: receive, send
statusOptional - Filter by status: pending, confirmed, failed
from_dateOptional - Start date (YYYY-MM-DD)
to_dateOptional - End date (YYYY-MM-DD)
pageOptional - Page number (default: 1)
per_pageOptional - Items per page (default: 20, max: 100)

Success Response (200)

{ "success": true, "data": { "user_id": 123, "external_id": "your_unique_user_id", "transactions": [ { "tx_hash": "0xabc123...", "type": "receive", "status": "confirmed", "from_address": "0xsender...", "to_address": "0xyouraddress...", "amount": "1.5", "symbol": "BNB", "chain": "BNB", "chain_id": 56, "confirmations": 12, "explorer_url": "https://bscscan.com/tx/0xabc123...", "confirmed_at": "2024-01-15T10:35:00+00:00", "created_at": "2024-01-15T10:30:00+00:00" } ], "pagination": { "current_page": 1, "per_page": 20, "total": 45, "last_page": 3 } } }
POST /wallet/debit

Debit funds from a user's wallet for card recharges or internal transactions. This is an internal operation that does not involve blockchain transactions.

Permission: wallet.debit

Request Body

{ "user_id": 123, // Required: CARDAXO user ID "chain": "BNB", // Required: Chain symbol "symbol": "USDT", // Required: Token/coin symbol "amount": "50.00", // Required: Amount to debit "reference": "CARD-RECHARGE-001", // Required: Your unique reference "description": "Card top-up", // Optional: Description "callback_url": "https://..." // Optional: Webhook URL }

Success Response (201)

{ "success": true, "message": "Debit processed successfully", "data": { "transaction_id": "internal_abc123...", "user_id": 123, "external_id": "your_unique_user_id", "amount": "50.00", "symbol": "USDT", "chain": "BNB", "reference": "CARD-RECHARGE-001", "new_balance": "50.00", "created_at": "2024-01-15T10:30:00+00:00" } }
POST /wallet/credit

Credit funds to a user's wallet. This is an internal operation for refunds or rewards.

Permission: wallet.credit

Request Body

{ "user_id": 123, // Required: CARDAXO user ID "chain": "BNB", // Required: Chain symbol "symbol": "USDT", // Required: Token/coin symbol "amount": "10.00", // Required: Amount to credit "reference": "REFUND-001", // Required: Your unique reference "description": "Refund for failed tx" // Optional }

Success Response (201)

{ "success": true, "message": "Credit processed successfully", "data": { "transaction_id": "internal_xyz789...", "user_id": 123, "external_id": "your_unique_user_id", "amount": "10.00", "symbol": "USDT", "chain": "BNB", "reference": "REFUND-001", "new_balance": "60.00", "created_at": "2024-01-15T10:30:00+00:00" } }
POST /withdrawal/request

Request a withdrawal to an external address. Withdrawals require admin approval.

Permission: withdrawal.request

Request Body

{ "user_id": 123, // Required: CARDAXO user ID "chain": "BNB", // Required: Chain symbol "symbol": "BNB", // Required: Token/coin symbol "to_address": "0xrecipient...", // Required: Destination address "amount": "1.0", // Required: Amount to withdraw "callback_url": "https://..." // Optional: Status update webhook }

Success Response (201)

{ "success": true, "message": "Withdrawal request submitted successfully", "data": { "withdrawal_id": 456, "user_id": 123, "to_address": "0xrecipient...", "gross_amount": "1.0", "fee": "0.001", "fee_type": "fixed", "net_amount": "0.999", "symbol": "BNB", "chain": "BNB", "status": "pending", "created_at": "2024-01-15T10:30:00+00:00" } }
POST /withdrawal/fee-preview

Preview withdrawal fees before submitting a request.

Permission: withdrawal.read

Request Body

{ "chain": "BNB", // Required: Chain symbol "symbol": "BNB", // Required: Token/coin symbol "amount": "1.0" // Required: Amount to withdraw }

Success Response (200)

{ "success": true, "data": { "gross_amount": "1.0", "fee": "0.001", "fee_type": "fixed", "net_amount": "0.999", "symbol": "BNB", "chain": "BNB", "valid": true, "message": "Amount is valid for withdrawal" } }
GET /withdrawal/{id}

Get the status of a withdrawal request.

Permission: withdrawal.read

Success Response (200)

{ "success": true, "data": { "withdrawal_id": 456, "user_id": 123, "external_id": "your_unique_user_id", "to_address": "0xrecipient...", "gross_amount": "1.0", "fee": "0.001", "fee_type": "fixed", "net_amount": "0.999", "symbol": "BNB", "chain": "BNB", "status": "completed", "tx_hash": "0xabc123...", "rejection_reason": null, "processed_at": "2024-01-15T11:00:00+00:00", "created_at": "2024-01-15T10:30:00+00:00" } }
GET /chains

Get list of supported blockchains.

Permission: chains.read

Success Response (200)

{ "success": true, "data": { "chains": [ { "symbol": "BNB", "name": "BSC Chain", "chain_id": 56, "is_testnet": false, "decimals": 18, "explorer_url": "https://bscscan.com" } ] } }

Webhook Management

Manage webhook configurations for receiving event notifications.

GET /webhooks

List all configured webhooks.

Permission: webhook.read

Success Response (200)

{ "success": true, "data": { "webhooks": [ { "id": 1, "webhook_url": "https://your-server.com/webhooks", "event_types": ["*"], "is_active": true, "retry_count": 3, "timeout_seconds": 30, "last_triggered_at": "2024-01-15T10:30:00+00:00", "last_success_at": "2024-01-15T10:30:00+00:00", "created_at": "2024-01-01T00:00:00+00:00" } ], "available_events": [ "deposit.pending", "deposit.confirmed", "withdrawal.pending", "withdrawal.approved", "withdrawal.rejected", "withdrawal.completed", "withdrawal.failed" ] } }
POST /webhooks

Create a new webhook configuration.

Permission: webhook.write

Request Body

{ "webhook_url": "https://your-server.com/webhooks", // Required "event_types": ["deposit.confirmed", "withdrawal.completed"], // Optional, defaults to ["*"] "retry_count": 3, // Optional, 0-10, default: 3 "timeout_seconds": 30 // Optional, 5-60, default: 30 }

Success Response (201)

{ "success": true, "message": "Webhook created successfully", "data": { "id": 1, "webhook_url": "https://your-server.com/webhooks", "webhook_secret": "whsec_abc123...", // Save this! Only shown once. "event_types": ["deposit.confirmed", "withdrawal.completed"], "is_active": true, "retry_count": 3, "timeout_seconds": 30, "created_at": "2024-01-15T10:30:00+00:00" } }
GET /webhooks/{id}

Get a single webhook configuration by ID.

Permission: webhook.read

Success Response (200)

{ "success": true, "data": { "id": 1, "webhook_url": "https://your-server.com/webhooks", "event_types": ["deposit.confirmed", "withdrawal.completed"], "is_active": true, "retry_count": 3, "timeout_seconds": 30, "last_triggered_at": "2024-01-15T10:30:00+00:00", "last_success_at": "2024-01-15T10:30:00+00:00", "created_at": "2024-01-01T00:00:00+00:00" } }
PUT /webhooks/{id}

Update webhook configuration. All fields are optional — only provided fields are updated.

Permission: webhook.write

Request Body

{ "webhook_url": "https://new-url.com/webhooks", // Optional "event_types": ["*"], // Optional "is_active": false, // Optional "retry_count": 5, // Optional, 0-10 "timeout_seconds": 45 // Optional, 5-60 }
DELETE /webhooks/{id}

Permanently delete a webhook configuration.

Permission: webhook.write

Success Response (200)

{ "success": true, "message": "Webhook deleted successfully" }
POST /webhooks/{id}/rotate-secret

Rotate the webhook secret. The new secret is returned once — update your server immediately. Old deliveries signed with the previous secret will no longer verify.

Permission: webhook.write

Success Response (200)

{ "success": true, "message": "Webhook secret rotated successfully", "data": { "id": 1, "webhook_secret": "whsec_new_secret..." } }

Webhook Events

When events occur, we send POST requests to your configured webhook URLs. Verify the signature using the webhook secret.

Verifying Webhook Signatures

// The webhook includes these headers: // X-Webhook-Signature: HMAC-SHA256 signature of the body // X-Webhook-Timestamp: Unix timestamp // PHP verification: $payload = $request->getContent(); $signature = $request->header('X-Webhook-Signature'); $expectedSignature = hash_hmac('sha256', $payload, $webhookSecret); if (!hash_equals($expectedSignature, $signature)) { return response('Invalid signature', 401); }

Event Types

deposit.pending

Sent when a deposit is detected but not yet confirmed.

{ "event": "deposit.pending", "timestamp": "2024-01-15T10:30:00+00:00", "data": { "user_id": 123, "external_id": "your_unique_user_id", "tx_hash": "0xabc123...", "from_address": "0xsender...", "to_address": "0xrecipient...", "amount": "1.5", "symbol": "BNB", "chain": "BNB", "chain_id": 56, "confirmations": 0 } }

deposit.confirmed

Sent when a deposit has been confirmed on the blockchain.

{ "event": "deposit.confirmed", "timestamp": "2024-01-15T10:35:00+00:00", "data": { "user_id": 123, "external_id": "your_unique_user_id", "tx_hash": "0xabc123...", "from_address": "0xsender...", "to_address": "0xrecipient...", "amount": "1.5", "symbol": "BNB", "chain": "BNB", "chain_id": 56, "confirmations": 12, "confirmed_at": "2024-01-15T10:35:00+00:00" } }

withdrawal.pending

Sent when a withdrawal request is created and awaiting admin approval.

{ "event": "withdrawal.pending", "timestamp": "2024-01-15T10:30:00+00:00", "data": { "withdrawal_id": 456, "user_id": 123, "external_id": "your_unique_user_id", "to_address": "0xrecipient...", "gross_amount": "1.0", "fee": "0.001", "net_amount": "0.999", "symbol": "BNB", "chain": "BNB", "status": "pending" } }

withdrawal.approved

Sent when a withdrawal request has been approved and is queued for processing.

{ "event": "withdrawal.approved", "timestamp": "2024-01-15T10:45:00+00:00", "data": { "withdrawal_id": 456, "user_id": 123, "external_id": "your_unique_user_id", "to_address": "0xrecipient...", "gross_amount": "1.0", "fee": "0.001", "net_amount": "0.999", "symbol": "BNB", "chain": "BNB", "status": "approved" } }

withdrawal.completed

Sent when a withdrawal has been processed and confirmed on the blockchain.

{ "event": "withdrawal.completed", "timestamp": "2024-01-15T11:00:00+00:00", "data": { "withdrawal_id": 456, "user_id": 123, "external_id": "your_unique_user_id", "to_address": "0xrecipient...", "gross_amount": "1.0", "fee": "0.001", "net_amount": "0.999", "symbol": "BNB", "chain": "BNB", "status": "completed", "tx_hash": "0xdef456..." } }

withdrawal.rejected

Sent when a withdrawal request is rejected. The rejection_reason field contains the reason provided.

{ "event": "withdrawal.rejected", "timestamp": "2024-01-15T11:00:00+00:00", "data": { "withdrawal_id": 456, "user_id": 123, "external_id": "your_unique_user_id", "to_address": "0xrecipient...", "gross_amount": "1.0", "symbol": "BNB", "chain": "BNB", "status": "rejected", "rejection_reason": "Suspicious activity detected" } }

withdrawal.failed

Sent when a withdrawal was attempted on-chain but the transaction failed (e.g. insufficient gas, reverted transaction). The withdrawal may be retried after investigation.

{ "event": "withdrawal.failed", "timestamp": "2024-01-15T11:05:00+00:00", "data": { "withdrawal_id": 456, "user_id": 123, "external_id": "your_unique_user_id", "to_address": "0xrecipient...", "gross_amount": "1.0", "fee": "0.001", "net_amount": "0.999", "symbol": "BNB", "chain": "BNB", "status": "failed", "tx_hash": null, "failure_reason": "Insufficient gas fee balance" } }

Error Handling

All errors return a consistent JSON structure:

{ "success": false, "error": "error_code", "message": "Human-readable error message", "errors": { ... } // Only for validation errors }

Error Codes

HTTP Status Error Code Description
400chain_not_supportedThe specified chain is not available
400token_not_supportedThe token is not supported on this chain
400wallet_not_foundUser doesn't have a wallet for this chain
400insufficient_balanceNot enough funds for the operation
400duplicate_webhookWebhook URL already exists
401missing_headersRequired auth headers are missing
401timestamp_expiredRequest timestamp is too old
401invalid_api_keyAPI key is invalid or inactive
401invalid_signatureHMAC signature verification failed
403ip_not_allowedIP address not in whitelist
403permission_deniedAPI key lacks required permission
404user_not_foundUser not found or wrong API client
404withdrawal_not_foundWithdrawal request not found
404webhook_not_foundWebhook configuration not found
422validation_errorRequest validation failed
429rate_limit_exceededToo many requests
500server_errorInternal server error

Push Notifications

Register and unregister user devices to receive push notifications for deposit and withdrawal events via Firebase Cloud Messaging (FCM).

POST /device/register

Register a device FCM token for a user. If the token is already registered, the record is updated. Call this on every app launch to keep tokens fresh.

Permission: device.register

Request Body

{ "user_id": 123, // Required: CARDAXO user ID "token": "fcm_device_token_here", // Required: FCM device token (max 500 chars) "platform": "android", // Required: "ios", "android", or "web" "device_name": "Pixel 8", // Optional "device_model": "Pixel 8 Pro", // Optional "app_version": "2.1.0" // Optional }

Success Response (200)

{ "success": true, "message": "Device registered successfully", "data": { "device_id": 42, "platform": "android", "registered_at": "2024-01-15T10:30:00+00:00" } }
POST /device/unregister

Remove a device FCM token. Call this on logout so the user stops receiving notifications on that device.

Permission: device.register

Request Body

{ "token": "fcm_device_token_here" // Required: FCM device token to remove }

Success Response (200)

{ "success": true, "message": "Device unregistered successfully" }

Error Response (404)

{ "success": false, "error": "token_not_found", "message": "Device token not found." }

Market Prices API

A self-hosted price feed powered by CoinGecko data, refreshed every minute. This API is separate from the main v1 API — it uses a different base path and a different authentication header.

Base path: /api/market
Authentication: Optional. If a market API key is configured on the server, include the header X-Market-Key: <key> on every request. Without it you will receive a 401. If no key is configured, requests are open.
No HMAC signing required — this API does not use the v1 signature scheme.
Response contract: Every response — including errors — always contains a data key. On success it holds the result; on error it is [] (list endpoints) or null (single-item endpoints). Always check success before using data.
GET /api/market/prices

Returns a paginated list of cryptocurrency prices sorted by market cap rank. Data is cached for 30 seconds.

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger100Results per page (max 500)
symbolstringFilter by exact symbol (e.g. btc)
searchstringSearch by name, symbol, or coin ID

Success Response 200

{
  "success": true,
  "data": [
    {
      "coin_id": "bitcoin",
      "symbol": "btc",
      "name": "Bitcoin",
      "price_usd": 83241.50,
      "market_cap_usd": 1640000000000,
      "market_cap_rank": 1,
      "price_change_24h": 1.25,
      "volume_24h": 28000000000
    }
  ],
  "meta": {
    "total": 545,
    "per_page": 100,
    "current_page": 1,
    "last_page": 6
  },
  "last_synced": "2026-04-11T07:07:08+00:00",
  "attribution": "Price data by CoinGecko (https://www.coingecko.com)"
}

Error Response 401 / 503

{
  "success": false,
  "error": "Invalid or missing API key. Pass X-Market-Key header.",
  "data": [],
  "meta": { "total": 0, "per_page": 100, "current_page": 1, "last_page": 1 }
}
GET /api/market/prices/{symbol}

Returns current price data for a single coin by symbol or CoinGecko ID (e.g. btc, bitcoin, usdt). Case-insensitive.

Success Response 200

{
  "success": true,
  "data": {
    "coin_id": "tether",
    "symbol": "usdt",
    "name": "Tether",
    "price_usd": 1.00,
    "market_cap_rank": 3
  },
  "last_synced": "2026-04-11T07:07:08+00:00",
  "attribution": "Price data by CoinGecko (https://www.coingecko.com)"
}

Not Found 404

{
  "success": false,
  "error": "Coin 'xyz' not found in market data.",
  "data": null
}
GET /api/market/ticker

Lightweight price ticker — returns only id, symbol, and price_usd for fast lookups. Ideal for populating a price table or converting balances. Optional query param: ?limit=500 (max 500, default 500).

Success Response 200

{
  "success": true,
  "count": 500,
  "data": [
    { "id": "bitcoin",  "symbol": "BTC",  "price_usd": 83241.50 },
    { "id": "ethereum", "symbol": "ETH",  "price_usd": 1612.30  },
    { "id": "tether",   "symbol": "USDT", "price_usd": 1.00     }
  ],
  "last_synced": "2026-04-11T07:07:08+00:00",
  "attribution": "Price data by CoinGecko (https://www.coingecko.com)"
}

Permissions

API clients can be granted specific permissions. Use * for full access.

Permission Description
user.registerRegister new users
address.enableEnable wallet addresses
address.readRead user addresses
balance.readRead user balances
transaction.readRead transaction history
wallet.debitDebit funds from wallets
wallet.creditCredit funds to wallets
withdrawal.requestCreate withdrawal requests
withdrawal.readRead withdrawal status
chains.readList supported chains
webhook.readList webhooks
webhook.writeCreate/update/delete webhooks
device.registerRegister push notification devices