API Reference

The signed HTP Open Payment API. One base URL serves both environments; your API key decides SANDBOX or PRODUCTION.

Base URL

Environments

ok_test_… → SANDBOX · ok_live_… → PRODUCTION

Authentication

Sign every request with HMAC-SHA256. See the Authentication guide.

Headers: X-Key-Id, X-Timestamp, X-Nonce, X-Signature, Idempotency-Key

METHOD + "\n" +
PATH (with query) + "\n" +
X-Timestamp + "\n" +
X-Nonce + "\n" +
Idempotency-Key (or empty) + "\n" +
hex(sha256(body))

X-Signature = base64(HMAC-SHA256(api_secret, string_to_sign))

Payments

Collect USDT from an HTP user through the HTP App cashier.

Create a payment

POST/open/v1/payments

Creates a payment in status CREATED and returns the checkout_url the user opens inside the HTP App to confirm with their payment PIN.

  • out_order_no is your own order number and must be unique per app, environment and order type.
  • The payment expires after expire_minutes (default set by HTP, usually 30). An expired payment is reported as CLOSED.
  • Set payer_sub to lock the payment to one user; otherwise any eligible HTP user who opens the link can pay and user_sub is filled in when it is paid.
  • The fee is computed from your approved terms; net_amount = amount - fee_amount is what your wallet receives.

Do not mark your order as paid on the browser redirect. Wait for the payment.paid callback or query the order.

Headers

X-Key-Idstringrequired
Signature header — see Authentication.
X-Timestampstringrequired
Signature header — see Authentication.
X-Noncestringrequired
Signature header — see Authentication.
X-Signaturestringrequired
Signature header — see Authentication.
Idempotency-Keystringrequired
8–64 characters [A-Za-z0-9_-]. Repeating a request with the same key and the same body replays the first successful response; the same key with a different body returns IDEMPOTENCY_KEY_REUSED. Generate one per business operation and reuse it on retries.

Request body application/json

out_order_nostringrequired
Your order number. Unique per app, environment and order type.
amountstringrequired
USDT amount, positive, at most two decimals. Responses always use two decimals.
currencystringrequired
USDT
descriptionstring
Shown to the user in the HTP cashier.
payer_substring
Optional. The OIDC sub of the only user allowed to pay. The user must have signed in to one of your apps and not revoked it (USER_NOT_AUTHORIZED otherwise).
expire_minutesinteger
Checkout lifetime in minutes. Defaults to the platform setting (30).

Response

200 · Order

Errors

  • 400INVALID_REQUEST — a field failed validation; message says which. Also returned when Idempotency-Key is missing or malformed.
  • 401SIGNATURE_INVALID — missing headers, unknown or revoked key, wrong signature, timestamp outside the window, or a reused nonce.
  • 403APP_UNAVAILABLE, PAYMENT_NOT_ENABLED, SANDBOX_USER_ONLY
  • 409ORDER_ALREADY_EXISTS, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_IN_PROGRESS
  • 422INVALID_AMOUNT, CURRENCY_NOT_SUPPORTED, LIMIT_EXCEEDED, USER_NOT_AUTHORIZED, SELF_PAYMENT_FORBIDDEN
  • 429TOO_MANY_REQUESTS — rate limit per app (default 600 requests/minute). Honour Retry-After.
# Sign first — see /docs/authentication or /tools/signature
curl -X POST '/open/v1/payments' \
  -H 'X-Key-Id: $HTP_KEY_ID' \
  -H 'X-Timestamp: $TS' \
  -H 'X-Nonce: $NONCE' \
  -H 'X-Signature: $SIGNATURE' \
  -H 'Idempotency-Key: $IDEMPOTENCY_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"out_order_no":"A20261107-0001","amount":"40.00","currency":"USDT","description":"Premium membership, 1 month","payer_sub":"2f6c0d1e8a9b4c7d","expire_minutes":30}'
{
  "trace_id": "6c1f0b9a3e2d4f5a",
  "status_code": 200,
  "message": "success",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "A20261107-0001",
    "order_type": "PAYMENT",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "0.00",
    "fee_amount": "0.40",
    "net_amount": "39.60",
    "currency": "USDT",
    "description": "Premium membership, 1 month",
    "user_sub": "2f6c0d1e8a9b4c7d",
    "checkout_url": "https://app.htp.example/open/checkout?token=Q2hlY2tvdXQ...",
    "expires_at": "2026-11-07T09:30:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}
Sign this request

Close an unpaid payment

POST/open/v1/payments/{id}/close

Closes a payment that is still CREATED so it can no longer be paid. Closing an already CLOSED payment returns it unchanged. A PAID payment cannot be closed (INVALID_STATE); refund it instead. No request body and no Idempotency-Key are needed.

Headers

X-Key-Idstringrequired
Signature header — see Authentication.
X-Timestampstringrequired
Signature header — see Authentication.
X-Noncestringrequired
Signature header — see Authentication.
X-Signaturestringrequired
Signature header — see Authentication.

Path parameters

idstring · uuidrequired
The HTP order id (UUID) returned when the order was created.

Response

200 · Order

Errors

  • 401SIGNATURE_INVALID — missing headers, unknown or revoked key, wrong signature, timestamp outside the window, or a reused nonce.
  • 404ORDER_NOT_FOUND — no such order for this app in the key's environment.
  • 409INVALID_STATE — only unpaid payments can be closed.
  • 429TOO_MANY_REQUESTS — rate limit per app (default 600 requests/minute). Honour Retry-After.
# Sign first — see /docs/authentication or /tools/signature
curl -X POST '/open/v1/payments/0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d/close' \
  -H 'X-Key-Id: $HTP_KEY_ID' \
  -H 'X-Timestamp: $TS' \
  -H 'X-Nonce: $NONCE' \
  -H 'X-Signature: $SIGNATURE'
{
  "trace_id": "string",
  "status_code": 200,
  "message": "success",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "string",
    "order_type": "PAYMENT",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "40.00",
    "fee_amount": "40.00",
    "net_amount": "40.00",
    "currency": "USDT",
    "description": "string",
    "user_sub": "string",
    "checkout_url": "https://…",
    "expires_at": "2026-11-07T09:00:00Z",
    "completed_at": "2026-11-07T09:00:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}
Sign this request

Refunds

Return all or part of a paid payment to the payer.

Refund a payment

POST/open/v1/payments/{id}/refunds

Refunds all or part of a PAID payment. The refund completes synchronously (status SUCCEEDED): the full refund amount is debited from your developer wallet and credited to the payer. Fees are not refunded. You can refund a payment several times until refunded_amount reaches amount.

A payment.refunded callback is also sent; its data is the refund order, with parent_id set to the payment.

Headers

X-Key-Idstringrequired
Signature header — see Authentication.
X-Timestampstringrequired
Signature header — see Authentication.
X-Noncestringrequired
Signature header — see Authentication.
X-Signaturestringrequired
Signature header — see Authentication.
Idempotency-Keystringrequired
8–64 characters [A-Za-z0-9_-]. Repeating a request with the same key and the same body replays the first successful response; the same key with a different body returns IDEMPOTENCY_KEY_REUSED. Generate one per business operation and reuse it on retries.

Path parameters

idstring · uuidrequired
The HTP order id (UUID) returned when the order was created.

Request body application/json

out_refund_nostringrequired
Your refund number. Unique per app and environment.
amountstringrequired
USDT amount, positive, at most two decimals. Responses always use two decimals.
reasonstring

Response

200 · Order

Errors

  • 400INVALID_REQUEST — a field failed validation; message says which. Also returned when Idempotency-Key is missing or malformed.
  • 401SIGNATURE_INVALID — missing headers, unknown or revoked key, wrong signature, timestamp outside the window, or a reused nonce.
  • 403APP_UNAVAILABLE, DEVELOPER_WALLET_UNAVAILABLE
  • 404ORDER_NOT_FOUND — no such order for this app in the key's environment.
  • 409REFUND_NOT_ALLOWED (payment not PAID), ORDER_ALREADY_EXISTS, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_IN_PROGRESS
  • 422INVALID_AMOUNT, REFUND_AMOUNT_EXCEEDED, INSUFFICIENT_BALANCE
  • 429TOO_MANY_REQUESTS — rate limit per app (default 600 requests/minute). Honour Retry-After.
# Sign first — see /docs/authentication or /tools/signature
curl -X POST '/open/v1/payments/0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d/refunds' \
  -H 'X-Key-Id: $HTP_KEY_ID' \
  -H 'X-Timestamp: $TS' \
  -H 'X-Nonce: $NONCE' \
  -H 'X-Signature: $SIGNATURE' \
  -H 'Idempotency-Key: $IDEMPOTENCY_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"out_refund_no":"R20261107-0001","amount":"15.00","reason":"Customer cancelled"}'
{
  "trace_id": "7d2a1c0b9e8f4a3b",
  "status_code": 200,
  "message": "success",
  "data": {
    "id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d",
    "out_order_no": "R20261107-0001",
    "order_type": "REFUND",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "SUCCEEDED",
    "amount": "15.00",
    "refunded_amount": "0.00",
    "fee_amount": "0.00",
    "net_amount": "15.00",
    "currency": "USDT",
    "description": "Customer cancelled",
    "user_sub": "2f6c0d1e8a9b4c7d",
    "completed_at": "2026-11-07T10:00:00Z",
    "created_at": "2026-11-07T10:00:00Z"
  }
}
Sign this request

Payouts

Send USDT from your developer wallet to an HTP user.

Pay an HTP user

POST/open/v1/payouts

Pays the user identified by payee_sub from your developer wallet. Completes synchronously (status SUCCEEDED) and sends a payout.completed callback. Only available when payouts were approved for your app (PAYOUT_NOT_ALLOWED otherwise). Payout limits are counted separately from payments.

Headers

X-Key-Idstringrequired
Signature header — see Authentication.
X-Timestampstringrequired
Signature header — see Authentication.
X-Noncestringrequired
Signature header — see Authentication.
X-Signaturestringrequired
Signature header — see Authentication.
Idempotency-Keystringrequired
8–64 characters [A-Za-z0-9_-]. Repeating a request with the same key and the same body replays the first successful response; the same key with a different body returns IDEMPOTENCY_KEY_REUSED. Generate one per business operation and reuse it on retries.

Request body application/json

out_order_nostringrequired
amountstringrequired
USDT amount, positive, at most two decimals. Responses always use two decimals.
currencystringrequired
USDT
payee_substringrequired
OIDC sub of the user to pay.
descriptionstring

Response

200 · Order

Errors

  • 400INVALID_REQUEST — a field failed validation; message says which. Also returned when Idempotency-Key is missing or malformed.
  • 401SIGNATURE_INVALID — missing headers, unknown or revoked key, wrong signature, timestamp outside the window, or a reused nonce.
  • 403APP_UNAVAILABLE, PAYMENT_NOT_ENABLED, PAYOUT_NOT_ALLOWED, SANDBOX_USER_ONLY, DEVELOPER_WALLET_UNAVAILABLE
  • 409ORDER_ALREADY_EXISTS, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_IN_PROGRESS
  • 422INVALID_AMOUNT, CURRENCY_NOT_SUPPORTED, LIMIT_EXCEEDED, USER_NOT_AUTHORIZED, SELF_PAYMENT_FORBIDDEN, INSUFFICIENT_BALANCE
  • 429TOO_MANY_REQUESTS — rate limit per app (default 600 requests/minute). Honour Retry-After.
# Sign first — see /docs/authentication or /tools/signature
curl -X POST '/open/v1/payouts' \
  -H 'X-Key-Id: $HTP_KEY_ID' \
  -H 'X-Timestamp: $TS' \
  -H 'X-Nonce: $NONCE' \
  -H 'X-Signature: $SIGNATURE' \
  -H 'Idempotency-Key: $IDEMPOTENCY_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"out_order_no":"P20261107-0001","amount":"25.00","currency":"USDT","payee_sub":"2f6c0d1e8a9b4c7d","description":"Referral reward"}'
{
  "trace_id": "string",
  "status_code": 200,
  "message": "success",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "string",
    "order_type": "PAYMENT",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "40.00",
    "fee_amount": "40.00",
    "net_amount": "40.00",
    "currency": "USDT",
    "description": "string",
    "user_sub": "string",
    "checkout_url": "https://…",
    "expires_at": "2026-11-07T09:00:00Z",
    "completed_at": "2026-11-07T09:00:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}
Sign this request

Orders

Look up payments, refunds and payouts.

Get an order

GET/open/v1/orders/{id}

Returns a payment, refund or payout by its HTP id. Only orders of the API key's environment are visible; an order of the other environment returns ORDER_NOT_FOUND.

Headers

X-Key-Idstringrequired
Signature header — see Authentication.
X-Timestampstringrequired
Signature header — see Authentication.
X-Noncestringrequired
Signature header — see Authentication.
X-Signaturestringrequired
Signature header — see Authentication.

Path parameters

idstring · uuidrequired
The HTP order id (UUID) returned when the order was created.

Response

200 · Order

Errors

  • 401SIGNATURE_INVALID — missing headers, unknown or revoked key, wrong signature, timestamp outside the window, or a reused nonce.
  • 404ORDER_NOT_FOUND — no such order for this app in the key's environment.
  • 429TOO_MANY_REQUESTS — rate limit per app (default 600 requests/minute). Honour Retry-After.
# Sign first — see /docs/authentication or /tools/signature
curl -X GET '/open/v1/orders/0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d' \
  -H 'X-Key-Id: $HTP_KEY_ID' \
  -H 'X-Timestamp: $TS' \
  -H 'X-Nonce: $NONCE' \
  -H 'X-Signature: $SIGNATURE'
{
  "trace_id": "string",
  "status_code": 200,
  "message": "success",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "string",
    "order_type": "PAYMENT",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "40.00",
    "fee_amount": "40.00",
    "net_amount": "40.00",
    "currency": "USDT",
    "description": "string",
    "user_sub": "string",
    "checkout_url": "https://…",
    "expires_at": "2026-11-07T09:00:00Z",
    "completed_at": "2026-11-07T09:00:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}
Sign this request

Find by order number

GET/open/v1/orders

Looks up an order by the number you chose. Use this to recover after a timeout on a create call. The query string is part of the signed PATH, exactly as sent (for example /open/v1/orders?out_order_no=A1&order_type=PAYMENT).

Headers

X-Key-Idstringrequired
Signature header — see Authentication.
X-Timestampstringrequired
Signature header — see Authentication.
X-Noncestringrequired
Signature header — see Authentication.
X-Signaturestringrequired
Signature header — see Authentication.

Query parameters

out_order_nostringrequired
Your out_order_no (use your out_refund_no for refunds).
order_typestring
PAYMENTREFUNDPAYOUT

Response

200 · Order

Errors

  • 400INVALID_REQUEST — a field failed validation; message says which. Also returned when Idempotency-Key is missing or malformed.
  • 401SIGNATURE_INVALID — missing headers, unknown or revoked key, wrong signature, timestamp outside the window, or a reused nonce.
  • 404ORDER_NOT_FOUND — no such order for this app in the key's environment.
  • 429TOO_MANY_REQUESTS — rate limit per app (default 600 requests/minute). Honour Retry-After.
# Sign first — see /docs/authentication or /tools/signature
curl -X GET '/open/v1/orders?out_order_no=A20261107-0001' \
  -H 'X-Key-Id: $HTP_KEY_ID' \
  -H 'X-Timestamp: $TS' \
  -H 'X-Nonce: $NONCE' \
  -H 'X-Signature: $SIGNATURE'
{
  "trace_id": "string",
  "status_code": 200,
  "message": "success",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "string",
    "order_type": "PAYMENT",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "40.00",
    "fee_amount": "40.00",
    "net_amount": "40.00",
    "currency": "USDT",
    "description": "string",
    "user_sub": "string",
    "checkout_url": "https://…",
    "expires_at": "2026-11-07T09:00:00Z",
    "completed_at": "2026-11-07T09:00:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}
Sign this request

Callbacks (webhooks)

Callbacks guide →

POST

payment.paid

The user paid a payment. data is the payment with status PAID and user_sub set.

Headers

X-Eventstringrequired
payment.paidpayment.refundedpayout.completed
X-Timestampstringrequired
Unix seconds when HTP signed this delivery. Reject deliveries older than 5 minutes.
X-Noncestringrequired
Random per delivery attempt.
Idempotency-Keystringrequired
Equals event_id. The same event keeps its ID across retries — de-duplicate on it.
X-Signaturestringrequired
Same scheme as requests, signed with your callback signing secret: base64(HMAC-SHA256(callback_secret, "POST\n" + path_and_query_of_your_callback_url + "\n" + X-Timestamp + "\n" + X-Nonce + "\n" + event_id + "\n" + hex(sha256(raw_body)))).
{
  "event_id": "9e8d7c6b-5a4f-4e3d-2c1b-0a9f8e7d6c5b",
  "event": "payment.paid",
  "created_at": "2026-11-07T09:05:12Z",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "A20261107-0001",
    "order_type": "PAYMENT",
    "environment": "SANDBOX",
    "status": "PAID",
    "amount": "40.00",
    "refunded_amount": "0.00",
    "fee_amount": "0.40",
    "net_amount": "39.60",
    "currency": "USDT",
    "description": "Premium membership, 1 month",
    "user_sub": "2f6c0d1e8a9b4c7d",
    "expires_at": "2026-11-07T09:30:00Z",
    "completed_at": "2026-11-07T09:05:12Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}
POST

payment.refunded

A refund succeeded. data is the refund order (order_type REFUND, parent_id = the payment).

Headers

X-Eventstringrequired
payment.paidpayment.refundedpayout.completed
X-Timestampstringrequired
Unix seconds when HTP signed this delivery. Reject deliveries older than 5 minutes.
X-Noncestringrequired
Random per delivery attempt.
Idempotency-Keystringrequired
Equals event_id. The same event keeps its ID across retries — de-duplicate on it.
X-Signaturestringrequired
Same scheme as requests, signed with your callback signing secret: base64(HMAC-SHA256(callback_secret, "POST\n" + path_and_query_of_your_callback_url + "\n" + X-Timestamp + "\n" + X-Nonce + "\n" + event_id + "\n" + hex(sha256(raw_body)))).
{
  "event_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
  "event": "payment.paid",
  "created_at": "2026-11-07T09:00:00Z",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "string",
    "order_type": "PAYMENT",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "40.00",
    "fee_amount": "40.00",
    "net_amount": "40.00",
    "currency": "USDT",
    "description": "string",
    "user_sub": "string",
    "checkout_url": "https://…",
    "expires_at": "2026-11-07T09:00:00Z",
    "completed_at": "2026-11-07T09:00:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}
POST

payout.completed

A payout succeeded. data is the payout order (order_type PAYOUT).

Headers

X-Eventstringrequired
payment.paidpayment.refundedpayout.completed
X-Timestampstringrequired
Unix seconds when HTP signed this delivery. Reject deliveries older than 5 minutes.
X-Noncestringrequired
Random per delivery attempt.
Idempotency-Keystringrequired
Equals event_id. The same event keeps its ID across retries — de-duplicate on it.
X-Signaturestringrequired
Same scheme as requests, signed with your callback signing secret: base64(HMAC-SHA256(callback_secret, "POST\n" + path_and_query_of_your_callback_url + "\n" + X-Timestamp + "\n" + X-Nonce + "\n" + event_id + "\n" + hex(sha256(raw_body)))).
{
  "event_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
  "event": "payment.paid",
  "created_at": "2026-11-07T09:00:00Z",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "string",
    "order_type": "PAYMENT",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "40.00",
    "fee_amount": "40.00",
    "net_amount": "40.00",
    "currency": "USDT",
    "description": "string",
    "user_sub": "string",
    "checkout_url": "https://…",
    "expires_at": "2026-11-07T09:00:00Z",
    "completed_at": "2026-11-07T09:00:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}

Objects

Order

idstring · uuidrequired
HTP order ID.
out_order_nostringrequired
Your order number (out_refund_no for refunds).
order_typestringrequired
PAYMENTREFUNDPAYOUT
parent_idstring · uuid
Refunds only — the payment that was refunded.
environmentstringrequired
SANDBOXPRODUCTION
statusstringrequired
PAYMENT: CREATED → PAID or CLOSED (closed or expired). REFUND and PAYOUT: SUCCEEDED.
CREATEDPAIDCLOSEDSUCCEEDED
amountstringrequired
USDT amount, positive, at most two decimals. Responses always use two decimals.
refunded_amountstringrequired
Payments only — total refunded so far.
fee_amountstringrequired
Platform fee on a payment. 0.00 on refunds and payouts.
net_amountstringrequired
What your wallet received for a payment (amount - fee_amount); equals amount for refunds and payouts.
currencystringrequired
USDT
descriptionstring
user_substring
The user's OIDC sub for your developer account. Empty on an unpaid payment without payer_sub.
checkout_urlstring · uri
Only on CREATED, unexpired payments. Open it inside the HTP App.
expires_atstring · date-time
completed_atstring · date-time
created_atstring · date-timerequired
{
  "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
  "out_order_no": "string",
  "order_type": "PAYMENT",
  "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
  "environment": "SANDBOX",
  "status": "CREATED",
  "amount": "40.00",
  "refunded_amount": "40.00",
  "fee_amount": "40.00",
  "net_amount": "40.00",
  "currency": "USDT",
  "description": "string",
  "user_sub": "string",
  "checkout_url": "https://…",
  "expires_at": "2026-11-07T09:00:00Z",
  "completed_at": "2026-11-07T09:00:00Z",
  "created_at": "2026-11-07T09:00:00Z"
}

WebhookEvent

event_idstring · uuidrequired
Unique per event and stable across retries. De-duplicate on it.
eventstringrequired
payment.paidpayment.refundedpayout.completed
created_atstring · date-timerequired
dataobjectrequired
data.idstring · uuidrequired
HTP order ID.
data.out_order_nostringrequired
Your order number (out_refund_no for refunds).
data.order_typestringrequired
PAYMENTREFUNDPAYOUT
data.parent_idstring · uuid
Refunds only — the payment that was refunded.
data.environmentstringrequired
SANDBOXPRODUCTION
data.statusstringrequired
PAYMENT: CREATED → PAID or CLOSED (closed or expired). REFUND and PAYOUT: SUCCEEDED.
CREATEDPAIDCLOSEDSUCCEEDED
data.amountstringrequired
USDT amount, positive, at most two decimals. Responses always use two decimals.
data.refunded_amountstringrequired
Payments only — total refunded so far.
data.fee_amountstringrequired
Platform fee on a payment. 0.00 on refunds and payouts.
data.net_amountstringrequired
What your wallet received for a payment (amount - fee_amount); equals amount for refunds and payouts.
data.currencystringrequired
USDT
data.descriptionstring
data.user_substring
The user's OIDC sub for your developer account. Empty on an unpaid payment without payer_sub.
data.checkout_urlstring · uri
Only on CREATED, unexpired payments. Open it inside the HTP App.
data.expires_atstring · date-time
data.completed_atstring · date-time
data.created_atstring · date-timerequired
{
  "event_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
  "event": "payment.paid",
  "created_at": "2026-11-07T09:00:00Z",
  "data": {
    "id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "out_order_no": "string",
    "order_type": "PAYMENT",
    "parent_id": "0b7a9c62-3f1e-4a2b-9d6c-5e4f3a2b1c0d",
    "environment": "SANDBOX",
    "status": "CREATED",
    "amount": "40.00",
    "refunded_amount": "40.00",
    "fee_amount": "40.00",
    "net_amount": "40.00",
    "currency": "USDT",
    "description": "string",
    "user_sub": "string",
    "checkout_url": "https://…",
    "expires_at": "2026-11-07T09:00:00Z",
    "completed_at": "2026-11-07T09:00:00Z",
    "created_at": "2026-11-07T09:00:00Z"
  }
}

Error

trace_idstringrequired
status_codeintegerrequired
Same as the HTTP status.
messagestringrequired
English explanation for developers. Do not branch on it.
error_codestringrequired
Stable code to branch on.
INVALID_REQUESTSIGNATURE_INVALIDREPLAY_PROTECTION_UNAVAILABLEIDEMPOTENCY_KEY_REUSEDIDEMPOTENCY_IN_PROGRESSAPP_UNAVAILABLEPAYMENT_NOT_ENABLEDPAYOUT_NOT_ALLOWEDSANDBOX_USER_ONLYCURRENCY_NOT_SUPPORTEDINVALID_AMOUNTLIMIT_EXCEEDEDORDER_ALREADY_EXISTSORDER_NOT_FOUNDUSER_NOT_AUTHORIZEDSELF_PAYMENT_FORBIDDENREFUND_NOT_ALLOWEDREFUND_AMOUNT_EXCEEDEDINSUFFICIENT_BALANCEDEVELOPER_WALLET_UNAVAILABLEINVALID_STATETOO_MANY_REQUESTSINTERNAL_ERROR
{
  "trace_id": "string",
  "status_code": 0,
  "message": "string",
  "error_code": "INVALID_REQUEST"
}