Skip to main content

Transactions

Money movement follows build → sign → submit → confirm. Build endpoints return an unsigned transaction plus an intentId; your wallet infrastructure signs and submits; TBook's indexer tracks the result through to settlement (see the intent lifecycle).

All build endpoints: secret or publishable key (contract style with accountId) · support Idempotency-Key · resolve accountId to the account's wallet on the vault's chain. An intentId is recorded only for authenticated calls — anonymous legacy-style calls get a transaction but no intent tracking and no idempotency dedupe.

Common build-time errors:

CodeWhen
invalid_request (400)Account has no wallet on the vault's chain
permission_denied (403)Account is frozen, or the required compliance.kyc/kyb attestation is missing before a first deposit
not_whitelisted (403)XAUa: wallet not yet whitelisted by the issuer (on-chain preflight at build time)
vault_paused (409)Vault paused — deposit/redeem rejected; claim and cancel-deposit stay open
insufficient_balance (400)Wallet lacks the USDC/shares/gas needed to build the transaction, or nothing is claimable

(cycle_closed is defined in the error contract but is not currently emitted — a cycle-window conflict surfaces as an on-chain abort at execution; the transaction never lands, so the intent simply never settles. Use the on-chain result and GET /v1/accounts/:id/positions as the source of truth.)

Common response shape (all four build endpoints):

FieldTypeNotes
intentIdstring?present on authenticated calls — track via lifecycle; appears in webhooks
vaultId, type, chain, railstringecho of what was built
transaction.formatenumsui:tx-bytes-base64 · solana:tx-base58
transaction.datastringthe unsigned transaction — sign and submit as-is
summaryobject?human-checkable amounts — see each endpoint for its fields; price-derived fields (sharePrice, estimatedShares, quotedAt) are best-effort and may be absent if the price source is briefly unavailable. quotedAt timestamps the shared price snapshot used — all instances quote from the same snapshot, so concurrent builds never disagree on price
messagestringhuman-readable description of the built transaction
expiresAttimestamp≈2 min on Solana (blockhash validity), ≈10 min on Sui. After expiry, rebuild with a new Idempotency-Key — the stale intent expires harmlessly

POST /v1/vaults/:vaultId/tx/deposit

Body: {accountId, amount}amount is a decimal string in the asset's currency, ≥ the vault's minDeposit.

On xaua-sui, a deposit is a gold subscription priced at the settlement cycle's NAV.

{
"intentId": "txi_01h…",
"vaultId": "rcusdp-sui", "type": "deposit", "chain": "sui", "rail": "direct",
"transaction": { "format": "sui:tx-bytes-base64", "data": "AAAC…" },
"summary": { "amount": "100", "sharePrice": "1.0275", "estimatedShares": "97.3236", "quotedAt": "2026-06-15T08:07:58Z" },
"message": "Deposit 100.000000 USDC into rcusdp-sui",
"expiresAt": "2026-06-15T08:10:00Z"
}

Extra errors: below_min_deposit (400).

POST /v1/vaults/:vaultId/tx/redeem

Body: {accountId, shares, mode}shares decimal string; mode is "queued" (default) or "instant".

  • Queued — enters the settlement cycle; on settlement the proceeds are paid directly to the account's wallet (transaction.redeem.settled with amount) on rcusdp-sui. Vaults that allocate a claimable balance instead signal it via claimable in the same event — see the event catalog.
  • Instant — only where the vault's catalog declares settlement.instantRedeem.available; pays USDC immediately, less the fee. Availability and fee are operator-tunable on-chain and the catalog values are indicative — attempt the build and handle rejection (fall back to queued), or read the on-chain vault state via @tbookdev/vault-sdk-sui.

The summary echoes {shares, mode}.

Extra errors: invalid_request if mode:"instant" is not available on this vault; insufficient_balance if the wallet holds fewer shares than requested.

POST /v1/vaults/:vaultId/tx/claim

Body: {accountId}. Builds a claim of the account's entire claimable balance, paid to the account's own wallet — an on-chain guarantee. Claim works even when a vault is paused.

Only relevant where a settlement allocated a claimable balance — rcusdp-sui currently pays redemption proceeds directly on settlement, so there is normally nothing to claim there.

Extra errors: insufficient_balance (nothing claimable).

POST /v1/vaults/:vaultId/tx/cancel-deposit

Body: {accountId, recordId}recordId comes from positions.pending.deposits[] and is required on the Sui rail (each pending record is cancelled individually). On the legacy Solana gateway rail the full pending deposit bucket is cancelled and recordId is ignored.

Fees: the vault's cancelFeeBps is withheld from the returned principal — 200 bps (2%) on rcusdp-sui (verified on-chain), 10 bps on the legacy rcusdp-sol rail. The response summary discloses {recordId?, cancelable, cancelFeeBps} plus a note: the on-chain returned amount is authoritative — reconcile against it, not the requested amount.

Extra errors: invalid_request (400) if the vault is not cancelable (xaua-sui declares cancelable: false) or if recordId is missing on the Sui rail.


POST /v1/transactions/:intentId/confirm

Report the chain result after you submit. Requires any valid API key.

Body: {digest} (Sui) or {signature} (Solana).

Response: {intentId, status} — the intent flips to submitted.

Optional but recommended: webhook correlation becomes exact. If you skip it, the indexer matches the transaction heuristically within about a minute.

Errors: not_found; conflict if the digest is already bound to a different intent.

GET /v1/transactions/:intentId

Requires any valid API key. Returns the full intent record:

{
"intentId": "txi_01h…", "status": "settled", "type": "deposit",
"vaultId": "rcusdp-sui", "accountId": "acct_7f3a…",
"chain": "sui", "walletAddress": "0xabc…",
"amount": "100", "shares": null, "mode": null,
"settlementCycle": 6, "digest": "8vJk…", "expiresAt": "…",
"createdAt": "…", "submittedAt": "…", "confirmedAt": "…", "settledAt": "…",
"failureReason": null
}

Webhooks

transaction.deposit.confirmedtransaction.deposit.settled (or .cancelled), transaction.redeem.confirmedtransaction.redeem.settled, transaction.claim.confirmed. Full catalog: Webhook events.