Project kinds
TxNod has a single discriminator per project — kind — and three legal values:
| Kind | Meaning | Where the chain lives |
|---|---|---|
production | Real money, mainnet RPC, mainnet derivation paths. | Bitcoin mainnet, Ethereum mainnet, Polygon PoS, BNB Smart Chain, TRON mainnet, Cardano mainnet, TON mainnet (chain id -239). |
testnet | Real testnet faucet money, testnet RPC, testnet derivation paths. | Bitcoin signet/testnet, Ethereum Sepolia, Polygon Amoy, BSC testnet, TRON Shasta/Nile, Cardano preprod/preview, TON testnet (chain id -3). |
sandbox | Fully simulated lifecycle, no chain-watcher subscription, auto-derived deterministic test wallets. Use the client.sandbox.* namespace and the /api/v1/sandbox/* REST surface. | None — simulation only. |
Kind is fixed at project create time and immutable for the project’s lifetime. To “promote” a testnet integration to production, create a fresh kind='production' project — there is no flag-flip.
The previous shape carried a separate network: 'mainnet' | 'testnet' field on every invoice request, response, wallet, and binding row. That field is gone everywhere. The kind→network mapping happens once, at the edge — provider URL selection, BIP-32 / CIP-19 version-byte derivation, TonConnect protocol message — and is never user-visible on the wire.
Implications for partner code
createInvoice no longer takes a network argument. The invoice’s underlying chain network is implied by which project the API credential is bound to:
import { TxnodClient } from '@txnod/sdk';
// Production credential → mainnet invoices.
const prod = new TxnodClient({
projectId: process.env.TXNOD_PROJECT_ID!,
apiSecret: process.env.TXNOD_API_SECRET!,
});
// Testnet credential → testnet invoices. Same code path, different secret.
const staging = new TxnodClient({
projectId: process.env.TXNOD_TESTNET_PROJECT_ID!,
apiSecret: process.env.TXNOD_TESTNET_API_SECRET!,
});
await prod.createInvoice({ amount_usd: 9.99, coin: 'btc', external_id: 'order-1' });
await staging.createInvoice({ amount_usd: 9.99, coin: 'btc', external_id: 'order-1' });InvoiceResponse likewise no longer carries a network field. If your webhook handler processes events from multiple projects (e.g. one production, one testnet), branch on the webhook envelope’s mode field — which extends to 'production' | 'testnet' | 'sandbox' — instead.
Cross-kind binding is rejected
Operator wallets carry their own kind (set when the wallet is registered through the dashboard’s Wallet wizard). Binding a wallet of one kind to a project of a different kind fails with a typed error:
| Kind on project | Kind on wallet | Result |
|---|---|---|
production | production | Bind succeeds. |
testnet | testnet | Bind succeeds. |
production | testnet | Server returns HTTP 422 error_code: 'wallet_kind_mismatch'. The SDK throws TxnodWalletKindMismatchError. |
testnet | production | Same — HTTP 422 wallet_kind_mismatch. |
sandbox | (any) | N/A — sandbox projects auto-derive their own deterministic test wallets at create time; the operator-binding surface never reaches sandbox projects. |
The 422 carries a human-readable detail you can surface verbatim in your dashboard / wizard UX. There is no automatic fallback — pick the right kind on both sides.
Subscription billing (txnod-self) is always production
The TxNod self-billing project (txnod-self) is permanently kind='production'. Subscription payments are real money; there is no “buy a testnet subscription” or “buy a sandbox subscription” path.
See also
- Quickstart — Switching to testnet for the staging wiring.
- Sandbox projects for the simulation-only
kind='sandbox'lifecycle. - SDK · Errors for the full typed-error catalog (
TxnodWalletKindMismatchError,TxnodWalletNotBoundError,TxnodTonConnectNetworkMismatchError, etc).