Server SDK
@tbookdev/vault-node is the backend companion: webhook signature verification plus a typed client for the REST API.
:::info SDK release status
Published on the public npm registry — install with npm install @tbookdev/vault-node. The REST contract is normative.
:::
Verify webhooks
import { TBookVaultServer } from "@tbookdev/vault-node";
const tbook = new TBookVaultServer({
secretKey: process.env.TBK_SECRET_KEY, // "apiKey" is accepted as an alias
webhookSecret: process.env.WHSEC, // only needed if you verify inbound events
});
app.post("/tbook", express.raw({ type: "*/*" }), (req, res) => {
const event = tbook.webhooks.verify(req.body, req.header("X-TBook-Signature"));
queue.push(event); // process async, keyed on event.id
res.sendStatus(200);
});
Verification implements the t=…,v1=… HMAC scheme — details and a raw-Python equivalent in Webhooks.
Typed API client
Every method maps 1:1 to a REST endpoint and adds nothing the REST API cannot do. The namespaces are accounts (create / get / list / update), transactions (buildDeposit / buildRedeem / buildClaim / buildCancelDeposit / confirm / get), and positions (list / earnings / transactions / portfolio), plus catalog reads (listAssets, listVaults, getVaultInfo, getSharePrice) and webhook management (registerWebhook, listWebhooks, removeWebhook, pollEvents).
const account = await tbook.accounts.create({
externalId: "user-8492",
type: "individual",
custody: "waas",
wallets: [{ chain: "sui", address: "0x…" }],
compliance: { kyc: { attested: true, provider: "your-kyc", ref: "kyc_123" } },
});
const built = await tbook.transactions.buildDeposit({
vaultId: "rcusdp-sui",
accountId: account.accountId,
amount: "100.00",
});
// sign built.transaction.data with YOUR signer, submit on-chain, then bind the digest:
await tbook.transactions.confirm(built.intentId!, { digest });
const positions = await tbook.positions.list(account.accountId);
Idempotency, retries, errors
Money-movement calls are idempotent by default: the SDK attaches an Idempotency-Key header (auto-generated when you don't pass one), reuses the same key across internal retries, and echoes the key used on the result as idempotencyKey. To control the key yourself, pass it as per-call options:
await tbook.transactions.buildDeposit(
{ vaultId: "rcusdp-sui", accountId, amount: "100.00" },
{ idempotencyKey: "dep-user-8492-2026-07-05" },
);
Requests time out client-side after 30 s and retry twice on network errors, timeouts, 429, and 5xx, so a retried call can never double-move funds. The one 4xx the SDK retries (≥ 0.2.1) is 409 idempotency_in_progress — a concurrent request holds the same Idempotency-Key — which it retries with the same key, honoring Retry-After, and returns the replayed response; other 4xx are never retried. Both defaults are configurable via the constructor's timeoutMs / retries. API failures throw TBookApiError (with status, code, message, requestId parsed from the error envelope); transport failures throw TBookNetworkError.
For defense in depth, assertSuiTransaction / decodeSuiTransactionSummary decode an unsigned Sui transaction returned by a build call and assert it does what you expect before you sign it (requires the optional @mysten/sui peer dependency).