Paying with USDT
Sable runs on prepaid credit. You fund a balance with USDT on a supported EVM
chain: Ethereum mainnet (chain_id: 1) and Arbitrum One (42161) today, or
on Solana, and inference and sandbox compute draw it down.
There is no card, no invoice, and no signup call.
How settlement works
- Send USDT to the treasury address from a wallet linked to your account.
- Submit the transaction hash.
- The gateway verifies the transfer against its own RPC node and credits your balance once it has enough confirmations.
GET /v1/billing/methods is public and returns the treasury address, the
supported chains, each chain's USDT contract and decimals, and the confirmation
requirement. That response (or the portal's Billing page) is the
authoritative source for the treasury address: addresses can rotate, so never
hardcode one.
# 1. Discover where and how to pay (no credentials needed)
curl https://api.buildsable.com/v1/billing/methods
# 2. After sending USDT, submit the transaction hash
curl https://api.buildsable.com/v1/billing/deposits \
-H "authorization: Bearer $SABLE_SESSION" \
-H 'content-type: application/json' \
-d '{"chain_id": 1, "tx_hash": "0xabc…"}'
# 3. Check the balance
curl https://api.buildsable.com/v1/billing/balance \
-H "authorization: Bearer $SABLE_SESSION"Why the sending wallet matters
A deposit is credited only if it came from a wallet already linked to your
account. This applies to every settlement path, including the inline
X-PAYMENT header below. Without that rule, anyone watching the treasury could
claim someone else's deposit by submitting its hash first. A transfer from an
unlinked wallet is refused with "that transfer was not sent from a wallet
linked to this account"; the remedy is to link the paying wallet first under
Portal → Wallets (linking requires a browser SIWE signature, which is what
proves ownership) and then re-submit the same hash: the funds are not lost,
just uncredited until the wallet is yours on record.
One transaction can carry several treasury-bound transfers (a batched or contract-mediated payment, say), and each qualifying transfer in it is credited individually.
Replay safety
Each on-chain transfer can be credited exactly once, globally: enforced by a
unique constraint on (chain_id, tx_hash, log_index), not by application logic.
Submitting the same hash repeatedly is safe and is in fact how you poll a deposit
that hasn't confirmed yet.
Confirmations
Below the chain's confirmation threshold a deposit comes back pending with its
current count. Re-submit (or use POST /v1/billing/deposits/refresh) and it
credits once confirmed. Waiting for confirmations is what stops a reorg from
handing out credit that was never really paid.
USDT on Solana
Sable also accepts USDT on Solana (chain_id: 501), live on the production
deployment. Check GET /v1/billing/methods for the solana block; that
response is authoritative for the current treasury address and mint (they can
rotate, so never hardcode them). On a self-hosted deployment the rail is
config-gated: if the block is absent, that deployment does not accept Solana
deposits, and nothing below applies.
{
"solana": {
"chain_id": 501,
"usdt_mint": "…",
"treasury_address": "…",
"decimals": 6,
"commitment": "finalized",
"memo_required": true
}
}
⚠️ Put your Sable account id in the transaction memo: a transfer without it cannot be attributed automatically. Solana wallets can't SIWE-link the way EVM wallets do, so attribution is memo-based instead of linked-wallet-based: attach your Sable account id (shown in the portal) as the transaction's memo when you send the USDT. Then submit the signature:
curl https://api.buildsable.com/v1/billing/deposits \
-H "authorization: Bearer $SABLE_SESSION" \
-H 'content-type: application/json' \
-d '{"chain_id": 501, "tx_hash": "<base58 transaction signature>"}'How verification differs from the EVM rail:
- Finality, not confirmation counts. The transfer is verified at Solana's
finalizedcommitment (typically around 30 seconds), with no separate confirmation threshold to wait out. - Balance deltas on the verified mint. The gateway checks the runtime token-balance change on the operator-verified USDT mint, so a lookalike token can't be passed off as USDT.
- Memo attribution. A transfer without a memo, or with an account id that isn't yours, is refused with a clear error: it is never silently kept and never credited to the wrong account. The funds aren't lost: the operator can attribute a memo-less transfer by hand, so contact support with the transaction signature if you sent one without the memo.
Everything else is the same as the EVM rail: the same replay guard (each transfer credits exactly once, globally), the same fail-closed sanctions screening, and the same exactly-once ledger crediting.
Paying in SABL
Sable is priced in dollars and settled in USDT, the way the rest of this page describes. A second, optional way to pay is rolling out: you will be able to pay for Sable compute and Sable Pro subscriptions in SABL, Sable's community token on Solana. Paying in SABL burns it and earns a discount.
This rail is not live on the production deployment yet. It is being built, and this section describes what it will do, not something you can do today. Until it ships, USDT and the on-chain USDT flows above remain the way to fund a balance.
A few things are worth being precise about, because paying in a token is easy to overstate:
- Optional and never required. Sable is always priced in dollars and always payable in USDT. SABL is one more way to settle, not a replacement for the dollar rails.
- It does not gate access. Nothing on Sable requires holding SABL. There is no token-gated model, tier, feature, or endpoint.
- No yield, no revenue share, no governance, no claim on Sable. SABL is a consumptive payment utility: you spend it to pay a bill and it is burned. It is not an investment and confers no rights.
- Supply is paid in dollars. When third-party operators exist, they are paid in dollars, never in token emissions.
The API shape
GET /v1/billing/methods already reports a sabl block. Until the operator
enables the rail it reads {"status": "rolling out"}, and nothing below is
callable. When enabled, the block carries the concrete terms:
{
"sabl": {
"chain_id": 502,
"mint": "DaPayqzdCXcrmvgz9Wx7MySipXxcSofGPtkMgVdqpump",
"burn_address": "…",
"decimals": 6,
"micro_usd_per_token": 1500,
"discount_bps": 500,
"memo_required": true
}
}
micro_usd_per_token is the operator-set rate for one whole SABL, and
discount_bps is the bonus applied for paying in SABL, in basis points. As with
the treasury addresses elsewhere on this page, these can change, so read them
from GET /v1/billing/methods rather than hardcoding them.
Once enabled, paying works exactly like a Solana USDT deposit:
send SABL to the burn_address with your Sable account id in the transaction
memo, then submit the signature.
curl https://api.buildsable.com/v1/billing/deposits \
-H "authorization: Bearer $SABLE_SESSION" \
-H 'content-type: application/json' \
-d '{"chain_id": 502, "tx_hash": "<base58 transaction signature>"}'The transfer is verified at Solana's finalized commitment against the
official mint only, so a lookalike token credits nothing. It credits once and is
replay-safe on the same (chain_id, tx_hash, log_index) guard as every other
rail. The credit is the SABL amount times the rate, plus the discount.
The only official SABL mint is
DaPayqzdCXcrmvgz9Wx7MySipXxcSofGPtkMgVdqpump on Solana; anything else using the
name is an impersonation.
The ledger
Your balance is the signed sum of an append-only ledger: deposits, grants, and
one debit per metered request. Nothing is ever overwritten, so every movement is
auditable, and GET /v1/billing/ledger returns it.
Debits are idempotent per usage event: a retried metering write can't bill you twice, because the database rejects the duplicate rather than trusting the code to remember.
Agents: paying inline with x402
An out-of-credit request returns 402 with an accepts array describing how to
pay, so an autonomous agent can top itself up and retry without a human:
{
"error": { "message": "account is out of credit…", "type": "insufficient_credit" },
"x402Version": 1,
"accepts": [{
"scheme": "sable-usdt-onchain",
"network": "eip155:1",
"asset": "0xdac1…",
"payTo": "0x…",
"maxAmountRequired": "5000000",
"extra": { "minConfirmations": 12, "settleHeader": "x-payment" }
}]
}
maxAmountRequired is a suggested minimum top-up, $5, expressed in the
asset's base units (5000000 for 6-decimal USDT), not a per-request price.
To settle, replay the request with an X-PAYMENT header carrying
{"chain_id": 1, "tx_hash": "0x…"} (raw JSON or base64). The transfer is
verified and credited before the handler runs, so the retry succeeds in one round
trip. The linked-wallet rule above applies here too: an X-PAYMENT transfer
sent from a wallet that isn't already linked to the account is refused with
"that transfer was not sent from a wallet linked to this account", and linking
requires a browser SIWE signature, so link the agent's paying wallet before
it needs to settle autonomously.
curl https://api.buildsable.com/v1/chat/completions \
-H "authorization: Bearer $SABLE_API_KEY" \
-H "x-payment: $(echo -n '{"chain_id":1,"tx_hash":"0xabc…"}' | base64)" \
-H 'content-type: application/json' \
-d '{"model":"sable-llama-3.3-70b","messages":[{"role":"user","content":"hi"}]}'On the scheme name
The scheme is sable-usdt-onchain, not x402's canonical exact. Canonical
exact settlement is a gasless pull: the client signs an EIP-3009
transferWithAuthorization and a facilitator pulls the funds. USDT predates
EIP-3009 and implements neither it nor EIP-2612 permit on the chains that
matter, so that flow simply isn't available for USDT. Rather than advertise
compatibility we don't have, the settlement step is named for what it actually is.
The 402 envelope and discovery shape are unchanged.
For most agents the cheaper pattern is to deposit once and draw down the prepaid balance: one on-chain transaction total, instead of one per call.
Pricing
Per-model prices are published in GET /v1/models and the exact cost of every
call rides along in its receipt. Sandbox compute bills in
vCPU-seconds and GB-seconds of memory. See Sandbox compute.
Peg note: USDT is credited at face value, treating 1 USDT as $1. That's a deliberate simplification: it keeps a price oracle out of the payment path.