Skip to Content
SDKclient.sandbox.* surface

client.sandbox.* surface

The SDK exposes 14 sandbox methods on client.sandbox.*, mirroring the Sandbox simulation API one-to-one. Constructed lazily — bundlers tree-shake the entire namespace when never referenced. Each method signs requests with the same HMAC scheme as the rest of the SDK and throws typed TxnodSandbox* errors per spec §7.1.

AI coding agents: a full, version-matched copy of this page and every other SDK guide ships inside the @txnod/sdk tarball at node_modules/@txnod/sdk/docs/05-sandbox.md, with an entry point at node_modules/@txnod/sdk/AGENTS.md. Prefer those offline files after install — they need no network access.

Construction

import { TxnodClient } from '@txnod/sdk'; const client = new TxnodClient({ projectId: process.env.TXNOD_PROJECT_ID!, apiSecret: process.env.TXNOD_API_SECRET!, // sk_sandbox_... environment: 'non-production', // explicit override; trusts NODE_ENV otherwise });

The environment option ('production' | 'non-production') takes precedence over TXNOD_ENVIRONMENT and NODE_ENV. Use 'non-production' for tests and staging-replicas.

iAcknowledgeRoutingRealCustomerFundsToSandboxAddresses

The escape-hatch constructor option. Default behaviour — when the SDK detects NODE_ENV === 'production' AND the configured secret starts with sk_sandbox_ — is to throw TxnodSandboxKeyInProductionError at construction and refuse to instantiate. Setting iAcknowledgeRoutingRealCustomerFundsToSandboxAddresses: true bypasses that hard-fail.

The only legitimate case for the override is a staging-replica that mirrors production env-vars (e.g. NODE_ENV=production) but is wired to a sandbox project for safe testing. Even with the override:

  • The SDK emits a non-suppressible console.error on every constructor invocation describing the misconfiguration.
  • Every outbound API request carries an X-Txnod-Client-Environment: production header, surfacing the misuse in server-side admin telemetry.
  • The override option name is intentionally verbose so it remains awkward to type — renaming requires a major-version bump and a deprecation cycle.
import { TxnodClient } from '@txnod/sdk'; // Staging-replica: NODE_ENV=production but secret is sandbox. const client = new TxnodClient({ projectId: process.env.TXNOD_PROJECT_ID!, apiSecret: process.env.TXNOD_API_SECRET!, iAcknowledgeRoutingRealCustomerFundsToSandboxAddresses: true, });

If you find yourself reaching for this in normal application code, the right fix is almost always environment: 'non-production' instead.

client.sandbox.simulateDetect(invoiceId, opts?)

Synthesizes invoice.detected (status pending → detected).

import type { TxnodClient } from '@txnod/sdk'; declare const client: TxnodClient; const result = await client.sandbox.simulateDetect( '01HK8MAR2QEXAMPLE000000000', { seed: 'order-42' }, ); // result.event_id: ULID // result.status: 'detected'

Throws: TxnodSandboxInvoiceNotFoundError, TxnodSandboxInvoiceTransitionInvalidError, TxnodSandboxRateLimitExceededError.

client.sandbox.simulatePaid(invoiceId)

Walks the invoice from detected → paid. Returns { event_id, status: 'paid' }. Throws the same error classes as simulateDetect.

client.sandbox.simulateOverpaid(invoiceId, params)

Walks from detected → paid (overpaid envelope). params is exactly one of { multiplier: number } or { extra_units: string }.

import type { TxnodClient } from '@txnod/sdk'; declare const client: TxnodClient; declare const invoiceId: string; await client.sandbox.simulateOverpaid(invoiceId, { multiplier: 1.5 });

Throws: TxnodSandboxInvoiceNotFoundError, TxnodSandboxInvoiceTransitionInvalidError, TxnodValidationError, TxnodSandboxRateLimitExceededError.

client.sandbox.simulatePartial(invoiceId, params)

Emits invoice.partial; invoice stays in detected. params is exactly one of { fraction: number } or { amount_units: string }.

import type { TxnodClient } from '@txnod/sdk'; declare const client: TxnodClient; declare const invoiceId: string; await client.sandbox.simulatePartial(invoiceId, { fraction: 0.5 });

Throws: same as simulateOverpaid.

client.sandbox.simulateExpire(invoiceId)

Forces expired. Returns { event_id, status: 'expired' }.

Throws: TxnodSandboxInvoiceNotFoundError, TxnodSandboxInvoiceTransitionInvalidError, TxnodSandboxRateLimitExceededError.

client.sandbox.simulateLatePayment(invoiceId, opts?)

Followup to simulateExpire. Walks expired → expired_paid_late.

Throws: same as simulateExpire.

client.sandbox.simulateReorg(invoiceId)

Walks a terminal-paid invoice (paid / overpaid / partial) into reverted. Emits invoice.reverted.

Throws: same as simulateExpire.

client.sandbox.simulateReconfirm(invoiceId)

After a reorg, fresh paid event with a stable, deterministic event_id (not a fresh ULID — exercises the dispatcher’s idempotent re-emission contract).

Throws: TxnodSandboxInvoiceNotFoundError, TxnodSandboxInvoiceTransitionInvalidError (invoice not in reverted), TxnodSandboxRateLimitExceededError.

client.sandbox.simulateDuplicateDelivery(invoiceId)

Re-fires the most recent terminal webhook for this invoice with the SAME event_id — exercises the integrator’s idempotency layer.

Throws: TxnodSandboxInvoiceNotFoundError, TxnodSandboxInvoiceTerminalError (no terminal event yet), TxnodSandboxRateLimitExceededError.

client.sandbox.simulateEvent(invoiceId, eventInput)

Low-level escape hatch. eventInput carries { event_type, amount_units, confirmations, block_height, chain_specific?, expected_current_status, seed? }. Returns { event_id, status }.

Throws: TxnodSandboxInvoiceNotFoundError, TxnodSandboxInvoiceTransitionInvalidError (current status ≠ expectedCurrentStatus), TxnodValidationError, TxnodSandboxRateLimitExceededError.

client.sandbox.clockAdvance(projectId, params)

Per-project: increments the per-chain confirmation counter for every detected invoice. params is { chain, blocks } (1 ≤ blocks ≤ 64). Per-chain rate-limited at 10 calls/min/project.

import type { TxnodClient } from '@txnod/sdk'; declare const client: TxnodClient; declare const projectId: string; const r = await client.sandbox.clockAdvance(projectId, { chain: 'btc', blocks: 6, }); // r.advanced: number, r.remaining: number

Throws: TxnodSandboxProjectRequiredError, TxnodSandboxKeyAgainstProductionProjectError, TxnodProductionKeyAgainstSandboxProjectError, TxnodValidationError, TxnodSandboxRateLimitExceededError.

client.sandbox.reset(projectId)

Soft-purges the project’s data tail (invoices, transactions, outbox events, address pool) while preserving the shell, xpubs, bindings, API key, and sandbox PAT. Use as beforeAll/afterAll in test suites.

Throws: TxnodSandboxProjectRequiredError, TxnodSandboxKeyAgainstProductionProjectError, TxnodProductionKeyAgainstSandboxProjectError, TxnodSandboxResetFailedError.

client.sandbox.destroy(projectId)

Cascade-deletes the entire sandbox project — shell, xpubs, bindings, API keys, sandbox PAT, all data tail. Irreversible.

Throws: TxnodSandboxProjectRequiredError, TxnodSandboxKeyAgainstProductionProjectError, TxnodProductionKeyAgainstSandboxProjectError, TxnodSandboxDeleteFailedError.

client.sandbox.listWallets(projectId)

Read-only listing of per-chain sandbox xpubs. Returns WalletsListResponse. Read-only — no transition errors fire.

Throws: TxnodSandboxProjectRequiredError, TxnodSandboxKeyAgainstProductionProjectError, TxnodProductionKeyAgainstSandboxProjectError.

Typed errors

Safety-class errors

These five typed errors implement the SDK’s structural defense layers per the Sandbox safety layered-defense model. Each is exported from @txnod/sdk; reference the SDK source for the canonical class shape:

Operational errors

These three typed errors fire during normal sandbox operation and are caught case-by-case in handler logic:

  • TxnodSandboxProjectRequiredError — endpoint reached with a non-sandbox project id.
  • TxnodSandboxInvoiceTransitionInvalidError — current invoice status does not permit the requested simulate-* call.
  • TxnodSandboxRateLimitExceededError — per-project simulate-* rate cap exceeded.

Additional sandbox-specific operational errors: TxnodSandboxInvoiceNotFoundError, TxnodSandboxInvoiceTerminalError, TxnodSandboxResetFailedError, TxnodSandboxDeleteFailedError.