# Tesser Documentation > Complete documentation for Large Language Models --- ## Document: Treasury Updates Webhook events fired during the deposit, withdrawal, and rebalance lifecycle URL: /webhooks/treasury-updates # Treasury Updates ## Deposit Events | Event | Fired when | | --- | --- | | `deposit.quote_created` | Route planning is complete; execution steps are created with exchange rate info | | `deposit.updated` | General notification that a field on the deposit has been updated | | `deposit.expired` | The deposit reached its expiration date-time and is no longer actively processing | ## Withdrawal Events | Event | Fired when | | --- | --- | | `withdrawal.quote_created` | Route planning is complete; execution steps are created with exchange rate info | | `withdrawal.balance_updated` | Balance check result is available (`reserved` or `awaiting_funds`) | | `withdrawal.updated` | General notification that a field on the withdrawal has been updated | | `withdrawal.expired` | The withdrawal reached its expiration date-time and is no longer actively processing | ## Rebalance Events | Event | Fired when | | --- | --- | | `rebalance.quote_created` | Route planning is complete; execution steps are created with exchange rate info | | `rebalance.balance_updated` | Balance check result is available (`reserved` or `awaiting_funds`) | | `rebalance.updated` | General notification that a field on the rebalance has been updated | | `rebalance.expired` | The rebalance reached its expiration date-time and is no longer actively processing | ## Step Events All other lifecycle updates for a deposit, withdrawal, or rebalance fire at the step level as each step progresses: | Event | Fired when | | --- | --- | | `step.signature_requested` | Signature has been requested for the step | | `step.signed` | Step has been cryptographically signed | | `step.submitted` | Step has been submitted for execution | | `step.confirmed` | Step execution is confirmed on-chain or by the partner | | `step.completed` | Step finished successfully | | `step.failed` | Step encountered an error and could not complete | | `step.updated` | General notification that a field on the step has been updated | The `data.object` for step events is the same across all Treasury resources. See for example the [DepositStep](/api/~schemas#deposit-step) resource. ## Payload The `data.object` in each to-level Treasury event contains the full resource. For the complete field reference, see the [Deposit schema](/api/~schemas#Deposit), [Withdrawal schema](/api/~schemas#Withdrawal), and [Rebalance schema](/api/~schemas#rebalance). --- ## Document: Payment Updates Webhook events fired during the payment lifecycle URL: /webhooks/payment-updates # Payment Updates ## Event Types | Event | Fired when | | --- | --- | | `payment.created` | A payment has been created in Tesser's system | | `payment.quote_created` | Route planning is complete; execution steps are created with exchange rate info | | `payment.balance_updated` | Balance check result is available (`reserved` or `awaiting_funds`) | | `payment.risk_updated` | Risk status has changed (e.g. `auto_approved`, `awaiting_decision`, or `auto_rejected`) | | `payment.updated` | General notification that a field on the payment has been updated. | | `payment.expired` | The payment reached its expiration date-time and is no longer actively processing | For a detailed breakdown of what each event means in the context of a payout, see [Payment Workflow](/overviews/funds-movement-lifecycle-and-data-model). ## Step Events Once steps are created (`payment.quote_created`), individual step lifecycle events fire as each step progresses: | Event | Fired when | | --- | --- | | `step.signature_requested` | Signature has been requested for the step | | `step.signed` | Step has been cryptographically signed | | `step.submitted` | Step has been submitted for execution | | `step.confirmed` | Step execution is confirmed on-chain or by the partner | | `step.completed` | Step finished successfully | | `step.failed` | Step encountered an error and could not complete | | `step.updated` | General notification that a field on the step has been updated | The `data.object` for Payment step events is a [PaymentStep](/api/~schemas#payment-step) resource. ## Payload The `data.object` for each top-level Payment event contains the full payment resource, including its steps. For the complete field reference, see the [Payment schema](/api/~schemas#Payment). For a walkthrough of how these events appear during a real payout lifecycle, see [Create a Payout](/how-tos/send-a-stablecoin-payout/create-a-stablecoin-payout). ## Key Status Fields For details on how `risk_status` and `balance_status` evolve through a payout, see [Payout Workflow](/overviews/funds-movement-lifecycle-and-data-model). --- ## Document: General Common envelope structure shared by all Tesser webhook events URL: /webhooks/general # General All Tesser webhook events share a common envelope structure. The envelope wraps event-specific data in a consistent format so your handler can route and process events uniformly. ## Envelope Fields | Field | Type | Description | | --- | --- | --- | | `id` | string | Unique event identifier | | `type` | string | Event type in `scope.action` format (e.g. `payment.quote_created`) | | `created_at` | string | ISO 8601 timestamp of when the event was created | | `data` | object | Contains the event-specific payload under `data.object` | ## Event Type Format Event types follow the pattern **`scope.action`**. Current scopes are `payment`, `deposit`, `withdrawal`, `rebalance`, and `step`. See [Payment Updates](./payment-updates) and [Treasury Updates](./treasury-updates) for event details. ## Example Payload for a Payment event ```json { "id": "90ee0a64-e8e9-43c0-8a60-20cf0cecf58f", "type": "payment.quote_created", "created_at": "2025-12-01T09:00:00.045Z", "data": { "object": { "id": "550e8400-e29b-41d4-a716-446655440020", "workspace_id": "550e8400-e29b-41d4-a716-446655440001", "organization_reference_id": "ref_123", "direction": "outbound", "from_account_id": null, "funding_account_id": null, "to_account_id": null, "from_amount": "55.85", "from_currency": "USDC", "from_network": null, "to_amount": "1000", "to_currency": "MXN", "to_network": null, "risk_status": "unchecked", "risk_status_reasons": [], "risk_reviewed_by": null, "risk_reviewed_at": null, "balance_status": "unreserved", "balance_reserved_at": null, "steps": [], "created_at": "2025-12-01T09:00:00.000Z", "updated_at": "2025-12-01T09:00:00.040Z", "expires_at": "2025-12-01T23:59:59.999Z" } } } ``` --- ## Document: Webhook Authentication Verify that webhooks originate from Tesser using Ed25519 signatures URL: /webhooks/authentication # Webhook Authentication Tesser signs every outgoing webhook request with an **Ed25519** asymmetric signature. You should verify this signature on your server before processing the payload. ## Signature Header Each webhook request includes the following headers: | Header | Description | | --- | --- | | `X-Tesser-Signature` | Base64-encoded Ed25519 signature of the request body | | `Content-Type` | Always `application/json` | | `User-Agent` | `Tesser-Webhooks/1.0` | The signature is computed over the exact UTF-8 bytes of the JSON request body. Do not parse and re-serialize the body before verifying — use the raw bytes. ## Public Key Tesser's webhook public key is provided in **SPKI DER** format, base64-encoded. You can also import it from the SDK types package: ```ts import { WEBHOOK_PUBLIC_KEY } from "@tesser-payments/types"; ``` ## Verifying Signatures ```js import { createPublicKey, verify } from "node:crypto"; import { WEBHOOK_PUBLIC_KEY } from "@tesser-payments/types"; function verifyWebhook(rawBody, signature) { const publicKeyObj = createPublicKey({ key: Buffer.from(WEBHOOK_PUBLIC_KEY, "base64"), type: "spki", format: "der", }); return verify( null, Buffer.from(rawBody, "utf8"), publicKeyObj, Buffer.from(signature, "base64"), ); } // In your webhook handler: const signature = req.headers["x-tesser-signature"]; const isValid = verifyWebhook(req.rawBody, signature); if (!isValid) { return res.status(401).json({ error: "Invalid signature" }); } ``` ## Important Notes - Always verify using the **raw request body** bytes. Parsing the JSON and re-serializing may change whitespace or key order, which will invalidate the signature. - If verification fails, respond with `401` and do not process the event. - The public key may be rotated in the future. Key rotation will be announced in advance and communicated through the Tesser Dashboard. --- ## Document: Treasury Management Stablecoin balance and yield management URL: /overviews/treasury-management # Treasury Management Tesser provides comprehensive treasury management capabilities for stablecoins. > Note: Most treasury management features are currently under development. Contact us to learn more about our roadmap and early access programs. **Balance Management** Effective balance management ensures payments can be processed successfully: - **Balance checks:** Before payment processing, the system validates that the source treasury wallet has sufficient funds to cover the payment amount and associated network fees and processing costs - **Balance reconciliation:** Regular balance verification for wallets against blockchain state - **Balance monitoring:** Low balance alerts when balances fall below thresholds or based on typical usage - **Automatic rebalancing**: Rebalance across tokens and chains to maintain optimal distribution **Yield Generation** Generate yield on otherwise idle stablecoin balances: - **Multiple Providers**: Integrations with yield providers to offer a range of options - **Risk Spectrum**: From US treasury-backed yield (lower risk) to DeFi yield options (higher yield, higher risk) - **Policy Controls**: Enable yield only where allowed by your policy and risk appetite - **Operational Safety**: Emphasis on capital preservation and liquidity availability for payments **Treasury Analytics** Comprehensive reporting and analytics: - **Utilization Reports**: Track fund usage across corridors - **Yield Performance**: Monitor returns on treasury holdings - **Cash Flow Forecasting**: Predict funding needs based on historical patterns - **Cost Analysis**: Compare treasury costs across different strategies --- ## Document: Transfers Internal movement of funds URL: /overviews/transfers # Transfers A Transfer is a movement of funds on the Tesser platform among accounts that your organization or your customers own. There are no arms-length third parties involved in a transfer. Transfers involving two treasury/customer wallets are not routed through shield wallets. **Transfers occur during:** - **Rebalancing among treasury wallets:** To ensure sufficient funds in your organization’s wallet(s), you or Tesser with your authorization may redistribute funds among your treasury wallets. - **Funds flow through a shield wallet to/from a Treasury wallet:** When processing a payment, funds will move from a treasury wallet to a shield wallet prior to being distributed to a beneficiary (outbound payment) or from a shield wallet to a treasury wallet (inbound payment). --- ## Document: Supported Tokens and Networks Supported blockchain networks and stablecoins URL: /overviews/tokens-and-networks # Supported Tokens and Networks
| **Chain** | **Token** | **Availability** |
|---|---|---|
| POLYGON | USDC, USDT | Available |
| ETHEREUM | USDC, USDT | Coming soon |
| STELLAR | USDC | Coming soon |
| SOLANA | USDC, USDT | Coming soon |
| Resource | Description |
|---|---|
| Organization |
An organization represents Tesser’s customer, with whom Tesser has a contractual relationship. All other resources belong to an organization. |
| Workspace |
A workspace is a container for counterparties, accounts, payments, and deposits/withdrawals. Users and their roles, API keys, and webhooks are also established at the workspace level. Every organization starts out with one workspace in the Tesser platform. Additional workspaces can be created by contacting Tesser Customer Support. You may want an additional workspace to segregate activity by line of business, business unit, geography, etc. |
| Tenant |
A tenant represents an organization’s customer when that customer is itself a platform with its own users who need to be represented in Tesser’s systems. When an organization’s customers are tenants, originators and/or beneficiaries are customers of the tenant, and thus end-customers to the organization. *Note: Most organizations will not need to use tenants to manage their integrations with Tesser. |
| Counterparty |
A counterparty represents an originator (ultimate sender) or beneficiary (ultimate receiver) of a payment. Counterparties can be individuals or businesses. Counterparties are associated with Accounts and belong to a Workspace. Registering counterparties in advance of submitting payments enables sanction screening and helps meet regulatory requirements. Counterparty information will be transmitted for fiat payouts and, as applicable, for stablecoin payouts to comply with Travel Rule obligations. |
| Account |
An account represents a store of value that holds fiat currency(ies) and/or stablecoin(s). Accounts can belong to a counterparty or to a workspace. Accounts may be external to the Tesser platform (e.g. pre-existing fiat bank accounts or wallets) or provisioned by Tesser (e.g. self-custodial wallets or ledgers at a custodian). |
| Quote |
A quote represents the cost to deliver a payment to a beneficiary. Quotes include the exchange rate between the source currency and the destination currency. Quotes can be based on a "from" (source) amount and currency or a "to" (destination) amount and currency. |
| Payment |
A payment is the movement of funds from an account provisioned by Tesser to a third party, or vice versa. Most commonly, payments will move funds from an originator to a beneficiary. Payments may be outbound (aka payouts) or inbound (receiving stablecoins, aka pay-ins). Payments also include funds movement to/from a yield provider. |
| Deposit/Withdrawal |
Deposits and withdrawals are first-party movements of funds into or out of liquidity providers. Deposits can occur by on-ramping funds through a liquidity provider. Withdrawals can occur by off-ramping funds through a liquidity provider. |
| Rebalance |
A rebalance represents funds movement among Tesser-provisioned wallets or ledgers for treasury management purposes. |
| Step |
A step represents funds movement from one account to another ( |
| **Originator is you or your customer/end-customer** | **Beneficiary is you or your customer/end-customer** | |
|---|---|---|
| **Outbound payment** | ✅ | ❌ |
| **Inbound payment** | ❌ | ✅ |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `accounts-1000` | 404 | Account with id '{id}' not found | The specified account ID does not exist in this workspace |
| `accounts-1001` | 404 | Entity with id '{id}' not found | The tenant or counterparty specified does not exist |
| `accounts-2000` | 403 | The entity does not belong to your workspace | User tries to link an account to an entity that belongs to a different workspace |
| `accounts-3000` | 400 | Cannot provide both tenant_id and counterparty_id | User provides both tenant_id and counterparty_id when only one is allowed |
| `accounts-3001` | 400 | A workspace-level bank account already exists. Only one is allowed per workspace. | User tries to create a second workspace-level bank account |
| `accounts-3002` | 400 | wallet_address is required for unmanaged wallets (is_managed=false) | User creates an unmanaged wallet without providing a wallet_address |
| `accounts-3003` | 400 | Cannot determine network for wallet type '{type}' | The provided wallet type does not map to a known blockchain network |
| `accounts-3004` | 400 | signature is required for managed wallets (is_managed=true) | User creates a managed wallet without providing the required signature |
| `accounts-3005` | 400 | Invalid signature format. Expected base64-encoded JSON with body and stamp fields. | The signature parameter could not be decoded or is missing required fields |
| `accounts-3006` | 400 | Circle Mint ledgers require a tenant_id or counterparty_id. Workspace-level sub-ledgers are not supported. | User tries to create a Circle Mint ledger without linking it to a tenant or counterparty |
| `accounts-3007` | 400 | This entity already has a Circle Mint ledger. Only one Circle Mint ledger per tenant/counterparty is allowed. | User tries to create a duplicate Circle Mint ledger for the same entity |
| `accounts-3008` | 400 | A workspace-level Circle Mint master wallet ledger already exists. | User tries to create a second master wallet ledger for the workspace |
| `accounts-3009` | 400 | CIRCLE_MINT provider requires a tenant_id or counterparty_id | Circle Mint metadata preparation requires an entity to be linked |
| `accounts-3010` | 400 | CIRCLE_MINT provider requires a business entity (not individual) | Circle Mint only supports business-classified entities, not individuals |
| `accounts-3011` | 400 | Entity is missing required fields for Circle | The entity does not have all required business fields populated for Circle onboarding |
| `accounts-3012` | 400 | Cannot create VAN: compliance state is not ACCEPTED | Virtual account number creation requires the account to have ACCEPTED compliance state |
| `accounts-3013` | 400 | Cannot create VAN: circle_wallet_id is missing from account metadata | The ledger account does not have a Circle wallet ID configured |
| `accounts-3014` | 400 | No workspace-level bank account found. Please create a bank account first. | A workspace-level bank account is required but none exists |
| `accounts-3015` | 400 | No wire bank accounts found in Circle. Please register a bank account on Circle dashboard first. | Circle does not have any wire bank accounts registered for this workspace |
| `accounts-3016` | 400 | Multiple wire bank accounts found in Circle | Production requires exactly one bank account in Circle, but multiple were found |
| `accounts-3017` | 400 | Could not determine Circle bank ID | Failed to resolve the Circle bank ID needed for wire transfers |
| `accounts-3018` | 400 | Unsupported provider. Must be one of : CIRCLE_MINT, KRAKEN | The specified provider is not supported for this operation. Must be one of : CIRCLE_MINT, KRAKEN |
| `accounts-3019` | 400 | Account does not have a provider configured | The account metadata does not contain provider configuration |
| `accounts-3020` | 400 | Entity type must be 'counterparty' or 'tenant' | The referenced entity is not a valid type for account linking |
| `accounts-3022` | 400 | Signature body has unexpected activity type '{actual}', expected '{expected}' | The stamped Turnkey activity inside the signature is not the expected CREATE_WALLET activity |
| `accounts-3023` | 400 | Signature body organizationId does not match the authenticated workspace's Turnkey sub-organization | The caller stamped an activity targeting a sub-organization that does not belong to their workspace |
| `accounts-3024` | 400 | Signed wallet account addressFormat '{actual}' does not match type '{type}' (expected '{expected}') | The stamped CREATE_WALLET parameters describe an address format that does not match the dto.type |
| `accounts-3025` | 400 | Managed wallets are only supported for type 'stablecoin_ethereum' (got '{type}') | User attempted to create a managed (is_managed=true) wallet for a non-Ethereum wallet type |
| `accounts-3100` | 400 | account_name is required and must be between 1-255 characters | User provides an account name that doesn't meet length requirements |
| `accounts-3101` | 400 | tenant_id must be a valid UUID | User provides a tenant_id that is not a valid UUID |
| `accounts-3102` | 400 | counterparty_id must be a valid UUID | User provides a counterparty_id that is not a valid UUID |
| `accounts-3103` | 400 | bank_name is required and must be between 1-255 characters | User provides a bank name that doesn't meet length requirements |
| `accounts-3104` | 400 | bank_code_type must be one of: SWIFT, BIC, IBAN, ROUTING | User provides a bank_code_type that is not in the allowed enum |
| `accounts-3105` | 400 | bank_identifier_code is required | User creates a bank account without providing the bank identifier code |
| `accounts-3106` | 400 | bank_account_number is required | User creates a bank account without providing the account number |
| `accounts-3107` | 400 | type must be one of: stablecoin_ethereum, stablecoin_solana, stablecoin_stellar | User provides a wallet type that is not in the allowed enum |
| `accounts-3108` | 400 | wallet_address must be a valid blockchain address for the specified network | User provides a wallet address that doesn't match the expected format for the network |
| `accounts-3109` | 400 | Wallet signature is malformed or invalid | User provides a signature that is not properly formatted or cannot be verified |
| `accounts-3110` | 400 | provider must be one of: CIRCLE_MINT, KRAKEN | User provides a provider value that is not in the allowed enum |
| `accounts-5000` | 502 | Failed to create wallet. Please try again or contact support if the issue persists. | Turnkey or other wallet provider failed during wallet creation |
| `accounts-5001` | 502 | Failed to create Circle external entity | Circle API call to create the external entity failed |
| `accounts-5002` | 502 | Unable to create virtual account number (VAN) for this ledger account. | Circle API call to create the virtual account number failed |
| `accounts-5003` | 502 | Failed to retrieve entity business fields from vault | Basis Theory vault call to retrieve entity fields failed |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `counterparties-1000` | 404 | Counterparty not found | The specified counterparty ID does not exist |
| `counterparties-1001` | 404 | Tenant with id '{id}' not found. Cannot assign counterparty to non-existent tenant. | User tries to assign a counterparty to a tenant_id that doesn't exist |
| `counterparties-2000` | 403 | You do not have access to this counterparty | User tries to access a counterparty that belongs to a different workspace |
| `counterparties-3002` | 400 | classification must be 'individual' or 'business' | User provides a classification value that is not 'individual' or 'business' |
| `counterparties-3003` | 400 | business_legal_name is required for business counterparties and must be 1-255 characters | User creates a business counterparty with missing or improperly formatted business_legal_name |
| `counterparties-3004` | 400 | business_address_country is required for business counterparties and must be a valid ISO 3166-1 alpha-2 country code | User provides a business_address_country that is not a valid 2-letter country code (e.g., "US", "GB") |
| `counterparties-3005` | 400 | individual_first_name is required for individual counterparties and must be 1-255 characters | User creates an individual counterparty with missing or improperly formatted first name |
| `counterparties-3006` | 400 | individual_last_name is required for individual counterparties and must be 1-255 characters | User creates an individual counterparty with missing or improperly formatted last name |
| `counterparties-3007` | 400 | individual_address_country is required for individual counterparties and must be a valid ISO 3166-1 alpha-2 country code | User provides an individual_address_country that is not a valid 2-letter country code |
| `counterparties-3008` | 400 | individual_date_of_birth must be a valid date in YYYY-MM-DD format | User provides a date_of_birth that doesn't match the required format or is not a valid date |
| `counterparties-3009` | 400 | tenant_id must be a valid UUID | User provides a tenant_id that is not a validly formatted UUID |
| `counterparties-3010` | 400 | individual_street_address1 is required for individual counterparties | User creates an individual counterparty without providing the street address |
| `counterparties-3011` | 400 | individual_city is required for individual counterparties | User creates an individual counterparty without providing the city |
| `counterparties-3012` | 400 | individual_postal_code is required for individual counterparties | User creates an individual counterparty without providing the postal code |
| `counterparties-3013` | 400 | business_street_address1 is required for business counterparties | User creates a business counterparty without providing the street address |
| `counterparties-3014` | 400 | business_city is required for business counterparties | User creates a business counterparty without providing the city |
| `counterparties-3015` | 400 | business_postal_code is required for business counterparties | User creates a business counterparty without providing the postal code |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `currencies-1000` | 404 | Currency not found | The specified currency code does not exist |
| `currencies-3000` | 400 | Invalid currency code format | User provides a currency code that is not a valid ISO 4217 format (e.g., USD, EUR, GBP) |
| `currencies-3001` | 400 | Currency is not supported for this operation | User attempts to use a currency that is valid but not supported for the requested operation |
| `currencies-3002` | 400 | Invalid currency pair for exchange | User attempts to exchange between currencies where the pair is not supported |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `networks-1000` | 404 | Network not found | The specified network identifier does not exist |
| `networks-3000` | 400 | Invalid network identifier | User provides a network identifier that is not a valid format (e.g., ethereum, polygon, base) |
| `networks-3001` | 400 | Network is not supported for this operation | User attempts to use a network that is valid but not supported for the requested operation |
| `networks-3002` | 400 | Currency is not compatible with the specified network | User provides a currency/network combination that is not compatible (e.g., BTC on Ethereum network) |
| `networks-3003` | 400 | Network is currently unavailable or under maintenance | User attempts to use a network that is temporarily unavailable |
| `networks-5000` | 502 | Failed to connect to network RPC endpoint | Connection to the blockchain network RPC endpoint failed due to external service error |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `payments-1000` | 404 | Payment not found | The specified payment ID does not exist |
| `payments-3000` | 400 | from_network must equal to_network | User provides different networks for from_network and to_network |
| `payments-3001` | 400 | invalid from_amount or to_amount | from_amount and to_amount should be positive and be valid numbers |
| `payments-3002` | 400 | Either from_amount or to_amount must be provided. Cannot create payment without an amount. | User does not provide either from_amount or to_amount |
| `payments-3003` | 400 | Only one of from_amount or to_amount should be provided. The other will be calculated using the exchange rate. | User provides both from_amount and to_amount when only one should be provided |
| `payments-3004` | 400 | from_network is required for crypto-to-crypto payments | User creates a payment with stablecoin from_currency but does not provide from_network |
| `payments-3005` | 400 | to_network is required for crypto-to-crypto payments | User creates a payment with stablecoin to_currency but does not provide to_network |
| `payments-3006` | 400 | Onramp (fiat-to-crypto) payments are not yet supported. Coming soon! | User attempts to create an onramp payment which is not supported yet |
| `payments-3007` | 400 | Invalid from_currency | User provides a from_currency that is not supported |
| `payments-3008` | 400 | Invalid to_currency | User provides a to_currency that is not supported |
| `payments-3009` | 400 | funding_account_id not found | User provides a funding_account_id that does not exist |
| `payments-3010` | 400 | to_account_id not found | User provides a to_account_id that does not exist |
| `payments-3011` | 400 | from_account_id not found | User provides a from_account_id that does not exist |
| `payments-3012` | 400 | Missing input parameter is_approved | User submits a payment review without the required is_approved parameter |
| `payments-3013` | 400 | signature is malformed or signed with incorrect key | User provides a signature that is malformed or not signed with the correct key |
| `payments-3014` | 400 | The signed transaction does not match the details of the payment | User provides a signature that does not match the payment details |
| `payments-3015` | 400 | The exchange rate quote has expired. Please create a new payment to get a fresh quote. | User attempts to execute a payment with an expired exchange rate quote (valid for 24 hours) |
| `payments-3016` | 400 | Payment quote expiration data is missing. Please create a new payment to get a fresh quote. | Payment has an exchange rate but is missing expiration data, indicating a data integrity issue |
| `payments-3017` | 400 | to_account has not yet been risk approved by custodian | Beneficiary account has a Circle recipient that is still pending verification or has not been registered yet |
| `payments-3018` | 400 | from_account_id and to_account_id should not both be managed accounts for payments. Use /v1/treasury/rebalances instead. | Transfers between two managed accounts are treasury operations. Use /v1/treasury/rebalances instead. |
| `payments-3019` | 400 | Account asset not found for {from_account_id} | No matching account asset found for the from account with the specified currency and network |
| `payments-3020` | 400 | No {currency} balance found on network {network} for {to_account_id} | The to account does not have a balance for the specified currency and network |
| `payments-3021` | 400 | Amounts not yet calculated. Payment may still be processing. | Payment amounts have not been calculated yet, the payment may still be processing |
| `payments-3022` | 400 | Payment missing currency information | Payment is missing required currency or network data |
| `payments-3023` | 400 | Payment has not passed compliance screening. Cannot execute. | Payment has not been approved through compliance/risk screening |
| `payments-3024` | 400 | Payment does not have reserved balance. Cannot execute. | Payment balance has not been reserved prior to execution |
| `payments-3025` | 400 | No transfer step found for payment | Payment does not have a transfer step |
| `payments-3026` | 400 | Transfer step is already in status {status}. Cannot execute. | Transfer step is in a status that does not allow execution |
| `payments-3027` | 400 | Transfer step missing account asset IDs. Cannot execute. | Transfer step is missing the required from or to account asset IDs |
| `payments-3028` | 400 | Could not load accounts for execution | Failed to load the from or to account required for payment execution |
| `payments-3029` | 400 | Payment missing required data for step creation | Payment is missing required currency, amount, or network information for step creation |
| `payments-3030` | 400 | To asset is required for onchain payments | Onchain payment steps require a to account asset to be resolved |
| `payments-3031` | 400 | To account {id} is not a fiat account | The to account is not the expected fiat_bank type for offramp payments |
| `payments-3032` | 400 | Account {id} does not belong to workspace | The referenced account does not belong to the payment's workspace |
| `payments-3033` | 400 | Account not found for address {address} | No account found with the specified wallet address |
| `payments-3034` | 400 | Account {id} has no entity ID | The account is missing the required entity ID for payment processing |
| `payments-3035` | 400 | Fiat account {id} not found for workspace | The specified fiat account was not found in the workspace |
| `payments-3036` | 400 | Payment has not been updated with accounts. Please call PATCH /payments/:paymentId first. | Payment must be updated with account IDs before execution |
| `payments-3037` | 400 | Workspace does not have a Turnkey sub-organization ID configured | Workspace is missing Turnkey sub-organization configuration required for signing |
| `payments-3038` | 400 | Unsupported network for signing: {network} | The payment network is not supported for transaction signing |
| `payments-3039` | 400 | Signature is required for payment execution | A Turnkey stamp/signature is required to execute the payment |
| `payments-3040` | 400 | Cannot execute payment from external account {id} | External accounts support CRUD operations only. Use managed accounts for payment execution. |
| `payments-3041` | 400 | No payment execution provider available for this payment | No supported provider (Alfred, Circle) could be resolved for this payment type and account combination |
| `payments-3042` | 400 | Source account asset not found | The source account asset could not be found for payment execution |
| `payments-3043` | 400 | Source account wallet address not found | The source account does not have a crypto wallet address |
| `payments-3044` | 400 | Destination account asset not found | The destination account asset could not be found for payment execution |
| `payments-3045` | 400 | Destination account wallet address not found | The destination account does not have a crypto wallet address |
| `payments-3046` | 400 | Unsupported currency: {currency} | The specified currency is not supported for transaction building |
| `payments-3047` | 400 | Wallet address mismatch | The provided wallet address does not match the account's wallet address |
| `payments-3048` | 400 | Amount mismatch between transfer step and provided amount | The provided amount does not match the transfer step amount |
| `payments-3049` | 400 | Transfer step missing from account asset ID | The transfer step does not have a from account asset ID set |
| `payments-3050` | 400 | Transfer step missing to account asset ID | The transfer step does not have a to account asset ID set |
| `payments-3051` | 400 | Destination account not found for {id} | The destination account could not be found by address or ID |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `tenants-1000` | 404 | Tenant not found | The specified tenant ID does not exist |
| `tenants-2000` | 403 | You do not have access to this tenant | User tries to access a tenant that belongs to a different workspace |
| `tenants-3000` | 400 | business_legal_name is required to create a tenant | User creates a tenant without providing the required business_legal_name field |
| `tenants-3001` | 400 | business_legal_name is required and must be 1-255 characters | User provides a business_legal_name that doesn't meet length requirements |
| `tenants-3002` | 400 | business_address_country must be a valid ISO 3166-1 alpha-2 country code | User provides a business_address_country that is not a valid 2-letter country code |
| `tenants-3003` | 400 | webhook_url must be a valid HTTPS URL | User provides a webhook_url that is not a properly formatted HTTPS URL |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `treasury-1000` | 404 | Rebalance with id '{id}' not found | The specified rebalance ID does not exist |
| `treasury-1001` | 404 | Transfer step not found for rebalance with id '{id}' | The transfer step associated with the rebalance could not be found |
| `treasury-1002` | 404 | Account with id '{id}' not found | The specified account ID does not exist or does not belong to this workspace |
| `treasury-1100` | 404 | Deposit with id '{id}' not found | The specified deposit ID does not exist in this workspace |
| `treasury-1101` | 404 | Account with id '{id}' not found | The specified account ID does not exist or does not belong to this workspace |
| `treasury-1102` | 404 | Source account with id '{id}' not found | The source bank account for this deposit could not be found |
| `treasury-1103` | 404 | VAN account with id '{id}' not found | The virtual account number (VAN) account could not be found |
| `treasury-2100` | 409 | Deposit is still being planned. Wait for the deposit.created webhook before fetching instructions. | Instructions were requested while the deposit's async planning step is still running (steps array is empty). Retry after the deposit.created webhook fires. |
| `treasury-3000` | 400 | Only Circle Mint ledger-to-ledger rebalancing is currently supported | Rebalancing is only available between Circle Mint ledger accounts |
| `treasury-3001` | 400 | Network must not be specified for ledger accounts | Ledger-to-ledger rebalances operate without a blockchain network |
| `treasury-3002` | 400 | At least one of from_amount or to_amount must be provided | A rebalance requires at least one amount to determine the transfer size |
| `treasury-3003` | 400 | Invalid currency pair for rebalance | The specified from_currency and to_currency combination is not supported for Circle rebalancing |
| `treasury-3004` | 400 | from_amount and to_amount must be equal when currencies are the same | When both amounts are provided for same-currency rebalances, they must match |
| `treasury-3005` | 400 | Source or destination account is missing Circle wallet ID | The account does not have a Circle wallet ID configured, which is required for ledger transfers |
| `treasury-3006` | 400 | Transfer step is missing account asset IDs | The transfer step does not have the required from/to account asset references |
| `treasury-3007` | 400 | Network is required when rebalancing between wallet accounts | On-chain wallet-to-wallet rebalances need from_network and to_network to be set |
| `treasury-3008` | 400 | from_network and to_network must match for wallet-to-wallet rebalance | Cross-chain wallet-to-wallet rebalance is not supported yet; source and destination network must be identical |
| `treasury-3009` | 400 | Wallet account is missing a crypto wallet address | On-chain wallet-to-wallet rebalance requires both accounts to have a crypto wallet address |
| `treasury-3010` | 400 | Wallet-to-wallet rebalance is only supported between managed wallets | Both source and destination accounts must be Tesser-managed (is_managed = true) wallet accounts |
| `treasury-3011` | 400 | Currency '{currency}' is not supported for wallet-to-wallet rebalance | On-chain wallet-to-wallet rebalance currently supports USDC only |
| `treasury-3012` | 400 | Network '{network}' is not supported for wallet-to-wallet rebalance | On-chain wallet-to-wallet rebalance currently supports BASE and BASE_SEPOLIA only |
| `treasury-3013` | 400 | Rebalance step is not in a signable state | The step must be in 'created' state with a prepared unsigned transaction to accept a signature |
| `treasury-3014` | 400 | Rebalance step is missing an unsigned transaction payload | The rebalance has not yet prepared an unsigned transaction — retry in a moment |
| `treasury-3015` | 400 | Signature is invalid or could not be verified | The Turnkey stamp did not produce a valid signed transaction |
| `treasury-3016` | 400 | Signed transaction does not match the rebalance details | The destination address or amount in the signed transaction does not match the prepared rebalance step |
| `treasury-3017` | 400 | Destination wallet is not registered with Circle or compliance is not active | The destination managed wallet must have an active Circle business recipient registration before it can receive rebalance transfers |
| `treasury-3018` | 400 | to_network is required when destination is a managed wallet | Rebalances to a managed stablecoin wallet must specify the target blockchain network |
| `treasury-3019` | 400 | Network '{network}' is not supported in this environment | Rebalances to managed wallets only support BASE in production and BASE_SEPOLIA in non-production environments |
| `treasury-3020` | 400 | Signature is not a valid base64-encoded {body, stamp} envelope | The signature field must be base64-encoded JSON containing the Turnkey activity body and stamp header value |
| `treasury-3021` | 400 | Stamped activity type must be '{expected}', received '{actual}' | The stamped Turnkey activity must be ACTIVITY_TYPE_SIGN_TRANSACTION_V2 to sign a rebalance step |
| `treasury-3022` | 400 | Stamped activity unsigned transaction does not match the rebalance step | The unsignedTransaction inside the stamped Turnkey activity must equal the unsigned transaction prepared by the rebalance step |
| `treasury-3023` | 400 | Stamped activity signWith address does not match the rebalance source wallet | The signWith parameter inside the stamped Turnkey activity must equal the source wallet's on-chain address |
| `treasury-3024` | 400 | Stamped activity organizationId does not match the workspace's Turnkey sub-organization | The stamped Turnkey activity's organizationId must equal the workspace's configured Turnkey sub-org ID |
| `treasury-3025` | 400 | Turnkey could not produce a signed transaction | Turnkey rejected or failed the stamped sign_transaction activity. Verify the stamp matches the body bytes, the API key is registered in the sub-org, and the wallet exists in that sub-org. |
| `treasury-3026` | 400 | Stamped activity parameters.type must be '{expected}', received '{actual}' | The Turnkey transaction type inside parameters.type must match the network of the rebalance step (e.g. TRANSACTION_TYPE_ETHEREUM for BASE) |
| `treasury-3100` | 400 | Account '{id}' is not a ledger account | Deposits can only be made to ledger accounts |
| `treasury-3101` | 400 | Account '{id}' is not configured for deposits. No supported provider found | The destination account does not have a supported deposit provider configured |
| `treasury-3102` | 400 | Ledger '{id}' is missing Circle Mint metadata | The ledger account does not have the required Circle Mint configuration |
| `treasury-3103` | 400 | Circle compliance not yet accepted for ledger. Cannot create deposit | Circle compliance must be accepted before deposits can be created |
| `treasury-3104` | 400 | Source account '{id}' does not match the configured source bank for this ledger | The provided source account does not match the bank account configured for this ledger |
| `treasury-3105` | 400 | Source bank account '{id}' not found or is not a fiat bank account | The source bank account could not be found or is not the correct account type |
| `treasury-3106` | 400 | Ledger account '{id}' does not have a '{currency}' asset | The destination ledger account does not hold the requested currency |
| `treasury-3107` | 400 | Invalid currency combination for Circle deposit: {fromCurrency} → {toCurrency} | The specified from_currency and to_currency combination is not supported for Circle deposits |
| `treasury-3108` | 400 | Deposit '{id}' is already finalized. Wire instructions are no longer available | The deposit has already been finalized and instructions cannot be retrieved |
| `treasury-3109` | 400 | Deposit '{id}' has no transfer step | The deposit does not have the expected transfer step |
| `treasury-3110` | 400 | Deposit '{id}' transfer step has no source account | The deposit transfer step is missing its source account reference |
| `treasury-3111` | 400 | Deposit '{id}' transfer step has no destination account | The deposit transfer step is missing its destination account reference |
| `treasury-3112` | 400 | Source account '{id}' is missing required bank details | The source account does not have all required bank details (bank name, code type, identifier code, or account number) |
| `treasury-3113` | 400 | VAN account '{id}' missing virtual_account_number metadata | The VAN account does not have the required virtual account number metadata |
| `treasury-3114` | 400 | VAN account '{id}' is missing required field: {field} | The VAN account is missing a required field for wire instructions (e.g., bank name, account number, SWIFT code, beneficiary name) |
| `treasury-3115` | 400 | Simulated deposits are not available in production | Deposit simulation is only available in sandbox environments |
| `treasury-3116` | 400 | to_network is required when destination is a managed wallet | Deposits to a managed stablecoin wallet must specify the target blockchain network |
| `treasury-3117` | 400 | to_network must not be specified for ledger accounts | Deposits to ledger accounts operate without a blockchain network |
| `treasury-3118` | 400 | Network '{network}' is not supported in this environment | Deposits to managed wallets only support BASE in production and BASE_SEPOLIA in non-production environments |
| `treasury-3119` | 400 | Deposit '{id}' has no estimated source amount yet | The deposit's estimated.from.amount or estimated.from.currency is not populated. The estimate is filled in after planning; this can occur if instructions are requested before the deposit has been planned. |
| `treasury-3120` | 400 | No deposit provider can handle this configuration. Verify the source bank account, destination account compliance state, and currency pair. | getEligibleDepositProviders returned an empty array — no provider matches the deposit's source account, destination account, currency pair, or workspace provider configuration (e.g., missing Circle API key in the vault). |
| `treasury-3133` | 400 | Ledger '{id}' is missing OpenFX metadata | The destination ledger account does not have OpenFX configuration (metadata.openfx) |
| `treasury-3134` | 400 | Currency pair {fromCurrency} → {toCurrency} is not supported for OpenFX deposits | OpenFX deposits support same-currency fiat→fiat or fiat→stablecoin (on-ramp) pairs only |
| `treasury-3135` | 400 | Source bank currency '{currency}' does not match deposit from_currency | The OpenFX-registered source bank account is configured for a different currency than the deposit request |
| `treasury-3139` | 400 | Wallet address '{address}' is not registered in OpenFX for {coin} on {network} | The wallet must be manually registered in the OpenFX dashboard before it can receive deposits |
| `treasury-3140` | 400 | Multiple OpenFX withdrawal addresses matched ({count}) — registration is ambiguous | More than one verified active withdrawal address matched; resolve via the OpenFX dashboard |
| `treasury-3141` | 400 | OpenFX coinNetworkName '{coinNetworkName}' is not a known Tesser network | The network returned by OpenFX does not map to a supported Tesser network identifier |
| `treasury-3142` | 400 | to_network is required for OpenFX wallet deposits | OpenFX wallet deposits must specify the target blockchain network via to_network |
| `treasury-3200` | 400 | from_currency must be a valid currency code | User provides an invalid from_currency value |
| `treasury-3201` | 400 | to_currency must be a valid currency code | User provides an invalid to_currency value |
| `treasury-3202` | 400 | from_amount must be a valid decimal string | User provides a from_amount that is not a valid number |
| `treasury-3203` | 400 | to_amount must be a valid decimal string | User provides a to_amount that is not a valid number |
| `treasury-3204` | 400 | from_account_id must be a valid UUID | User provides a from_account_id that is not a valid UUID |
| `treasury-3205` | 400 | to_account_id must be a valid UUID | User provides a to_account_id that is not a valid UUID |
| `treasury-3206` | 400 | from_network must be a valid network identifier | User provides an invalid from_network value |
| `treasury-3207` | 400 | to_network must be a valid network identifier | User provides an invalid to_network value |
| `treasury-5100` | 502 | Failed to obtain OpenFX quote for {fromCurrency} → {toCurrency} | OpenFX rejected or did not respond to the projection quote during deposit planning. The deposit cannot be created until OpenFX returns a valid rate. |
| `treasury-5101` | 502 | OpenFX trade failed for deposit '{id}' after retry budget | The swap leg of an OpenFX cross-currency deposit could not be executed within the retry budget. The swap step has been marked as failed. |
| `treasury-5102` | 502 | OpenFX quote expired before trade could execute | OpenFX rejected the trade because the quote had expired (HTTP 422). The next attempt will request a fresh quote. |
| `treasury-5103` | 502 | OpenFX withdrawal failed for deposit '{id}' (reason: {reason}) | The wallet leg of an OpenFX deposit could not be submitted to OpenFX. The withdrawal step has been marked as failed. |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `vault-0001` | 500 | Failed to store sensitive data in vault | The vault provider failed to store the token. This may be due to a temporary outage or configuration issue. |
| `vault-0002` | 500 | Vault provider did not return a token ID | The vault provider accepted the data but did not return a token ID. This indicates an unexpected API response. |
| `vault-0003` | 400 | Unknown vault field | The specified field name is not configured for vault storage. Check the VaultFieldConfig for supported fields. |
| `vault-0004` | 400 | Invalid field type for operation | The operation does not match the field type. For example, trying to store text in a file-only field. |
| `vault-0005` | 409 | Vault record already exists for this resource and field | A create-only vault write found an existing record for the same (resourceId, fieldName). Rotate or delete the existing record instead. |
| `vault-0010` | 404 | Vault token not found | The specified token does not exist in the vault. It may have been deleted or never created. |
| `vault-0011` | 500 | Failed to retrieve token from vault | The vault provider failed to retrieve the token. This may be due to a temporary outage or authentication issue. |
| `vault-0012` | 500 | Failed to reveal token value | The vault provider failed to reveal the token value. The API key may not have sufficient permissions. |
| `vault-0020` | 500 | Failed to delete token from vault | The vault provider failed to delete the token. This may be due to a temporary outage or authentication issue. |
| `vault-0030` | 502 | Vault proxy request failed | The vault proxy failed to forward the request to the destination. Check the destination URL and authentication. |
| `vault-0031` | 502 | Vault proxy destination returned an error | The vault proxy successfully forwarded the request, but the destination returned an error response. |
| `vault-0040` | 503 | Vault provider is not configured | The vault provider API key is not configured. Contact support to enable vault functionality. |
| `vault-0041` | 400 | Liquidation provider not found | The specified liquidation provider is not configured. Check the available providers. |
| `vault-0050` | 502 | Failed to forward vaulted data to provider | The vaulted data could not be forwarded to the liquidation provider. This may be due to a provider outage or misconfiguration. |
| `vault-0051` | 422 | Provider rejected the forwarded data | The liquidation provider rejected the forwarded data. Check the data format and provider requirements. |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `circle-1000` | 404 | Circle API key not configured for organization | The organization does not have a Circle Mint API key configured in the vault |
| `circle-2000` | 401 | Circle API key authentication failed | The configured Circle Mint API key failed authentication with Circle API |
| `circle-4290` | 429 | Cannot complete request because rate limited by provider Circle Mint | The Circle Mint API returned a 429 rate limit response |
| `circle-5000` | 502 | Circle Mint service error | An error occurred while communicating with the Circle Mint API |
| `circle-5001` | 503 | Circle Mint service temporarily unavailable | The Circle Mint API is temporarily unavailable |
| Error Code | HTTP Status | Error Message | Description |
|---|---|---|---|
| `idempotency-0001` | 409 | A request with this idempotency key is currently being processed. Please wait and retry. | Another request with the same idempotency key is still in progress |
| `idempotency-0002` | 400 | Keys for idempotent requests can only be used with the same parameters they were first used with. | The idempotency key was previously used with a different request body. Use a different key for different requests. |
| *Belongs to:* | **Type: Bank account** | **Type: Wallet** | **Type: Ledger** |
|---|---|---|---|
| **Workspace** | n/a |
An omnibus wallet an organization uses to manage its treasury operations in an aggregated manner. (Managed) |
An omnibus wallet an organization uses to manage its treasury operations in an aggregated manner. (Managed) |
| **Tenant** | n/a |
A wallet designated for funds belonging to a tenant. (Managed) |
A wallet designated for funds belonging to a tenant. (Managed) |
| **Counterparty** |
A bank account that belongs to an originator or a beneficiary. (Unmanaged) For fiat payouts, bank account info as transmitted as part of Travel Rule compliance.
|
A wallet designated for an originator or beneficiary provisioned by Tesser. (Managed) A wallet provisioned for the originator or beneficiary by a non-Tesser provider. (Unmanaged) |
A wallet designated for an originator or beneficiary. (Managed) |
| **Status** | **Terminal** | **Webhook event type** | **Description** |
|---|---|---|---|
| `unchecked` | No |
Not sent. All payments at creation have a risk status of `unchecked` |
Beneficiary wallet identifier has not been supplied or risk check has not yet completed. |
| `awaiting_decision` | No | payment.risk_updated | Beneficiary wallet has been risk screened and requires manual review to determine whether to send the payout. |
| `auto_approved` | Yes | payment.risk_updated | Beneficiary wallet has been risk screened and automatically approved per your organization's policy. |
| `manually_approved` | Yes | payment.risk_updated | Beneficiary wallet has been risk screened and has been manually reviewed and approved. |
| `auto_rejected` | Yes | payment.risk_updated | Beneficiary wallet has been risk screened and automatically rejected per your organization's policy. |
| `manually_rejected` | Yes | payment.risk_updated | Beneficiary wallet has been risk screened and has been manually reviewed and rejected. |
| **Status** | **Terminal** | **Webhook event type** | **Description** |
|---|---|---|---|
| `unreserved` | No |
Not sent. All payments at creation have a balance status of `unreserved` |
Source wallet id has not been supplied or the reserve operation has not yet completed. |
| `awaiting_funds` | No | payment.balance_updated | The balance of the source wallet was checked and there are insufficient funds to process the payout. The payment is queued and awaiting funds from a deposit. |
| `reserved` | Yes | payment.balance_updated | The balance of the source wallet was checked and funds were reserved to process the payout. |
| **Status** | **Terminal** | **Webhook event type** | **Description** |
|---|---|---|---|
| `created` | No |
Not sent. All steps at creation have a status of `created` |
Tesser has created a record for this step. |
| `submitted` | No | step.submitted | Tesser submitted the step information to the blockchain or fiat payment network |
| `confirmed` | No | step.confirmed | The step was accepted by the operator of the payment network |
| `finalized` | No | step.finalized | For crypto transfer steps, the block containing the transfer step has been finalized. The transfer step is now permanent and irreversible. How long it takes to finalize a transfer depends on the blockchain. |
| `completed` | No | step.completed |
For fiat transfer steps, indicates the local payment network has delivered funds to the beneficiary. *Note, some fiat payment networks may not provide formal confirmation of funds delivery, in which case funds are assumed to be delivered unless a `failed` status is indicated |
| `failed` | Yes | step.failed | The transfer step was not successful; funds were not transferred from the `from_account` to the `to_account` |