API Documentation
Corporate card issuing API for managing users, cardholders, and virtual cards.
Workflow pages
Use the workflow pages for step-by-step operational guidance.
- Encrypted PayloadCode examples for payload encryption in Python, Go, JavaScript, and TypeScript.
- Master Wallet TopupGet the TRC20 deposit address, transfer USDT, and confirm the master balance.
- Cardholder CreationCreate users, choose a card product, and submit KYC data for cardholders.
- Card IssueCreate a card order, track status, and fetch issued card data.
- Card TopupTop up a card, track the topup order, and verify completion.
- Card Status ChangeFreeze or unfreeze cards and confirm provider status changes.
Transport, auth, and encryption
All v1 endpoints live under /api/v1 and require the API-KEY header. Every request body is an encrypted envelope that wraps the decrypted JSON payload.
Encrypted envelope
The only field in the HTTP body is encrypted. The value is the base64 ciphertext of your decrypted JSON.
| Field | Type | Required | Description |
|---|---|---|---|
encrypted | string | Required | Base64 XSalsa20-Poly1305 ciphertext containing the decrypted JSON payload. |
{
"encrypted": "<base64-secretbox-ciphertext>"
}Decrypted payload
Inside the encrypted payload, you must include request_timestamp in microseconds. The server enforces a 30 second window and rejects duplicates. All endpoint-specific fields are placed at the root level alongside request_timestamp — do not wrap them in a data object.
| Field | Type | Required | Description |
|---|---|---|---|
request_timestamp | integer | Required | Microseconds since epoch (string or integer). Must be within 30 seconds of server time. |
...endpoint fields | any | Optional | Endpoint-specific fields placed at the root level. See each endpoint for the exact fields. |
{
"request_timestamp": 1700000000000000,
"user_id": "ext-123"
}Required headers
API-KEYfor authentication.Content-Type: application/jsonfor all requests.Idempotency-Keyfor card issue and topup requests.
API-KEY: your-api-key
Content-Type: application/json
Idempotency-Key: unique-request-idError format
API returns semantically correct HTTP status codes: 200 for success, 4xx for client errors, and 5xx for server errors. Error details are returned inside the encrypted payload using a standard error object.
Standard error object
| Field | Type | Required | Description |
|---|---|---|---|
success | boolean | Required | Always false for errors. |
error | string | Required | Human-readable error message. |
error_slug | string | Required | Stable machine-readable error code. |
{
"success": false,
"error": "Invalid request",
"error_slug": "invalid_request"
}Unencrypted auth and encryption errors
If authentication or encryption fails, the response is not encrypted and does not follow the standard error schema.
| Error | HTTP | When |
|---|---|---|
API key not found | 404 | Unknown API-KEY value. |
You do not have access to this API. | 403 | API-KEY is valid but lacks access. |
Encrypted payload not found | 400 | Missing encrypted envelope in body. |
Invalid encrypted payload format | 400 | Malformed encrypted field or payload. |
Request timestamp not found in encrypted payload | 400 | Missing request_timestamp inside decrypted payload. |
Invalid timestamp format | 400 | request_timestamp is not a valid integer. |
Request timestamp outside allowed window | 400 | Timestamp is outside the 30 second window. |
Duplicate request detected | 400 | Same api_key + url + request_timestamp reused. |
Common error slugs
Error responses now return semantically correct HTTP status codes. Use the error_slug field for programmatic error handling.
| Error Slug | HTTP | Description |
|---|---|---|
invalid_request | 400 | Request payload failed validation. |
invalid_amount | 400 | Amount is invalid or negative. |
idempotency_key_required | 400 | Missing Idempotency-Key header for mutation. |
insufficient_funds | 403 | Master balance is too low for the operation. |
user_not_found | 404 | User with the given external_user_id does not exist. |
card_not_found | 404 | Card with the given card_id does not exist. |
cardholder_not_found | 404 | Cardholder not found for external_user_id. |
order_not_found | 404 | Order with the given order_id does not exist. |
external_user_id_exists | 409 | A user with this external_user_id already exists. |
cardholder_already_exists | 409 | A cardholder already exists for this user. |
card_not_active | 422 | Card is not in active status for this operation. |
card_user_mismatch | 422 | Card does not belong to the specified user. |
internal_error | 500 | Unhandled server error. |
provider_error | 502 | External card provider returned an error. |
topup_attempts_exceeded | 503 | Maximum retry attempts exceeded. Try again later. |
Idempotency and replay protection
The platform enforces two layers of protection: replay protection for all endpoints, and business idempotency for card issue and topup.
- Replay protection deduplicates
api_key + url + request_timestampfor 30 seconds. - For
/cards/createand/cards/topup, theIdempotency-Keyheader is required. - Retried requests with the same idempotency key return the original order without double charging.
Status models
Orders and cards move through predictable states. Use these transitions when you design polling and retries.
View Status Models