Invoices
Base URL: https://txnod.com/api/v1. Every request must carry the three HMAC headers:
X-Project-Id— the caller’s project ULID.X-Timestamp— current Unix timestamp (seconds). Requests outside the ±300-second window are rejected withtimestamp_out_of_window.X-Signature— HMAC-SHA256 hex digest of${timestamp}.${rawBody}using the project’s API secret.
The SDK (@txnod/sdk) handles signing transparently — prefer it over hand-rolled cURL except for smoke tests.
Create an invoice
POST https://txnod.com/api/v1/invoices — creates a new invoice. Idempotent on (project_id, external_id): a replayed request returns the existing invoice with HTTP 200 instead of 201.
Request
Request body (application/json)
| Field | Type | Required | Constraints |
|---|---|---|---|
external_id | string | yes | min length: 1; max length: 128 |
amount_usd | number | no | exclusive min: 0 |
amount_crypto | string | no | pattern: ^\d+(\.\d+)?$ |
coin | "btc" | "eth" | "usdt_erc20" | "usdc_erc20" | "trx" | "usdt_trc20" | "ada" | "pol" | "usdt_polygon" | "usdc_polygon" | "bnb" | "usdt_bep20" | "usdc_bep20" | "ton" | "usdt_ton" | yes | — |
callback_url | string (URI) | no | — |
metadata | object | no | — |
Response
200 — Idempotent replay — existing invoice returned unchanged
| Field | Type | Required | Constraints |
|---|---|---|---|
id | string (ULID) | yes | — |
project_id | string (ULID) | yes | — |
external_id | string | yes | — |
coin | "btc" | "eth" | "usdt_erc20" | "usdc_erc20" | "trx" | "usdt_trc20" | "ada" | "pol" | "usdt_polygon" | "usdc_polygon" | "bnb" | "usdt_bep20" | "usdc_bep20" | "ton" | "usdt_ton" | yes | — |
address | string | yes | — |
amount_crypto | string | yes | — |
amount_crypto_units | string | yes | — |
amount_usd | number | null | yes | — |
rate_snapshot | object | null | yes | — |
payment_token | string | null | yes | pattern: ^[0-9a-f]{8}$ |
payment_uri | string | yes | — |
callback_url | string | null | yes | — |
metadata | object | null | yes | — |
matching_mode | "exact" | "at_least" | "any" | yes | — |
confirmation_threshold | integer | yes | min: 0 |
status | "pending" | "detected" | "paid" | "overpaid" | "partial" | "expired" | "expired_paid_late" | "reverted" | "cancelled" | yes | — |
expires_at | integer | yes | min: 0 |
expires_at_iso | string (ISO 8601) | yes | — |
created_at | integer | yes | min: 0 |
created_at_iso | string (ISO 8601) | yes | — |
derivation_path | string | no | pattern: ^m(\/\d+'?)+$ |
verification_standard | "bip84" | "bip44" | "cip1852" | "bip44_ed25519" | no | — |
transactions | array<object> | no | — |
confirmations | integer | no | min: 0 |
201 — Invoice created
| Field | Type | Required | Constraints |
|---|---|---|---|
id | string (ULID) | yes | — |
project_id | string (ULID) | yes | — |
external_id | string | yes | — |
coin | "btc" | "eth" | "usdt_erc20" | "usdc_erc20" | "trx" | "usdt_trc20" | "ada" | "pol" | "usdt_polygon" | "usdc_polygon" | "bnb" | "usdt_bep20" | "usdc_bep20" | "ton" | "usdt_ton" | yes | — |
address | string | yes | — |
amount_crypto | string | yes | — |
amount_crypto_units | string | yes | — |
amount_usd | number | null | yes | — |
rate_snapshot | object | null | yes | — |
payment_token | string | null | yes | pattern: ^[0-9a-f]{8}$ |
payment_uri | string | yes | — |
callback_url | string | null | yes | — |
metadata | object | null | yes | — |
matching_mode | "exact" | "at_least" | "any" | yes | — |
confirmation_threshold | integer | yes | min: 0 |
status | "pending" | "detected" | "paid" | "overpaid" | "partial" | "expired" | "expired_paid_late" | "reverted" | "cancelled" | yes | — |
expires_at | integer | yes | min: 0 |
expires_at_iso | string (ISO 8601) | yes | — |
created_at | integer | yes | min: 0 |
created_at_iso | string (ISO 8601) | yes | — |
derivation_path | string | no | pattern: ^m(\/\d+'?)+$ |
verification_standard | "bip84" | "bip44" | "cip1852" | "bip44_ed25519" | no | — |
transactions | array<object> | no | — |
confirmations | integer | no | min: 0 |
Errors
| Status | Error code(s) | Trigger |
|---|---|---|
| 400 | validation_error, invalid_coin, invalid_xpub_format, invalid_webhook_url | Request body or query failed validation |
| 401 | auth_invalid, signature_invalid, timestamp_out_of_window | HMAC authentication failed |
| 402 | subscription_expired | Operator’s TxNod subscription is not active; writes are blocked. Operator must renew via dashboard /billing |
| 403 | key_revoked, key_suspended, project_suspended, permission_denied | Auth key or project disabled by operator action |
| 409 | external_id_conflict, xpub_not_verified | Idempotent replay or ownership challenge incomplete |
| 422 | coin_not_enabled, amount_out_of_range, wallet_not_bound, tron_no_activated_addresses_available | Coin not enabled, amount out of range, no verified wallet bound for the requested chain on the project, or — TRON only — operator’s address pool has zero activated rows |
| 429 | rate_limit_exceeded | Per-project invoice-create rate limit exceeded |
| 500 | internal_error | Internal error (including cold-start rate unavailability) |
| 503 | pool_exhausted | Address pool at hard cap; retry after Retry-After |
Examples
curl -X POST https://txnod.com/api/v1/invoices \
-H "X-Project-Id: $TXNOD_PROJECT_ID" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <hex>" \
-H "Content-Type: application/json" \
-d '{"external_id":"order-123","amount_usd":10.0,"coin":"btc"}'import { TxnodClient } from '@txnod/sdk';
const client = new TxnodClient({
projectId: process.env.TXNOD_PROJECT_ID!,
apiSecret: process.env.TXNOD_API_SECRET!,
});
const invoice = await client.createInvoice({
external_id: 'order-123',
amount_usd: 10.0,
coin: 'btc',
});Search invoices
GET https://txnod.com/api/v1/invoices — cursor-paginated search over the caller project’s invoices.
Request
Query parameters
| Param | Type | Required | Constraints |
|---|---|---|---|
external_id | string | no | — |
address | string | no | — |
tx_hash | string | no | — |
amount | string | no | — |
status | "pending" | "detected" | "paid" | "overpaid" | "partial" | "expired" | "expired_paid_late" | "reverted" | "cancelled" | no | — |
date_from | string (ISO 8601) | no | — |
date_to | string (ISO 8601) | no | — |
cursor | string (ULID) | no | — |
limit | integer | no | min: 1; max: 200; default: 50 |
Response
200 — Cursor-paginated invoice list
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | yes | — |
next_cursor | string (ULID) | no | — |
Errors
| Status | Error code(s) | Trigger |
|---|---|---|
| 400 | validation_error | Query validation error |
| 401 | auth_invalid, signature_invalid, timestamp_out_of_window | HMAC authentication failed |
Examples
curl "https://txnod.com/api/v1/invoices?status=paid&limit=20" \
-H "X-Project-Id: $TXNOD_PROJECT_ID" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <hex>"import { TxnodClient } from '@txnod/sdk';
const client = new TxnodClient({
projectId: process.env.TXNOD_PROJECT_ID!,
apiSecret: process.env.TXNOD_API_SECRET!,
});
const page = await client.searchInvoices({ status: 'paid', limit: 20 });
for (const invoice of page.items) console.log(invoice.id);Retrieve an invoice
GET https://txnod.com/api/v1/invoices/{id} — fetch a single invoice by ULID. Returns 404 on cross-project lookups (no enumeration).
Request
Path parameters
| Param | Type | Required | Constraints |
|---|---|---|---|
id | string (ULID) | yes | — |
Response
200 — Invoice detail including transactions + confirmations
| Field | Type | Required | Constraints |
|---|---|---|---|
id | string (ULID) | yes | — |
project_id | string (ULID) | yes | — |
external_id | string | yes | — |
coin | "btc" | "eth" | "usdt_erc20" | "usdc_erc20" | "trx" | "usdt_trc20" | "ada" | "pol" | "usdt_polygon" | "usdc_polygon" | "bnb" | "usdt_bep20" | "usdc_bep20" | "ton" | "usdt_ton" | yes | — |
address | string | yes | — |
amount_crypto | string | yes | — |
amount_crypto_units | string | yes | — |
amount_usd | number | null | yes | — |
rate_snapshot | object | null | yes | — |
payment_token | string | null | yes | pattern: ^[0-9a-f]{8}$ |
payment_uri | string | yes | — |
callback_url | string | null | yes | — |
metadata | object | null | yes | — |
matching_mode | "exact" | "at_least" | "any" | yes | — |
confirmation_threshold | integer | yes | min: 0 |
status | "pending" | "detected" | "paid" | "overpaid" | "partial" | "expired" | "expired_paid_late" | "reverted" | "cancelled" | yes | — |
expires_at | integer | yes | min: 0 |
expires_at_iso | string (ISO 8601) | yes | — |
created_at | integer | yes | min: 0 |
created_at_iso | string (ISO 8601) | yes | — |
derivation_path | string | no | pattern: ^m(\/\d+'?)+$ |
verification_standard | "bip84" | "bip44" | "cip1852" | "bip44_ed25519" | no | — |
transactions | array<object> | no | — |
confirmations | integer | no | min: 0 |
Errors
| Status | Error code(s) | Trigger |
|---|---|---|
| 401 | auth_invalid, signature_invalid, timestamp_out_of_window | HMAC authentication failed |
| 404 | invoice_not_found | Invoice not found or belongs to a different project |
Examples
curl https://txnod.com/api/v1/invoices/01HK8MAR2QEXAMPLE000000000 \
-H "X-Project-Id: $TXNOD_PROJECT_ID" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <hex>"import { TxnodClient } from '@txnod/sdk';
const client = new TxnodClient({
projectId: process.env.TXNOD_PROJECT_ID!,
apiSecret: process.env.TXNOD_API_SECRET!,
});
const invoice = await client.getInvoice('01HK8MAR2QEXAMPLE000000000');
console.log(invoice.status);Cancel an invoice
POST https://txnod.com/api/v1/invoices/{id}/cancel — cancels an invoice in pending or detected state and releases its pool address into cooldown. Terminal-status invoices return 409 invoice_not_cancellable.
Request
Path parameters
| Param | Type | Required | Constraints |
|---|---|---|---|
id | string (ULID) | yes | — |
Response
200 — Invoice cancelled
| Field | Type | Required | Constraints |
|---|---|---|---|
id | string (ULID) | yes | — |
project_id | string (ULID) | yes | — |
external_id | string | yes | — |
coin | "btc" | "eth" | "usdt_erc20" | "usdc_erc20" | "trx" | "usdt_trc20" | "ada" | "pol" | "usdt_polygon" | "usdc_polygon" | "bnb" | "usdt_bep20" | "usdc_bep20" | "ton" | "usdt_ton" | yes | — |
address | string | yes | — |
amount_crypto | string | yes | — |
amount_crypto_units | string | yes | — |
amount_usd | number | null | yes | — |
rate_snapshot | object | null | yes | — |
payment_token | string | null | yes | pattern: ^[0-9a-f]{8}$ |
payment_uri | string | yes | — |
callback_url | string | null | yes | — |
metadata | object | null | yes | — |
matching_mode | "exact" | "at_least" | "any" | yes | — |
confirmation_threshold | integer | yes | min: 0 |
status | "pending" | "detected" | "paid" | "overpaid" | "partial" | "expired" | "expired_paid_late" | "reverted" | "cancelled" | yes | — |
expires_at | integer | yes | min: 0 |
expires_at_iso | string (ISO 8601) | yes | — |
created_at | integer | yes | min: 0 |
created_at_iso | string (ISO 8601) | yes | — |
derivation_path | string | no | pattern: ^m(\/\d+'?)+$ |
verification_standard | "bip84" | "bip44" | "cip1852" | "bip44_ed25519" | no | — |
transactions | array<object> | no | — |
confirmations | integer | no | min: 0 |
Errors
| Status | Error code(s) | Trigger |
|---|---|---|
| 401 | auth_invalid, signature_invalid, timestamp_out_of_window | HMAC authentication failed |
| 404 | invoice_not_found | Invoice not found or belongs to a different project |
| 409 | invoice_not_cancellable, invalid_state_transition | Invoice is not in a cancellable state |
Examples
curl -X POST https://txnod.com/api/v1/invoices/01HK8MAR2QEXAMPLE000000000/cancel \
-H "X-Project-Id: $TXNOD_PROJECT_ID" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: <hex>"import { TxnodClient } from '@txnod/sdk';
const client = new TxnodClient({
projectId: process.env.TXNOD_PROJECT_ID!,
apiSecret: process.env.TXNOD_API_SECRET!,
});
const cancelled = await client.cancelInvoice('01HK8MAR2QEXAMPLE000000000');
console.log(cancelled.status);