@txnod/sdk 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +29 -0
- package/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +434 -0
- package/dist/_shared/index.d.ts +68 -0
- package/dist/client-sandbox.d.ts +396 -0
- package/dist/client-sandbox.d.ts.map +1 -0
- package/dist/client-sandbox.js +448 -0
- package/dist/client-sandbox.js.map +1 -0
- package/dist/client.d.ts +429 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +588 -0
- package/dist/client.js.map +1 -0
- package/dist/env.d.ts +29 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +44 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +1887 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +2107 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/internals/error-ctor-map.d.ts +11 -0
- package/dist/internals/error-ctor-map.d.ts.map +1 -0
- package/dist/internals/error-ctor-map.js +75 -0
- package/dist/internals/error-ctor-map.js.map +1 -0
- package/dist/internals/fetch-with-retry.d.ts +34 -0
- package/dist/internals/fetch-with-retry.d.ts.map +1 -0
- package/dist/internals/fetch-with-retry.js +233 -0
- package/dist/internals/fetch-with-retry.js.map +1 -0
- package/dist/internals/hmac.d.ts +2 -0
- package/dist/internals/hmac.d.ts.map +1 -0
- package/dist/internals/hmac.js +10 -0
- package/dist/internals/hmac.js.map +1 -0
- package/dist/internals/logger.d.ts +9 -0
- package/dist/internals/logger.d.ts.map +1 -0
- package/dist/internals/logger.js +16 -0
- package/dist/internals/logger.js.map +1 -0
- package/dist/internals/parse-problem-details.d.ts +3 -0
- package/dist/internals/parse-problem-details.d.ts.map +1 -0
- package/dist/internals/parse-problem-details.js +76 -0
- package/dist/internals/parse-problem-details.js.map +1 -0
- package/dist/internals/synthetic-details.d.ts +12 -0
- package/dist/internals/synthetic-details.d.ts.map +1 -0
- package/dist/internals/synthetic-details.js +19 -0
- package/dist/internals/synthetic-details.js.map +1 -0
- package/dist/verify/chains/bsc.d.ts +17 -0
- package/dist/verify/chains/bsc.d.ts.map +1 -0
- package/dist/verify/chains/bsc.js +15 -0
- package/dist/verify/chains/bsc.js.map +1 -0
- package/dist/verify/chains/btc.d.ts +22 -0
- package/dist/verify/chains/btc.d.ts.map +1 -0
- package/dist/verify/chains/btc.js +55 -0
- package/dist/verify/chains/btc.js.map +1 -0
- package/dist/verify/chains/cardano.d.ts +73 -0
- package/dist/verify/chains/cardano.d.ts.map +1 -0
- package/dist/verify/chains/cardano.js +175 -0
- package/dist/verify/chains/cardano.js.map +1 -0
- package/dist/verify/chains/evm.d.ts +21 -0
- package/dist/verify/chains/evm.d.ts.map +1 -0
- package/dist/verify/chains/evm.js +46 -0
- package/dist/verify/chains/evm.js.map +1 -0
- package/dist/verify/chains/polygon.d.ts +17 -0
- package/dist/verify/chains/polygon.d.ts.map +1 -0
- package/dist/verify/chains/polygon.js +15 -0
- package/dist/verify/chains/polygon.js.map +1 -0
- package/dist/verify/chains/secp256k1-bip32.d.ts +20 -0
- package/dist/verify/chains/secp256k1-bip32.d.ts.map +1 -0
- package/dist/verify/chains/secp256k1-bip32.js +88 -0
- package/dist/verify/chains/secp256k1-bip32.js.map +1 -0
- package/dist/verify/chains/ton-cell.d.ts +179 -0
- package/dist/verify/chains/ton-cell.d.ts.map +1 -0
- package/dist/verify/chains/ton-cell.js +614 -0
- package/dist/verify/chains/ton-cell.js.map +1 -0
- package/dist/verify/chains/ton.d.ts +84 -0
- package/dist/verify/chains/ton.d.ts.map +1 -0
- package/dist/verify/chains/ton.js +131 -0
- package/dist/verify/chains/ton.js.map +1 -0
- package/dist/verify/chains/tron.d.ts +21 -0
- package/dist/verify/chains/tron.d.ts.map +1 -0
- package/dist/verify/chains/tron.js +42 -0
- package/dist/verify/chains/tron.js.map +1 -0
- package/dist/verify/config.d.ts +41 -0
- package/dist/verify/config.d.ts.map +1 -0
- package/dist/verify/config.js +120 -0
- package/dist/verify/config.js.map +1 -0
- package/dist/verify/errors.d.ts +56 -0
- package/dist/verify/errors.d.ts.map +1 -0
- package/dist/verify/errors.js +58 -0
- package/dist/verify/errors.js.map +1 -0
- package/dist/verify/index.d.ts +119 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +166 -0
- package/dist/verify/index.js.map +1 -0
- package/dist/verify/xpub-safety.d.ts +33 -0
- package/dist/verify/xpub-safety.d.ts.map +1 -0
- package/dist/verify/xpub-safety.js +54 -0
- package/dist/verify/xpub-safety.js.map +1 -0
- package/dist/verify-webhook-signature.d.ts +30 -0
- package/dist/verify-webhook-signature.d.ts.map +1 -0
- package/dist/verify-webhook-signature.js +84 -0
- package/dist/verify-webhook-signature.js.map +1 -0
- package/docs/00-getting-started.md +135 -0
- package/docs/01-authentication.md +114 -0
- package/docs/02-invoices.md +216 -0
- package/docs/03-rates-and-quotes.md +82 -0
- package/docs/04-webhooks.md +126 -0
- package/docs/05-errors.md +199 -0
- package/docs/05-sandbox.md +159 -0
- package/docs/06-idempotency.md +132 -0
- package/docs/examples/express-webhook-receiver.md +97 -0
- package/docs/examples/nextjs-route-handler.md +206 -0
- package/docs/examples/sandbox-vitest-suite.md +263 -0
- package/docs/index.md +66 -0
- package/docs/reference/client.md +392 -0
- package/docs/reference/errors.md +161 -0
- package/docs/reference/types.md +400 -0
- package/package.json +53 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "TxnodClient reference"
|
|
3
|
+
description: "Every method of TxnodClient and the verifyWebhookSignature helper: signature, behaviour, and a minimal example."
|
|
4
|
+
sdk_version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# `TxnodClient` reference
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { TxnodClient } from '@txnod/sdk';
|
|
11
|
+
|
|
12
|
+
const client = new TxnodClient({
|
|
13
|
+
projectId: string; // required — your TXNOD_PROJECT_ID
|
|
14
|
+
apiSecret: string; // required — your TXNOD_API_SECRET
|
|
15
|
+
baseUrl?: string; // optional — defaults to 'https://txnod.com'
|
|
16
|
+
requestTimeoutMs?: number; // optional — per-attempt HTTP timeout (default 30_000)
|
|
17
|
+
maxResponseBytes?: number; // optional — body size cap (default 1 MiB)
|
|
18
|
+
requestLogger?: TxnodRequestLogger; // optional — per-attempt observer
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`baseUrl` controls only the HTTP origin the SDK talks to. The HMAC scheme, header names, request/response shapes, and retry semantics are all endpoint-agnostic. Any trailing slash is stripped by the constructor. Typical values:
|
|
23
|
+
|
|
24
|
+
- Omitted → `https://txnod.com` (default).
|
|
25
|
+
- `TXNOD_BASE_URL=https://staging.txnod.com` for a staging environment.
|
|
26
|
+
- `'https://pay.mycompany.com'` for a self-hosted txnod-compatible deployment.
|
|
27
|
+
|
|
28
|
+
Every method returns a `Promise<T>` and throws a subclass of `TxnodError` on a non-2xx response. Retries for `429` (×3, honouring `Retry-After`) and `5xx` (×2, full-jitter backoff, base 500 ms, cap 30 s) happen inside the SDK.
|
|
29
|
+
|
|
30
|
+
After every call (success or error) the client stores the server's `X-Txnod-Request-Id` header on `client.lastRequestId` (`string | undefined`). On error paths the same id is also surfaced as `err.request_id` on the thrown `TxnodError`. The field is per-instance — concurrent calls on a shared client overwrite each other; create a client per request scope, or read the field synchronously after `await`-ing.
|
|
31
|
+
|
|
32
|
+
Methods below are ordered by how you will reach for them in a typical integration.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## `createInvoice`
|
|
37
|
+
|
|
38
|
+
Create a new invoice. Idempotent on `(project_id, external_id)`.
|
|
39
|
+
|
|
40
|
+
**Signature**
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
createInvoice(body: InvoiceCreateRequest): Promise<InvoiceResponse>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Parameters**
|
|
47
|
+
|
|
48
|
+
| Field | Type | Required | Notes |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| `external_id` | `string` (1..128) | yes | Your-side stable id for this charge |
|
|
51
|
+
| `coin` | `Coin` | yes | One of the 15 supported coins |
|
|
52
|
+
| `amount_usd` | `number` positive finite | either-or | Mutually exclusive with `amount_crypto` |
|
|
53
|
+
| `amount_crypto` | `string` (`^\d+(\.\d+)?$`) | either-or | Mutually exclusive with `amount_usd` |
|
|
54
|
+
| `callback_url` | `string` (https URL) | no | Webhook target for this invoice |
|
|
55
|
+
| `metadata` | `Record<string, unknown>` | no | Free-form JSON returned on the invoice |
|
|
56
|
+
|
|
57
|
+
**Throws**: `TxnodExternalIdConflictError` (idempotent — fetch existing), `TxnodValidationError`, `TxnodInvalidCoinError`, `TxnodCoinNotEnabledError`, `TxnodAmountOutOfRangeError`, `TxnodXpubNotVerifiedError`, `TxnodWalletNotFoundError`, `TxnodWalletNotBoundError`, `TxnodInvalidWebhookUrlError`, `TxnodTronNoActivatedAddressesError` (TRON only — operator must activate addresses), `TxnodSubscriptionExpiredError` (HTTP 402 — operator must renew subscription), `TxnodPoolExhaustedError` (retryable), auth/identity family (see [`../05-errors.md`](../05-errors.md)). The SDK also raises `AddressVerificationError` (not a `TxnodError` subclass) when the returned `address` does not derive from a configured xpub — see [`../02-invoices.md`](../02-invoices.md#address-verification).
|
|
58
|
+
|
|
59
|
+
**Example**
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const invoice = await client.createInvoice({
|
|
63
|
+
external_id: 'order-42',
|
|
64
|
+
coin: 'usdt_trc20',
|
|
65
|
+
amount_usd: 9.99,
|
|
66
|
+
callback_url: 'https://your-site.com/api/txnod-webhook',
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## `createOrGetInvoice`
|
|
75
|
+
|
|
76
|
+
Idempotent invoice creation. Tries `createInvoice`; on `TxnodExternalIdConflictError` fetches the pre-existing invoice via `searchInvoices({ external_id })` and returns it.
|
|
77
|
+
|
|
78
|
+
**Signature**
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
createOrGetInvoice(body: InvoiceCreateRequest): Promise<InvoiceResponse>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Throws**: every error that `createInvoice` can throw EXCEPT `TxnodExternalIdConflictError`. If the conflict is raised but the matching invoice cannot be fetched (race, recently deleted), the original conflict error rethrows.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## `refreshXpubConfig`
|
|
89
|
+
|
|
90
|
+
Re-read `TXNOD_<chain>_XPUB` env vars (and `TXNOD_TON_PUBKEY` siblings) into the client's xpub config. Call after wallet rotation in long-lived processes.
|
|
91
|
+
|
|
92
|
+
**Signature**
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
refreshXpubConfig(): void
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Idempotent. No-op when env vars are unchanged.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## `getInvoice`
|
|
103
|
+
|
|
104
|
+
Fetch an invoice by its ULID.
|
|
105
|
+
|
|
106
|
+
**Signature**
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
getInvoice(id: string): Promise<InvoiceResponse>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Throws**: `TxnodInvoiceNotFoundError` on unknown id.
|
|
113
|
+
|
|
114
|
+
**Example**
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const invoice = await client.getInvoice('01HK8MAR2QEXAMPLE000000000');
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## `searchInvoices`
|
|
123
|
+
|
|
124
|
+
Cursor-paginated search.
|
|
125
|
+
|
|
126
|
+
**Signature**
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
searchInvoices(query: InvoiceSearchQuery): Promise<CursorPaginatedInvoiceResponse>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Query fields** (all optional)
|
|
133
|
+
|
|
134
|
+
| Field | Type | Notes |
|
|
135
|
+
|---|---|---|
|
|
136
|
+
| `external_id` | `string` | Exact match |
|
|
137
|
+
| `address` | `string` | Exact match on deposit address |
|
|
138
|
+
| `tx_hash` | `string` | Invoices that received this tx |
|
|
139
|
+
| `amount` | `string` | Crypto amount as decimal string |
|
|
140
|
+
| `status` | `InvoiceStatus` | See [`../02-invoices.md`](../02-invoices.md#lifecycle) |
|
|
141
|
+
| `date_from` / `date_to` | ISO-8601 datetime | Created-at window |
|
|
142
|
+
| `cursor` | ULID | `next_cursor` from the previous page |
|
|
143
|
+
| `limit` | `1..200` | Default `50` |
|
|
144
|
+
|
|
145
|
+
**Response**: `{ items: InvoiceResponse[], next_cursor?: string }`. `next_cursor` is omitted on the last page.
|
|
146
|
+
|
|
147
|
+
**Example**
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
const page = await client.searchInvoices({ status: 'paid', limit: 50 });
|
|
151
|
+
for (const invoice of page.items) { /* ... */ }
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## `cancelInvoice`
|
|
157
|
+
|
|
158
|
+
**Signature**
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
cancelInvoice(id: string): Promise<InvoiceResponse>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Throws**: `TxnodInvoiceNotFoundError`, `TxnodInvoiceNotCancellableError` when the invoice is past its cancellable state.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## `getRates`
|
|
169
|
+
|
|
170
|
+
Current indicative USD→coin rates. See [`../03-rates-and-quotes.md`](../03-rates-and-quotes.md).
|
|
171
|
+
|
|
172
|
+
**Signature**
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
getRates(query: { coins?: string }): Promise<RatesResponse>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`coins` is an optional comma-separated list. Omit for all enabled coins.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## `quoteAmount`
|
|
183
|
+
|
|
184
|
+
Indicative USD → per-coin crypto quote. See [`../03-rates-and-quotes.md`](../03-rates-and-quotes.md).
|
|
185
|
+
|
|
186
|
+
**Signature**
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
quoteAmount(query: { amount_usd: number; coins?: string }): Promise<QuoteResponse>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## `listOrphanPayments`
|
|
195
|
+
|
|
196
|
+
Paginated list of on-chain receipts that did not match any open invoice.
|
|
197
|
+
|
|
198
|
+
**Signature**
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
listOrphanPayments(query: OrphanPaymentListQuery): Promise<CursorPaginatedOrphanPaymentResponse>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Query fields** (all optional)
|
|
205
|
+
|
|
206
|
+
| Field | Type | Notes |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `chain` | `Chain` | `btc` / `eth` / `tron` / `ada` / `polygon` / `bsc` / `ton` |
|
|
209
|
+
| `attributed` | `boolean` | Filter by attribution state (SDK serialises to `?attributed=true`/`false`) |
|
|
210
|
+
| `tx_hash` | `string` | Exact |
|
|
211
|
+
| `date_from` / `date_to` | ISO-8601 datetime | |
|
|
212
|
+
| `amount_units_gte` / `amount_units_lte` | integer string | Filter in smallest unit |
|
|
213
|
+
| `cursor` | ULID | |
|
|
214
|
+
| `limit` | `1..200` | Default `50` |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## `attributeOrphanPayment`
|
|
219
|
+
|
|
220
|
+
Post-hoc bind an orphan on-chain receipt to an `external_id`.
|
|
221
|
+
|
|
222
|
+
**Signature**
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
attributeOrphanPayment(
|
|
226
|
+
txHash: string,
|
|
227
|
+
body: OrphanAttributeRequest,
|
|
228
|
+
): Promise<InvoiceResponse>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Body**
|
|
232
|
+
|
|
233
|
+
| Field | Type | Required | Notes |
|
|
234
|
+
|---|---|---|---|
|
|
235
|
+
| `external_id` | `string` (1..128) | yes | Target invoice's external id |
|
|
236
|
+
| `user_id` | `string` (1..256) | no | Your-side user id for audit trail |
|
|
237
|
+
| `metadata` | `Record<string, unknown>` | no | |
|
|
238
|
+
| `to_address` | `string` | no | Disambiguate among multi-output txs |
|
|
239
|
+
| `tx_output_index` | `int ≥ 0` | no | Disambiguate among multi-output txs |
|
|
240
|
+
|
|
241
|
+
**Throws**: `TxnodOrphanNotFoundError`, `TxnodOrphanAlreadyAttributedError` (idempotent — treat as success).
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## `listWebhookEvents`
|
|
246
|
+
|
|
247
|
+
Paginated audit list of outbound webhook events for the calling project.
|
|
248
|
+
|
|
249
|
+
**Signature**
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
listWebhookEvents(query: WebhookEventListApiQuery): Promise<WebhookEventListResponse>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
**Query fields** (all optional)
|
|
256
|
+
|
|
257
|
+
| Field | Type | Notes |
|
|
258
|
+
|---|---|---|
|
|
259
|
+
| `status` | `'delivered' \| 'retrying' \| 'dlq' \| 'skipped'` | |
|
|
260
|
+
| `event_type` | `WebhookEventType` | See [`../04-webhooks.md`](../04-webhooks.md#event-types) |
|
|
261
|
+
| `mode` | `'production' \| 'testnet' \| 'sandbox'` | Filter events by the source project's kind (mode = project.kind on the server side) |
|
|
262
|
+
| `since` | ISO-8601 datetime | |
|
|
263
|
+
| `invoice_id` | ULID | |
|
|
264
|
+
| `cursor` | ULID | |
|
|
265
|
+
| `limit` | `1..200` | Default `50` |
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## `resendWebhookEvent`
|
|
270
|
+
|
|
271
|
+
Re-enqueue a previously dispatched webhook event.
|
|
272
|
+
|
|
273
|
+
**Signature**
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
resendWebhookEvent(eventId: string): Promise<WebhookEventResendResponse>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Response**
|
|
280
|
+
|
|
281
|
+
| Field | Notes |
|
|
282
|
+
|---|---|
|
|
283
|
+
| `event_id` | New ULID for the resent delivery |
|
|
284
|
+
| `original_event_id` | ULID of the original event |
|
|
285
|
+
| `event_type` | Same as the original |
|
|
286
|
+
| `project_id` / `invoice_id` / `target_url` | Lineage |
|
|
287
|
+
|
|
288
|
+
**Throws**: `TxnodEventNotFoundError`.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## Sandbox surface (`client.sandbox.*`)
|
|
293
|
+
|
|
294
|
+
Lazy accessor (`client.sandbox` is constructed on first use; bundlers tree-shake the entire namespace when never referenced) onto the 14 sandbox-mode endpoints introduced by Story 37.2. Use it from a sandbox-API-secret-authenticated client (`apiSecret: 'sk_sandbox_...'` + `environment: 'non-production'`) — production secrets cannot reach these routes (the server returns `production_key_against_sandbox_project`). See [`../05-sandbox.md`](../05-sandbox.md) for the lifecycle picture and per-method worked examples.
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
const client = new TxnodClient({
|
|
298
|
+
projectId: process.env.TXNOD_SANDBOX_PROJECT_ID!,
|
|
299
|
+
apiSecret: process.env.TXNOD_SANDBOX_API_SECRET!, // sk_sandbox_*
|
|
300
|
+
environment: 'non-production',
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Lazy — TxnodClientSandbox constructed on first access.
|
|
304
|
+
const r = await client.sandbox.simulatePaid('01HK8MAR2QEXAMPLE000000000');
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**Method surface:**
|
|
308
|
+
|
|
309
|
+
| Method | HTTP | Returns | Throws |
|
|
310
|
+
|---|---|---|---|
|
|
311
|
+
| `simulateDetect(invoiceId, opts?)` | `POST /api/v1/sandbox/invoices/{invoiceId}/simulate-detect` | `{ event_id, status: 'detected' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `pending`), `TxnodSandboxInvoiceNotFoundError` |
|
|
312
|
+
| `simulatePaid(invoiceId, opts?)` | `POST .../simulate-paid` | `{ event_id, status: 'paid' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `detected`) |
|
|
313
|
+
| `simulateOverpaid(invoiceId, params)` | `POST .../simulate-overpaid` | `{ event_id, status: 'paid' }` | `TxnodSandboxInvoiceTransitionInvalidError` |
|
|
314
|
+
| `simulatePartial(invoiceId, params)` | `POST .../simulate-partial` | `{ event_id, status: 'detected' }` | `TxnodSandboxInvoiceTransitionInvalidError` |
|
|
315
|
+
| `simulateExpire(invoiceId)` | `POST .../simulate-expire` | `{ event_id, status: 'expired' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice already terminal) |
|
|
316
|
+
| `simulateLatePayment(invoiceId, opts?)` | `POST .../simulate-late-payment` | `{ event_id, status: 'expired_paid_late' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `expired`) |
|
|
317
|
+
| `simulateReorg(invoiceId)` | `POST .../simulate-reorg` | `{ event_id, status: 'reverted' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `paid`/`overpaid`/`partial`) |
|
|
318
|
+
| `simulateReconfirm(invoiceId)` | `POST .../simulate-reconfirm` | `{ event_id, status: 'paid' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `reverted`) |
|
|
319
|
+
| `simulateDuplicateDelivery(invoiceId)` | `POST .../simulate-duplicate-delivery` | `{ event_id }` | `TxnodSandboxInvoiceTerminalError` (no terminal event yet) |
|
|
320
|
+
| `simulateEvent(invoiceId, eventInput)` | `POST .../simulate-event` | `{ event_id, status }` | `TxnodSandboxInvoiceTransitionInvalidError` (current status != `expectedCurrentStatus`) |
|
|
321
|
+
| `clockAdvance(projectId, params)` | `POST /api/v1/sandbox/{projectId}/clock/advance` | `{ advanced, remaining }` | `TxnodSandboxRateLimitExceededError` (>10/min/project) |
|
|
322
|
+
| `reset(projectId)` | `POST /api/v1/sandbox/{projectId}/reset` | `{ status: 'reset' }` | `TxnodSandboxResetFailedError` |
|
|
323
|
+
| `destroy(projectId)` | `DELETE /api/v1/sandbox/{projectId}` | `{ status: 'deleted' }` | `TxnodSandboxDeleteFailedError` |
|
|
324
|
+
| `listWallets(projectId)` | `GET /api/v1/sandbox/{projectId}/wallets` | `WalletsListResponse` | (read-only; no transition errors) |
|
|
325
|
+
|
|
326
|
+
Every `simulate*` and lifecycle method can additionally throw `TxnodSandboxInvoiceNotFoundError` (cross-tenant invoice id), `TxnodSandboxRateLimitExceededError` (per-project caps), and the auth/identity family. The full sandbox error catalogue lives in [`../05-errors.md`](../05-errors.md#sandbox-lifecycle--simulation).
|
|
327
|
+
|
|
328
|
+
**Result envelope types** (SDK-only — these aren't exported from `@txnod/shared`):
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
interface SandboxSimulateResult {
|
|
332
|
+
event_id: string | null; // outbox row id; null when no webhook is enqueued (e.g. simulateDuplicateDelivery re-emits an existing event)
|
|
333
|
+
status: string; // resulting invoice status — narrowed per-method in the table above
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
interface SandboxClockAdvanceResult {
|
|
337
|
+
advanced: number; // count of detected→terminal transitions this call performed
|
|
338
|
+
remaining: number; // detected invoices still under threshold after the bump
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
interface SandboxLifecycleResult {
|
|
342
|
+
status: 'reset' | 'deleted'; // discriminant for which lifecycle op completed
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Each method's worked example lives on its JSDoc — IDE hovers surface the full snippet (e.g. hover `client.sandbox.simulatePaid` to see the imports + a runnable invocation).
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
# `verifyWebhookSignature`
|
|
351
|
+
|
|
352
|
+
Synchronously verifies an inbound webhook, parses the body, and returns the typed `WebhookEvent`. See [`../04-webhooks.md`](../04-webhooks.md).
|
|
353
|
+
|
|
354
|
+
**Signature**
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
function verifyWebhookSignature(
|
|
358
|
+
headers: Headers | Record<string, string> | Record<string, string[]>,
|
|
359
|
+
rawBody: string,
|
|
360
|
+
secret: string,
|
|
361
|
+
): WebhookEvent
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Throws** (all are `TxnodError` subclasses):
|
|
365
|
+
|
|
366
|
+
- `TxnodSignatureFormatError` — header absent or not `t=<int>,v1=<hex>`
|
|
367
|
+
- `TxnodTimestampError` — `|now - t| > 300` seconds; `.skew_seconds` is signed delta
|
|
368
|
+
- `TxnodHmacError` — computed HMAC mismatch
|
|
369
|
+
- `TxnodWebhookPayloadParseError` — HMAC passed but body is not valid JSON; `.cause` carries the underlying `SyntaxError`. Treat as 401-class (do not parse, do not act)
|
|
370
|
+
|
|
371
|
+
**Example**
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
const event = verifyWebhookSignature(
|
|
375
|
+
request.headers,
|
|
376
|
+
await request.text(),
|
|
377
|
+
process.env.TXNOD_WEBHOOK_SECRET!,
|
|
378
|
+
);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
# Named exports
|
|
384
|
+
|
|
385
|
+
Also exported from `@txnod/sdk`:
|
|
386
|
+
|
|
387
|
+
- `PACKAGE_NAME` — the literal `'@txnod/sdk'`.
|
|
388
|
+
- `SDK_VERSION` — the current version string, in sync with `package.json`.
|
|
389
|
+
- Type re-exports: `Coin`, `Chain`, `Network`, `InvoiceStatus`, `WebhookEvent`, `WebhookEventType` (definitions are fully expanded plain-TypeScript aliases inside this tarball — no `zod`, no `@txnod/*` needed at install or typecheck time). Use them when annotating wrappers around `createInvoice` / `searchInvoices` so the union literals stay in sync with the SDK.
|
|
390
|
+
- `verifyAddress(input)` and the corresponding `VerifyAddressInput` / `VerificationResult` types — invoked automatically by `createInvoice` when the matching `TXNOD_<chain>_XPUB` env var is set, but exported so partners can run the same check on a stored invoice off-flow.
|
|
391
|
+
- `AddressVerificationError` — thrown by `verifyAddress` (and by `createInvoice` post-response) on mismatch. Not a `TxnodError` subclass; carries `expected_address` / `derived_address`.
|
|
392
|
+
- Every `Txnod*` error class — see [`errors.md`](./errors.md).
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Error class catalogue"
|
|
3
|
+
description: "Complete error_code → class lookup and the narrative groups."
|
|
4
|
+
sdk_version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Error class catalogue
|
|
8
|
+
|
|
9
|
+
All classes extend `TxnodError` and are exported from `@txnod/sdk`. The base class carries `error_code`, `status`, `request_id`, and the full RFC 7807 `raw` envelope. See [`../05-errors.md`](../05-errors.md) for usage patterns.
|
|
10
|
+
|
|
11
|
+
## `error_code` → class
|
|
12
|
+
|
|
13
|
+
The 55-row lookup below covers every server-emitted code in `ERROR_CODES` (58 entries, see [`packages/shared/src/errors/error-codes.ts`](../../../shared/src/errors/error-codes.ts)) that the SDK can surface. Three server-only auth codes (`otp_expired` / `otp_used` / `invite_invalid`) are intentionally not mapped — they only fire from dashboard auth/invite routes that no `TxnodClient` method can reach. If a server bug leaks one of those, `parseProblemDetails` falls through to a generic `TxnodError` via the `degraded()` branch.
|
|
14
|
+
|
|
15
|
+
### Validation / config
|
|
16
|
+
|
|
17
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| `validation_error` | `TxnodValidationError` | 400 | `err.raw.errors` carries per-field issues |
|
|
20
|
+
| `invalid_coin` | `TxnodInvalidCoinError` | 400 | |
|
|
21
|
+
| `invalid_xpub_format` | `TxnodInvalidXpubFormatError` | 400 | |
|
|
22
|
+
| `invalid_webhook_url` | `TxnodInvalidWebhookUrlError` | 400 | |
|
|
23
|
+
| `coin_not_enabled` | `TxnodCoinNotEnabledError` | 422 | Check enabled coins via `getRates()` |
|
|
24
|
+
| `amount_out_of_range` | `TxnodAmountOutOfRangeError` | 422 | Respect project min/max |
|
|
25
|
+
| `xpub_not_verified` | `TxnodXpubNotVerifiedError` | 409 | Complete verification challenge first |
|
|
26
|
+
| `invoice_not_cancellable` | `TxnodInvoiceNotCancellableError` | 409 | |
|
|
27
|
+
| `invalid_state_transition` | `TxnodInvalidStateTransitionError` | 409 | |
|
|
28
|
+
|
|
29
|
+
### Auth / identity
|
|
30
|
+
|
|
31
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
32
|
+
|---|---|---|---|
|
|
33
|
+
| `auth_invalid` | `TxnodAuthInvalidError` | 401 | Missing / wrong headers |
|
|
34
|
+
| `signature_invalid` | `TxnodSignatureInvalidError` | 401 | HMAC mismatch |
|
|
35
|
+
| `signature_replayed` | `TxnodSignatureReplayedError` | 401 | Same `(timestamp, signature)` seen before — caller must mint a fresh nonce per attempt |
|
|
36
|
+
| `timestamp_out_of_window` | `TxnodTimestampOutOfWindowError` | 401 | Fix NTP |
|
|
37
|
+
| `key_suspended` | `TxnodKeySuspendedError` | 403 | Operator action |
|
|
38
|
+
| `key_revoked` | `TxnodKeyRevokedError` | 403 | Rotate to new key |
|
|
39
|
+
| `project_suspended` | `TxnodProjectSuspendedError` | 403 | Operator action |
|
|
40
|
+
| `permission_denied` | `TxnodPermissionDeniedError` | 403 | Missing capability |
|
|
41
|
+
| `subscription_expired` | `TxnodSubscriptionExpiredError` | 402 | Operator subscription not `active`; writes blocked. Renew via dashboard `/billing` |
|
|
42
|
+
|
|
43
|
+
### Not found
|
|
44
|
+
|
|
45
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
46
|
+
|---|---|---|---|
|
|
47
|
+
| `invoice_not_found` | `TxnodInvoiceNotFoundError` | 404 | |
|
|
48
|
+
| `project_not_found` | `TxnodProjectNotFoundError` | 404 | |
|
|
49
|
+
| `wallet_not_found` | `TxnodWalletNotFoundError` | 404 | |
|
|
50
|
+
| `orphan_not_found` | `TxnodOrphanNotFoundError` | 404 | |
|
|
51
|
+
| `event_not_found` | `TxnodEventNotFoundError` | 404 | |
|
|
52
|
+
|
|
53
|
+
### Conflict / idempotent
|
|
54
|
+
|
|
55
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `external_id_conflict` | `TxnodExternalIdConflictError` | 409 | **Idempotent** — fetch existing |
|
|
58
|
+
| `orphan_already_attributed` | `TxnodOrphanAlreadyAttributedError` | 409 | **Idempotent** — treat as success |
|
|
59
|
+
| `wallet_not_bound` | `TxnodWalletNotBoundError` | 422 | Operator must bind a verified wallet of matching kind for the requested chain before invoice creation succeeds |
|
|
60
|
+
| `wallet_not_owned` | `TxnodWalletNotOwnedError` | 403 | **Dashboard-only** — never reaches partner API |
|
|
61
|
+
| `wallet_has_active_bindings` | `TxnodWalletHasActiveBindingsError` | 409 | **Dashboard-only** — never reaches partner API |
|
|
62
|
+
| `wallet_kind_mismatch` | `TxnodWalletKindMismatchError` | 422 | **Dashboard-only** — cross-kind bind attempt blocked |
|
|
63
|
+
|
|
64
|
+
### Chain-runtime — TRON / TON
|
|
65
|
+
|
|
66
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| `tron_no_activated_addresses_available` | `TxnodTronNoActivatedAddressesError` | 422 | TRON pool has zero activated rows; `.walletId` carries the operator wallet id. Operator-action-required |
|
|
69
|
+
| `ton_operator_wallet_not_deployed` | `TxnodTonOperatorWalletNotDeployedError` | 422 | TON operator wallet has not been broadcast on-chain yet — operator must perform the first send to deploy `StateInit` |
|
|
70
|
+
| `ton_invalid_wallet_version` | `TxnodTonInvalidWalletVersionError` | 422 | Configured `TXNOD_TON_WALLET_VERSION` does not match the deployed wallet contract |
|
|
71
|
+
| `ton_jetton_resolve_failed` | `TxnodTonJettonResolveFailedError` | 503 | Toncenter/TONAPI hiccup resolving the jetton master/wallet pair — retry with backoff |
|
|
72
|
+
| `ton_comment_parse_failed` | `TxnodTonCommentParseFailedError` | 422 | Inbound TON tx carried a malformed `payment_token` comment |
|
|
73
|
+
|
|
74
|
+
### TonConnect (operator-onboarding)
|
|
75
|
+
|
|
76
|
+
These never reach partner methods — they fire on the operator's TonConnect signing flow during xpub onboarding. Exported so the dashboard's typed-error catch can branch correctly.
|
|
77
|
+
|
|
78
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| `tonconnect_payload_expired` | `TxnodTonConnectPayloadExpiredError` | 400 | Challenge payload signed past TTL |
|
|
81
|
+
| `tonconnect_payload_unknown` | `TxnodTonConnectPayloadUnknownError` | 400 | Replay or stale browser tab |
|
|
82
|
+
| `tonconnect_domain_mismatch` | `TxnodTonConnectDomainMismatchError` | 400 | `proof.domain.value` vs `TXNOD_TONCONNECT_DOMAIN` mismatch |
|
|
83
|
+
| `tonconnect_timestamp_skew` | `TxnodTonConnectTimestampSkewError` | 400 | Wallet clock skew outside window |
|
|
84
|
+
| `tonconnect_unknown_wallet_version` | `TxnodTonConnectUnknownWalletVersionError` | 400 | Unrecognised `walletStateInit` version |
|
|
85
|
+
| `tonconnect_signature_invalid` | `TxnodTonConnectSignatureInvalidError` | 400 | Ed25519 verification failed |
|
|
86
|
+
| `tonconnect_network_mismatch` | `TxnodTonConnectNetworkMismatchError` | 400 | Wallet on `-3` (testnet) vs server-configured mainnet, or vice versa |
|
|
87
|
+
|
|
88
|
+
### Sandbox (lifecycle + simulation)
|
|
89
|
+
|
|
90
|
+
Surfaced by `/api/v1/sandbox/*` REST endpoints, the 14 `client.sandbox.*` SDK methods, and the `sandbox:simulate`-scoped MCP tools.
|
|
91
|
+
|
|
92
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
93
|
+
|---|---|---|---|
|
|
94
|
+
| `sandbox_project_required` | `TxnodSandboxProjectRequiredError` | 403 | Sandbox endpoint hit against a `kind: 'production'` project |
|
|
95
|
+
| `sandbox_per_operator_cap_reached` | `TxnodSandboxPerOperatorCapReachedError` | 422 | Operator at the per-operator sandbox-project cap |
|
|
96
|
+
| `sandbox_key_against_production_project` | `TxnodSandboxKeyAgainstProductionProjectError` | 400 | `sk_sandbox_` secret used against a production project |
|
|
97
|
+
| `production_key_against_sandbox_project` | `TxnodProductionKeyAgainstSandboxProjectError` | 400 | Production secret used against a sandbox project |
|
|
98
|
+
| `sandbox_provisioning_failed` | `TxnodSandboxProvisioningFailedError` | 500 | Server failed to provision the sandbox shell — retry |
|
|
99
|
+
| `sandbox_invoice_transition_invalid` | `TxnodSandboxInvoiceTransitionInvalidError` | 422 | `simulate*` against an invoice in a state that disallows the transition |
|
|
100
|
+
| `sandbox_invoice_not_found` | `TxnodSandboxInvoiceNotFoundError` | 404 | Sandbox invoice not in this sandbox project (oracle-safe across miss reasons) |
|
|
101
|
+
| `sandbox_invoice_terminal` | `TxnodSandboxInvoiceTerminalError` | 422 | `simulateDuplicateDelivery` requires a prior terminal webhook |
|
|
102
|
+
| `sandbox_rate_limit_exceeded` | `TxnodSandboxRateLimitExceededError` | 429 | Per-project sandbox cap hit (e.g. `clockAdvance` ≤ 10/min) |
|
|
103
|
+
| `sandbox_reset_failed` | `TxnodSandboxResetFailedError` | 500 | `client.sandbox.reset` failed mid-purge — retry |
|
|
104
|
+
| `sandbox_delete_failed` | `TxnodSandboxDeleteFailedError` | 500 | `client.sandbox.destroy` failed mid-delete — retry |
|
|
105
|
+
| `sandbox_active_invoice_cap_reached` | `TxnodSandboxActiveInvoiceCapReachedError` | 422 | Sandbox project has too many concurrently-open invoices — finalise or expire some first |
|
|
106
|
+
|
|
107
|
+
### Transient
|
|
108
|
+
|
|
109
|
+
| `error_code` | Class | HTTP status | Notes |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `rate_limit_exceeded` | `TxnodRateLimitError` | 429 | `.retry_after_seconds` |
|
|
112
|
+
| `pool_exhausted` | `TxnodPoolExhaustedError` | 503 | `.retry_after_seconds` — hard-cap hit, wait for cooldown |
|
|
113
|
+
| `webhook_capacity_exhausted` | `TxnodWebhookCapacityExhaustedError` | 503 | `.retry_after_seconds` always `0` — partner retry alone won't free capacity, treat as transient outage |
|
|
114
|
+
| `internal_error` | `TxnodServerError` | 5xx | Log `request_id`, retryable |
|
|
115
|
+
|
|
116
|
+
### SDK-local synthesised (no `error_code` — init-time / verify-time guards)
|
|
117
|
+
|
|
118
|
+
These have no `error_code` in the runtime `CODE_TO_CTOR` map — they synthesise a `ProblemDetails` envelope locally so the `instanceof TxnodError` invariant holds. Catch them around `new TxnodClient(...)`, `parseXpubConfig(...)`, and `verifyWebhookSignature(...)`.
|
|
119
|
+
|
|
120
|
+
The `Synthetic status` column carries a representative HTTP code so the envelope shape stays uniform with server-emitted errors — no HTTP exchange actually happens for the init-time classes (they fire before any request leaves the process); for the verify-time webhook trio, 401 reflects the canonical "treat as unauthenticated" disposition partners should map them to.
|
|
121
|
+
|
|
122
|
+
| Class | `kind` | Synthetic status | When |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| `TxnodSignatureFormatError` | `signature_format` | 401 | `verifyWebhookSignature` — header missing or not `t=<int>,v1=<hex>` |
|
|
125
|
+
| `TxnodHmacError` | `hmac` | 401 | `verifyWebhookSignature` — computed HMAC mismatch |
|
|
126
|
+
| `TxnodTimestampError` | `timestamp` | 401 | `verifyWebhookSignature` — `\|now − t\| > 300`; `.skew_seconds` exposes signed delta |
|
|
127
|
+
| `TxnodWebhookPayloadParseError` | `webhook_payload_parse` | 401 | `verifyWebhookSignature` — HMAC passed but body is not valid JSON; `.cause` is the underlying `SyntaxError` |
|
|
128
|
+
| `TxnodSandboxKeyInProductionError` | `sandbox_key_in_production` | 401 | `new TxnodClient(...)` — `sk_sandbox_` secret with `environment: 'production'` (or production-detected `NODE_ENV`); `.signal`, `.secretPrefix`, `.detectedEnvironment` |
|
|
129
|
+
| `TxnodEnvironmentUnknownError` | `environment_unknown` | 401 | `new TxnodClient(...)` — `sk_sandbox_` secret without explicit `environment` and `NODE_ENV` unset/unknown |
|
|
130
|
+
| `TxnodSandboxXpubInProductionError` | `sandbox_xpub_in_production` | 401 | `parseXpubConfig` / `parseTonConfig` — testnet xpub loaded for a chain whose env-detected mode is production; `.chain`, `.xpubPrefix`, `.detectedEnvironment` |
|
|
131
|
+
|
|
132
|
+
## Fields on specific classes
|
|
133
|
+
|
|
134
|
+
- `TxnodRateLimitError.retry_after_seconds: number` — value of the `Retry-After` header (0 when absent).
|
|
135
|
+
- `TxnodPoolExhaustedError.retry_after_seconds: number` — value of the `Retry-After` header (0 when absent). Server derives it from the soonest `locked_until` across locked pool rows, falling back to the project's configured cooldown.
|
|
136
|
+
- `TxnodWebhookCapacityExhaustedError.retry_after_seconds: number` — always `0`. The server cannot tell when the operator will add a webhook shard; the actionable signal is the operator-side admin alert raised on every rejection, not partner-side retry timing. `.kind` carries the `error_code` literal; the underlying `chain` is also surfaced in the problem-details body.
|
|
137
|
+
- `TxnodTimestampError.skew_seconds: number` — `now - t`; negative means the webhook timestamp is in the future.
|
|
138
|
+
- `TxnodValidationError.raw.errors` — server-emitted per-field Zod issues.
|
|
139
|
+
- `TxnodTronNoActivatedAddressesError.walletId: string` — operator wallet id (ULID, guaranteed non-empty). The constructor fails fast if the server omits it. Use it to deep-link the operator into `/wallets/{walletId}` for in-dashboard activation.
|
|
140
|
+
|
|
141
|
+
## Narrative grouping
|
|
142
|
+
|
|
143
|
+
Grouped by branching strategy in [`../05-errors.md`](../05-errors.md):
|
|
144
|
+
|
|
145
|
+
- Fatal — fix inputs / configuration
|
|
146
|
+
- Fatal — auth / identity
|
|
147
|
+
- Fatal — not found
|
|
148
|
+
- Idempotent — treat as success
|
|
149
|
+
- Transient — retry
|
|
150
|
+
- TON-chain runtime
|
|
151
|
+
- TonConnect operator-onboarding
|
|
152
|
+
- Sandbox lifecycle + simulation
|
|
153
|
+
- SDK-local synthesised (init-time guards)
|
|
154
|
+
- Dashboard-only (exported but not reachable from `TxnodClient`)
|
|
155
|
+
- Webhook-local (thrown by `verifyWebhookSignature` only)
|
|
156
|
+
|
|
157
|
+
## Related
|
|
158
|
+
|
|
159
|
+
- [`../05-errors.md`](../05-errors.md) — branching patterns
|
|
160
|
+
- [`../01-authentication.md`](../01-authentication.md) — auth-family context
|
|
161
|
+
- [`../04-webhooks.md`](../04-webhooks.md) — webhook-local errors
|