Open Platform API Reference
Complete API reference for Vuken's open platform. Integrate wallet management, collection tasks, and webhook notifications into your application.
Version: M1 Last updated: 2026-05-25
1 Overview
1.1 Base URL
https://vuken.io/api
1.2 Authentication
All open platform endpoints require a Developer API Token in the HTTP header:
Authorization: Bearer <developer-api-token>
How to obtain a Developer API Token:
- Register an account and verify your email
- Subscribe to the developer plan via
POST /subscriptions/activate(requires Session Token) - Create a Developer API Token via
POST /api-tokens(requires Session Token) - Use the returned token for all subsequent open platform requests
Developer API Tokens have
['api:access']abilities and can only access the endpoints documented here. Account management operations (profile, subscriptions, webhooks, transfers) require a Session Token. Tokens do not expire by default and must be manually revoked viaDELETE /api-tokens/{id}.
Example:
curl -X GET https://vuken.io/api/wallets \
-H "Authorization: Bearer 1|your-developer-api-token-here"
1.3 Response Format
All responses are JSON with a uniform envelope:
Success
{
"code": "OK",
"message": "success",
"data": { ... }
}
Failure
{
"code": "BIZ-WALLET-ADDRESS-NOT-FOUND",
"message": "wallet address not found",
"data": {}
}
| Field | Description |
|---|---|
code | OK on success; semantic error code on failure |
message | Human-readable description |
data | Business payload on success; empty object on failure |
1.4 HTTP Status Codes
| Status | Meaning |
|---|---|
200 | Success (read / update) |
201 | Resource created |
401 | Unauthenticated — invalid or missing token |
403 | Authenticated but not authorized |
404 | Resource not found |
409 | Conflict (duplicate resource, lock conflict) |
422 | Validation failed or business rule violation |
429 | Rate limit exceeded (includes Retry-After header) |
502 | On-chain operation failed (node or contract error) |
1.5 Error Codes
| Error Code | Module | Description |
|---|---|---|
BIZ-NETWORK-NOT-FOUND | Wallet | Network code does not exist or is unavailable |
BIZ-ADDRESS-TYPE-NOT-ALLOWED | Wallet | Unsupported address type value |
BIZ-MAIN-ADDRESS-ALREADY-EXISTS | Wallet | Main address already exists for this network |
BIZ-MAIN-ADDRESS-REQUIRED | Wallet | Must create main address before sub-address |
BIZ-SUB-ADDRESS-SUBSCRIPTION-REQUIRED | Wallet | Active sub-address subscription required |
BIZ-SUB-ADDRESS-QUOTA-EXCEEDED | Wallet | Sub-address quota for subscription plan exceeded |
BIZ-WALLET-ADDRESS-NOT-FOUND | Wallet | Address does not exist or does not belong to current user |
BIZ-ASSET-NOT-FOUND | Wallet | Asset symbol does not exist |
BIZ-AMOUNT-INVALID | Wallet | Amount must be a positive integer string |
BIZ-COMMAND-LOCKED | Wallet | Address already has an active command in progress |
BIZ-COMMAND-NOT-FOUND | Wallet | Command number does not exist |
CHAIN-BALANCE-QUERY-FAILED | Wallet | On-chain balance query failed |
BIZ-ADDRESS-TYPE-NOT-SUB | Collection | Collection can only be executed on sub-addresses |
RISK-REVIEW-BLOCKED | Collection | Request blocked by risk control |
BIZ-COLLECTION-TASK-NOT-FOUND | Collection | Collection task does not exist |
CASHIER-TOKEN-ACTIVE-EXISTS | Cashier | An active cashier token already exists for this address |
CASHIER-TOKEN-NOT-FOUND | Cashier | Cashier token not found or does not belong to current user |
CASHIER-TOKEN-ALREADY-DISABLED | Cashier | Cashier token is already disabled |
CASHIER-TOKEN-MISSING | Cashier | No cashier Bearer token provided |
CASHIER-TOKEN-INVALID | Cashier | Cashier token is invalid, expired, or disabled |
BIZ-AMOUNT-BELOW-MINIMUM | Cashier | Balance is below the minimum collect threshold |
1.6 Rate Limits
When a rate limit is exceeded, the API returns 429 with headers:
Retry-After: 60
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
| Endpoint | Limit | Key |
|---|---|---|
POST /wallets | 20/min | User ID |
POST /wallets/{address}/balances/refresh | 20/min | User ID |
POST /collections | 10/min | User ID |
POST /cashier/tokens | 20/min | User ID |
DELETE /cashier/tokens/{tokenNo} | 20/min | User ID |
POST /cashier/collect | 10/min | Cashier Token |
Read endpoints (GET *) | 60/min | User ID / Cashier Token |
1.7 Idempotency
The following mutating endpoints support idempotency to prevent duplicate operations. Include a client-generated UUID in the header:
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
| Endpoint | Required |
|---|---|
POST /collections | Yes |
POST /cashier/collect | Yes |
If the same Idempotency-Key is submitted again within the window, the original response is returned without re-executing the operation.
2 Wallet
2.1 List Wallet Addresses
GET /wallets
Rate limit: 60/min/user
Returns all wallet addresses belonging to the current user. Supports filtering by address type and pagination.
Query Parameters (optional)
| Parameter | Type | Default | Description |
|---|---|---|---|
type | integer | — | Filter by type: 1 main address, 2 sub-address; omit for all |
page | integer | 1 | Page number (1-based) |
per_page | integer | 20 | Items per page (max 100) |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"items": [
{
"wallet_address_id": 1,
"network_code": "TRON",
"address": "TXyz123...abc",
"address_type": 1,
"address_label": "Main Wallet",
"status": 1,
"created_at": "2026-05-21 10:00:00"
},
{
"wallet_address_id": 2,
"network_code": "TRON",
"address": "TSUB456...def",
"address_type": 2,
"address_label": null,
"status": 1,
"created_at": "2026-05-21 10:05:00"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 42,
"pages": 3
}
}
}
| Field | Description |
|---|---|
wallet_address_id | Unique address ID used in other API calls |
address_type | 1 main address, 2 sub-address |
status | 1 active, 0 disabled |
2.2 Get Address Details
GET /wallets/{address}
Rate limit: 60/min/user
Path Parameters
| Parameter | Description |
|---|---|
address | On-chain wallet address string (e.g. TXyz123...abc) |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"wallet_address_id": 1,
"network_code": "TRON",
"address": "TXyz123...abc",
"address_type": 1,
"address_label": "Main Wallet",
"status": 1,
"created_at": "2026-05-21 10:00:00"
}
}
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
404 | BIZ-WALLET-ADDRESS-NOT-FOUND | Address not found or does not belong to current user |
2.3 Create Wallet Address
POST /wallets
Rate limit: 20/min/user
Creates a new on-chain wallet address. Key pairs are generated server-side; the private key is encrypted with AES-256-GCM and never returned.
Request Body
{
"network_code": "TRON",
"address_type": 1,
"address_label": "Main Wallet"
}
| Field | Type | Required | Description |
|---|---|---|---|
network_code | string | ✓ | Network code (e.g. TRON) |
address_type | integer | ✓ | 1 main address, 2 sub-address |
address_label | string | Optional label, max 120 characters |
Response 201
{
"code": "OK",
"message": "success",
"data": {
"wallet_address_id": 42,
"network_code": "TRON",
"address": "TXyz123...abc",
"address_type": 1,
"status": 1
}
}
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
409 | BIZ-MAIN-ADDRESS-ALREADY-EXISTS | Main address already exists for this network |
422 | BIZ-NETWORK-NOT-FOUND | Network code does not exist |
422 | BIZ-ADDRESS-TYPE-NOT-ALLOWED | Unsupported address type value |
422 | BIZ-MAIN-ADDRESS-REQUIRED | Main address required before creating sub-address |
422 | BIZ-SUB-ADDRESS-SUBSCRIPTION-REQUIRED | Active sub_address subscription required |
422 | BIZ-SUB-ADDRESS-QUOTA-EXCEEDED | Sub-address quota for current plan exceeded |
Main address uniqueness: Only one main address per user per network is allowed.
Sub-address prerequisite: A main address must exist for the network, and the user must have an active
sub_addresssubscription with available quota (meta.max_sub_addresses;-1means unlimited).
2.4 Get Address Balances
GET /wallets/{address}/balances
Rate limit: 60/min/user
Returns the last cached balance snapshot. Does not actively query the chain. Use Refresh Address Balances to force an on-chain update.
Path Parameters
| Parameter | Description |
|---|---|
address | On-chain wallet address string |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"address": "TXyz123...abc",
"items": [
{
"symbol": "TRX",
"balance_available": "50000000",
"balance_locked": "0",
"balance_updated_at": "2026-05-20 09:30:00"
},
{
"symbol": "USDT",
"balance_available": "1000000000",
"balance_locked": "0",
"balance_updated_at": "2026-05-20 09:30:00"
}
],
"refresh_source": 1,
"balance_updated_at": "2026-05-20 09:30:00"
}
}
| Field | Description |
|---|---|
items[].symbol | Asset symbol |
items[].balance_available | Available balance (smallest unit integer string) |
items[].balance_locked | Locked balance (command in progress) |
refresh_source | Last refresh source: 1 auto, 2 manual |
Unit: All balance values are in the smallest on-chain unit. For TRON: 1 TRX = 1,000,000 SUN; 1 USDT = 1,000,000 (6 decimal places).
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
404 | BIZ-WALLET-ADDRESS-NOT-FOUND | Address not found or does not belong to current user |
2.5 Refresh Address Balances
POST /wallets/{address}/balances/refresh
Rate limit: 20/min/user
Actively queries the on-chain node for the latest balance and updates the local cache.
Path Parameters
| Parameter | Description |
|---|---|
address | On-chain wallet address string |
Request Body (optional)
{
"force": true
}
| Field | Type | Description |
|---|---|---|
force | boolean | true to bypass cache cooldown; defaults to false |
Response 200
Same structure as Get Address Balances, with refresh_source: 2.
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
404 | BIZ-WALLET-ADDRESS-NOT-FOUND | Address not found or does not belong to current user |
502 | CHAIN-BALANCE-QUERY-FAILED | On-chain node query failed |
2.6 Get Address Chain Resources (TRON)
GET /wallets/{address}/resources
Rate limit: 60/min/user
Returns on-chain resource status for a TRON address: Energy, Bandwidth, staked TRX, and delegation details.
This endpoint is only valid for networks that support resource staking (currently TRON). Calling it for other network addresses returns
422 BIZ-CHAIN-RESOURCES-NOT-SUPPORTED.
Path Parameters
| Parameter | Description |
|---|---|
address | On-chain wallet address string (must belong to current user) |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"energy": {
"available": 12500,
"used": 25000,
"limit": 65000,
"staked_sun": "5000000",
"delegated_out_sun": "2000000",
"received_sun": "3000000"
},
"bandwidth": {
"free_available": 500,
"free_used": 100,
"free_limit": 600,
"staked_available": 300,
"staked_used": 200,
"staked_limit": 500,
"staked_sun": "1000000",
"delegated_out_sun": "0",
"received_sun": "0"
},
"withdrawable_sun": "1000000",
"delegated_to": [
{ "address": "TSub111...abc" }
],
"received_from": [
{ "address": "TMain222...xyz" }
]
}
}
Energy Fields
| Field | Description |
|---|---|
energy.available | Current available energy (limit - used) |
energy.used | Energy consumed in the current cycle |
energy.limit | Total energy cap (own stake + received delegations) |
energy.staked_sun | TRX staked to obtain energy (SUN) |
energy.delegated_out_sun | TRX equivalent of energy delegated to other addresses (SUN) |
energy.received_sun | TRX equivalent of energy received from other addresses (SUN) |
Bandwidth Fields
| Field | Description |
|---|---|
bandwidth.free_available | Remaining free daily bandwidth (TRON grants 600/day per address) |
bandwidth.free_limit | Free bandwidth cap (fixed 600/day) |
bandwidth.staked_available | Remaining staked bandwidth |
bandwidth.staked_limit | Total staked bandwidth cap |
bandwidth.staked_sun | TRX staked to obtain bandwidth (SUN) |
Other Fields
| Field | Description |
|---|---|
withdrawable_sun | TRX that has passed the unstaking wait period and is ready to withdraw (SUN) |
delegated_to | List of addresses this address has delegated resources to |
received_from | List of addresses that have delegated resources to this address |
SUN conversion: 1 TRX = 1,000,000 SUN.
staked_sun: "5000000"means 5 TRX staked.Delegation index note: The
delegated_toandreceived_fromfields require a TronGrid Pro API Key. If not configured, these fields will return empty arrays with a warning logged server-side.
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
404 | BIZ-WALLET-ADDRESS-NOT-FOUND | Address not found or does not belong to current user |
422 | BIZ-CHAIN-RESOURCES-NOT-SUPPORTED | Network does not support on-chain resource staking |
2.7 List Commands
GET /commands
Rate limit: 60/min/user
Returns all command records for the current user. Supports filtering by type and status with pagination.
Query Parameters (optional)
| Parameter | Type | Default | Description |
|---|---|---|---|
type | integer | — | 1 query, 2 withdraw, 3 collection, 4 stake, 5 unstake, 6 withdraw-unstaked, 7 vote, 8 claim-reward; omit for all |
status | integer | — | Filter by status (see Command Status Enum); omit for all |
page | integer | 1 | Page number |
per_page | integer | 20 | Items per page (max 100) |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"items": [
{
"command_no": "CMD7QKHY57AIWWXCQZ47P00",
"type": 2,
"status": 3,
"error_code": null,
"last_error": null,
"started_at": "2026-05-22 10:00:00",
"finished_at": "2026-05-22 10:00:15",
"created_at": "2026-05-22 09:59:58"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 5,
"pages": 1
}
}
}
2.8 Get Command Status
GET /commands/{commandNo}
Rate limit: 60/min/user
Path Parameters
| Parameter | Description |
|---|---|
commandNo | Command number (e.g. CMD7QKHY57AIWWXCQZ47P00) |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"command_no": "CMD7QKHY57AIWWXCQZ47P00",
"status": 3,
"type": 2,
"error_code": null,
"last_error": null,
"started_at": "2026-05-22 10:00:00",
"finished_at": "2026-05-22 10:00:15"
}
}
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
404 | BIZ-COMMAND-NOT-FOUND | Command not found or does not belong to current user |
3 Collection
Collection is the process of sweeping funds from a sub-address back to the main address.
3.1 Create Collection Task
POST /collections
Rate limit: 10/min/user Idempotency: Idempotency-Key header required
Request Header
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440001
Request Body
{
"address": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"asset_symbol": "USDT"
}
| Field | Type | Required | Description |
|---|---|---|---|
address | string | ✓ | Sub-address string (address_type=2, must belong to current user) |
asset_symbol | string | ✓ | Asset to collect (e.g. USDT) |
Response 201
{
"code": "OK",
"message": "success",
"data": {
"command_no": "CMD-20260520-0002",
"task_no": "COL-20260520-0001",
"status": 1,
"error_code": null,
"last_error": null,
"started_at": "2026-05-20 10:20:00",
"finished_at": null
}
}
| Field | Description |
|---|---|
task_no | Collection task number, used to query progress |
command_no | Associated command number |
status | Collection task status (see Collection Task Status Enum) |
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
403 | RISK-REVIEW-BLOCKED | Request blocked by risk control |
409 | BIZ-COMMAND-LOCKED | Address already has an active command in progress |
422 | BIZ-ADDRESS-TYPE-NOT-SUB | Only sub-addresses can be collected from |
422 | BIZ-WALLET-ADDRESS-NOT-FOUND | Address not found or does not belong to current user |
422 | BIZ-ASSET-NOT-FOUND | Asset symbol does not exist |
TRON Collection Flow (multi-step):
TRC20 strategy (e.g. USDT):
FUNDING_TRX— Main address transfers TRX to sub-address for gas feesACQUIRING_ENERGY— Energy acquisition decision: self-delegation or third-party; broadcasts transaction and waits for on-chain confirmationWAIT_ENERGY_READY— Polls sub-address energy until threshold is met (up to 60 s)COLLECTING— Sub-address transfers asset to main addressUNDELEGATING_ENERGY— Undelegates energy from sub-address (self-delegation path only)RECOVER_TRX— Remaining TRX swept back to main addressDONE— Collection complete, Webhook event dispatchedTRX strategy:
COLLECTING— Sub-address transfers TRX to main addressDONE— Collection complete, Webhook event dispatched
3.2 Get Collection Task Status
GET /collections/{taskNo}
Rate limit: 60/min/user
Path Parameters
| Parameter | Description |
|---|---|
taskNo | Collection task number (e.g. COL-20260520-0001) |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"command_no": "CMD-20260520-0002",
"task_no": "COL-20260520-0001",
"status": 11,
"error_code": null,
"last_error": null,
"started_at": "2026-05-20 10:20:00",
"finished_at": "2026-05-20 10:21:30"
}
}
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
404 | BIZ-COLLECTION-TASK-NOT-FOUND | Task not found or does not belong to current user |
4 Cashier
The Cashier module lets you generate time-limited payment links for sub-addresses. Payers open the link in any browser — no Vuken account required.
Two authentication contexts:
| Group | Auth | Used By |
|---|---|---|
| Token Management (§4.1) | Authorization: Bearer <developer-api-token> | Your server |
| Payment API (§4.2) | Authorization: Bearer <cashier-token> | Payer's browser |
4.1 Token Management
These endpoints use your standard Developer API Token. Call them from your server to issue and manage cashier payment links.
4.1.1 Create Cashier Token
POST /cashier/tokens
Rate limit: 20/min/user
Request Body
{
"address": "TGhCS3TbXRLqEyD2LHB8VkSEs1pHSJaq7c",
"ttl_minutes": 60
}
| Field | Type | Required | Description |
|---|---|---|---|
address | string | ✓ | Sub-address string (must belong to current user) |
ttl_minutes | integer | — | Token validity in minutes. Default 60. Allowed: 60, 120, 480, 1440 |
Response 201
{
"code": "OK",
"message": "success",
"data": {
"token_no": "CT3F788C86",
"token": "ct_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"wallet_address_id": 7,
"address": "TGhCS3TbXRLqEyD2LHB8VkSEs1pHSJaq7c",
"network_code": "TRON",
"status": "active",
"ttl_minutes": 60,
"expires_at": "2026-06-11T05:00:00+00:00",
"disabled_at": null,
"disable_reason": null,
"created_at": "2026-06-11T04:00:00+00:00"
}
}
Security:
tokenis only returned in this201response. Store it immediately — it cannot be retrieved again. If lost, disable this token and create a new one.
The payment link for the payer is:
https://vuken.io/cashier?ct=<token>
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
422 | BIZ-WALLET-ADDRESS-NOT-FOUND | Address not found or does not belong to current user |
422 | BIZ-ADDRESS-TYPE-NOT-SUB | Only sub-addresses can have cashier tokens |
409 | CASHIER-TOKEN-ACTIVE-EXISTS | An active token already exists for this address |
4.1.2 List Cashier Tokens
GET /cashier/tokens
Rate limit: 60/min/user
Query Parameters
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default 1) |
per_page | integer | Items per page (default 15, max 100) |
Response 200
{
"code": "OK",
"message": "success",
"data": {
"data": [
{
"token_no": "CT3F788C86",
"wallet_address_id": 7,
"address": "TGhCS3TbXRLqEyD2LHB8VkSEs1pHSJaq7c",
"network_code": "TRON",
"status": "active",
"ttl_minutes": 60,
"expires_at": "2026-06-11T05:00:00+00:00",
"disabled_at": null,
"disable_reason": null,
"created_at": "2026-06-11T04:00:00+00:00"
}
],
"meta": {
"current_page": 1,
"per_page": 15,
"total": 1,
"last_page": 1
}
}
}
4.1.3 Disable Cashier Token
DELETE /cashier/tokens/{tokenNo}
Rate limit: 20/min/user
Path Parameters
| Parameter | Description |
|---|---|
tokenNo | Cashier token number (e.g. CT3F788C86) |
Request Body (optional)
{
"reason": "Replaced by new token"
}
Response 200
{
"code": "OK",
"message": "success",
"data": {
"token_no": "CT3F788C86",
"status": "disabled",
"disabled_at": "2026-06-11T04:30:00+00:00",
"disable_reason": "Replaced by new token"
}
}
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
404 | CASHIER-TOKEN-NOT-FOUND | Token not found or does not belong to current user |
409 | CASHIER-TOKEN-ALREADY-DISABLED | Token is already disabled |
4.2 Payment API
These endpoints use a Cashier Token (ct_xxx) as the Bearer token. They are intended for the payer's browser session. Each request automatically extends the token's expiry.
Authorization: Bearer ct_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
4.2.1 Get Session
GET /cashier/session
Returns the current payment session including the receiving address, per-asset minimum collection amounts, and the latest collection task (if any).
Response 200
{
"code": "OK",
"message": "success",
"data": {
"token_no": "CT3F788C86",
"expires_at": "2026-06-11T05:00:00+00:00",
"min_collect_amounts": {
"USDT": "20000000",
"TRX": "1000000"
},
"address": {
"wallet_address_id": 7,
"address": "TGhCS3TbXRLqEyD2LHB8VkSEs1pHSJaq7c",
"network_code": "TRON",
"address_label": "Workspace #7"
},
"active_task": {
"task_no": "COLTEST0000000001",
"command_no": "CMD-TEST-00000001",
"status": 6,
"last_error": null,
"started_at": "2026-06-10 04:00:00",
"finished_at": null
}
}
}
| Field | Description |
|---|---|
min_collect_amounts | Per-asset minimum collect threshold map (key = asset symbol, value = smallest-unit integer string). Empty map means no minimum. |
active_task | Latest collection task object, or null if no task exists. When non-null, a collection is already in progress — do not trigger a new one. |
active_task.status | See Collection Task Status Enum |
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
401 | CASHIER-TOKEN-MISSING | No Bearer token provided |
401 | CASHIER-TOKEN-INVALID | Token is invalid, expired, or disabled |
4.2.2 Get Balance
GET /cashier/balance
Returns the current on-chain balance of the payment address (cached; use /cashier/session to trigger a refresh).
Response 200
{
"code": "OK",
"message": "success",
"data": {
"address": "TGhCS3TbXRLqEyD2LHB8VkSEs1pHSJaq7c",
"items": [
{
"symbol": "USDT",
"balance_available": "25000000",
"balance_locked": "0",
"balance_updated_at": "2026-06-11T04:10:00+00:00"
},
{
"symbol": "TRX",
"balance_available": "5000000",
"balance_locked": "0",
"balance_updated_at": "2026-06-11T04:10:00+00:00"
}
],
"balance_updated_at": "2026-06-11T04:10:00+00:00"
}
}
All amounts are in the smallest on-chain unit. See Balance Units.
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
401 | CASHIER-TOKEN-MISSING | No Bearer token provided |
401 | CASHIER-TOKEN-INVALID | Token is invalid, expired, or disabled |
4.2.3 Get Active Task
GET /cashier/task
Returns the latest collection task for this payment session, or null if no task exists.
Response 200
{
"code": "OK",
"message": "success",
"data": {
"task_no": "COLTEST0000000001",
"command_no": "CMD-TEST-00000001",
"status": 6,
"last_error": null,
"started_at": "2026-06-10 04:00:00",
"finished_at": null
}
}
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
401 | CASHIER-TOKEN-MISSING | No Bearer token provided |
401 | CASHIER-TOKEN-INVALID | Token is invalid, expired, or disabled |
4.2.4 Trigger Collection
POST /cashier/collect
Triggers a collection task to sweep the balance at the payment address to the main wallet.
Rate limit: 10/min/token Idempotency: Idempotency-Key header required
Request Header
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440099
Request Body
{
"asset_symbol": "USDT"
}
| Field | Type | Required | Description |
|---|---|---|---|
asset_symbol | string | ✓ | Asset to collect (e.g. USDT, TRX) |
Response 201
{
"code": "OK",
"message": "success",
"data": {
"task_no": "COL20260611XXXXXXXX",
"command_no": "CMD20260611XXXXXXXX",
"status": 1,
"last_error": null,
"started_at": "2026-06-11T04:15:00+00:00",
"finished_at": null
}
}
Error Responses
| HTTP | Error Code | Scenario |
|---|---|---|
401 | CASHIER-TOKEN-MISSING | No Bearer token provided |
401 | CASHIER-TOKEN-INVALID | Token is invalid, expired, or disabled |
409 | BIZ-COMMAND-LOCKED | A collection task is already in progress |
422 | BIZ-ASSET-NOT-FOUND | Asset symbol does not exist |
422 | BIZ-AMOUNT-BELOW-MINIMUM | Balance is below the minimum collect threshold |
5 Webhooks
When key events occur (collection completed, command succeeded/failed), the platform delivers an HTTP POST request to the webhook_url you configured.
5.1 Setup
Webhooks require a Session Token to configure (server-side setup only):
- Subscribe to the developer plan:
POST /subscriptions/activate - Set your webhook endpoint:
PUT /subscriptions/config
{
"service_code": "developer",
"config": {
"webhook_url": "https://your-server.com/webhooks/receive",
"webhook_secret": "your-signing-secret"
}
}
webhook_secretis used to generate theX-Webhook-Signatureheader. Store it securely — it cannot be retrieved after being set.
5.2 Event Types
| Event Type | Trigger |
|---|---|
withdrawal.completed | A withdrawal command is broadcast successfully (txid obtained) |
withdrawal.failed | A withdrawal command fails (signing / broadcast error) |
collection.completed | A collection task completes — tokens credited to the main address (DONE) |
collection.failed | A collection task fails at any step and enters a terminal state (FAILED) |
Webhooks only fire on terminal states. Intermediate steps (e.g.
FUNDING_TRX,COLLECTING) do not trigger webhooks.
5.3 Payload Format
All webhook requests are POST with Content-Type: application/json. All event fields are at the top level of the JSON body — there is no nested data wrapper. The signature (see §5.5) is computed over this raw body.
Common fields (present on every event):
| Field | Description |
|---|---|
event_id | Globally unique identifier (UUID). Fixed per event; unchanged across retries. Use for deduplication. |
event_type | Event type (see table above) |
occurred_at | Event timestamp (ISO 8601) |
withdrawal.* payload:
{
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"event_type": "withdrawal.completed",
"occurred_at": "2026-05-22T10:00:00+08:00",
"command_no": "CMD5GH3KQWXYZ12345",
"status": 3,
"txid": "2a09503378f288571f916d8d1e856eb116f69d3e6ff6dbb83fab6a186eb33d14",
"error": null,
"finished_at": "2026-05-22T10:00:15+08:00"
}
| Field | Description |
|---|---|
command_no | Withdrawal command number |
status | 3 SUCCESS, 4 FAILED |
txid | On-chain transaction hash; null on failure |
error | Failure reason; null on success |
finished_at | Terminal timestamp |
collection.* payload:
{
"event_id": "550e8400-e29b-41d4-a716-446655440002",
"event_type": "collection.completed",
"occurred_at": "2026-05-22T10:01:00+08:00",
"task_no": "COL7ABCDEFG123456X",
"status": 11,
"network_code": "TRON",
"asset_symbol": "USDT",
"address": "TSubXxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"swept_amount": 10000000,
"sweep_tx_hash": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"credited_at": "2026-05-22T10:01:00+08:00",
"error": null
}
| Field | Description |
|---|---|
task_no | Collection task number |
status | 11 DONE, 12 FAILED |
network_code | Network code (e.g. TRON) |
address | Sub-address the collection ran on |
swept_amount | Amount swept to the main address (smallest unit integer); null on failure |
sweep_tx_hash | Sweep transaction hash; null on failure |
credited_at | On-chain credit timestamp; null on failure |
error | Failure reason; null on success |
5.4 Request Headers
Every webhook delivery includes the following headers:
X-Webhook-Signature: sha256=<hex_signature>
X-Event-Id: <uuid>
X-Event-Type: collection.completed
5.5 Signature Verification
The X-Webhook-Signature header is HMAC-SHA256 computed over a canonical JSON rebuilt from the payload — not the raw request body. The platform sorts the top-level keys of the full payload using MySQL's JSON key ordering (shorter keys first, then byte order), re-encodes with unescaped Unicode and no extra whitespace, then signs:
canonical_json = json_encode(sort_keys(full_payload), UNESCAPED_UNICODE) // no spaces
signature = "sha256=" + hex( HMAC-SHA256(canonical_json, webhook_secret) )
Do not verify against the raw request body — the transmitted key order is not guaranteed. You must rebuild
canonical_jsonwith the same key-sort rule (length first, then byte order) before hashing. All current event fields are scalar top-level values, so a single top-level key sort is sufficient.
Verification examples:
// PHP
$payload = json_decode($request->getContent(), true);
uksort($payload, fn ($a, $b) => (strlen($a) <=> strlen($b)) ?: strcmp($a, $b));
$canonical = json_encode($payload, JSON_UNESCAPED_UNICODE);
$expected = 'sha256=' . hash_hmac('sha256', $canonical, $webhookSecret);
$valid = hash_equals($expected, (string) $request->header('X-Webhook-Signature'));
# Python
import hmac, hashlib, json
payload = json.loads(raw_body)
ordered = dict(sorted(payload.items(), key=lambda kv: (len(kv[0]), kv[0])))
canonical = json.dumps(ordered, ensure_ascii=False, separators=(',', ':'))
expected = 'sha256=' + hmac.new(webhook_secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()
valid = hmac.compare_digest(expected, request.headers['X-Webhook-Signature'])
// Node.js
const crypto = require('crypto');
const payload = JSON.parse(rawBody);
const ordered = {};
for (const k of Object.keys(payload).sort((a, b) => a.length - b.length || (a < b ? -1 : a > b ? 1 : 0))) {
ordered[k] = payload[k];
}
const canonical = JSON.stringify(ordered);
const expected = 'sha256=' + crypto.createHmac('sha256', webhookSecret).update(canonical).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(receivedSignature));
Always use a timing-safe comparison (
hash_equals,hmac.compare_digest,timingSafeEqual) to prevent timing attacks.
5.6 Retry Policy
If your server does not return 2xx within the timeout, the platform will retry:
| Attempt | Delay |
|---|---|
| 1st | Immediate |
| 2nd | After 1 minute |
| 3rd | After 5 minutes |
| 4th+ | Marked DEAD, retries stop |
Timeout: 15 s total per request. Your endpoint must return 2xx within this window.
After DEAD, the delivery can be viewed in GET /webhooks/deliveries (Session Token required).
5.7 Receiver Best Practices
- Verify the signature — reject any request where
X-Webhook-Signatureis missing or invalid. - Return
200immediately — process the event asynchronously to avoid timeout failures. - Deduplicate by
event_id— the same event may be delivered more than once on retry; your handler must be idempotent. - Use HTTPS — plaintext endpoints expose your payload and invalidate signature security.
Appendix
Command Status Enum
| Value | Name | Description |
|---|---|---|
1 | PENDING | Queued, waiting to execute |
2 | RUNNING | Currently executing |
3 | SUCCESS | Completed successfully |
4 | FAILED | Execution failed |
5 | REJECTED | Blocked by risk control |
6 | CANCELLED | Manually cancelled |
Command Type Enum
| Value | Name | Description |
|---|---|---|
1 | QUERY | Query command |
2 | WITHDRAWAL | Withdrawal / transfer out |
3 | COLLECTION | Sub-address collection |
4 | STAKE | Stake TRX for resources |
5 | UNSTAKE | Unstake TRX |
6 | WITHDRAW_UNSTAKED | Withdraw matured unstaked TRX |
7 | VOTE | Vote for Super Representatives |
8 | WITHDRAW_REWARD | Claim voting rewards |
Collection Task Status Enum
| Value | Name | Description |
|---|---|---|
1 | PENDING | Task created; no step has started yet |
2 | FUNDING_TRX | Transferring TRX gas fee to sub-address |
16 | ACQUIRING_ENERGY | Energy acquisition in progress — self-delegation or third-party proxy transaction broadcast, awaiting on-chain confirmation |
17 | WAIT_ENERGY_READY | Polling sub-address energy until threshold is met (up to 60 s) |
6 | COLLECTING | Transferring asset to main address |
15 | UNDELEGATING_ENERGY | Undelegating energy from sub-address (self-delegation path only) |
8 | RECOVER_TRX | Recovering remaining TRX to main address |
11 | DONE | All steps complete; webhook dispatched |
12 | FAILED | Unrecoverable error; task abandoned |
Supported Networks
network_code | Network | Native Asset |
|---|---|---|
TRON | TRON Mainnet | TRX |
Balance Units
All balance and amount values use the smallest on-chain unit as integer strings:
| Network | Asset | Smallest Unit | Example |
|---|---|---|---|
| TRON | TRX | SUN (1 TRX = 1,000,000 SUN) | "1000000" = 1 TRX |
| TRON | USDT (TRC20) | 6 decimal places | "1000000" = 1 USDT |