sable network
Documentation · all sections

SDKs

Two official SDKs wrap the OpenAI client for TypeScript and Python, pre-configured for https://api.buildsable.com/v1, and add helpers for the Sable-specific surface: sandbox runs, credit, delegated sub-keys, and receipt and attestation verification. Everything the OpenAI client can do (chat, embeddings, streaming) works unchanged, because Sable is the OpenAI client with a different base URL.

Both SDKs are at version 0.3.0, which added two layers under the hand-written one: a generated client with a method for every operation in the gateway's OpenAPI document, and launchpad helpers that read the public coin endpoints and build unsigned transactions.

Two more client packages ship alongside them: a CLI and a framework middleware. All four are listed below.

None of the four is published to npm or PyPI yet. Install them from the repository's sdk/ directories for now. Publishing is planned, not done, and we'd rather say so than have you hunt for a package that isn't there.

PackageWhat it isPath
@sable-network/sdkWhat it isThe TypeScript client (extends the OpenAI client)Pathsdk/ts/
sable-networkWhat it isThe Python client (wraps openai)Pathsdk/python/
@sable-network/cliWhat it issable on the command line: chat, run code, check credit, verify a receiptPathsdk/cli/
@sable-network/middlewareWhat it isA zero-dependency fetch adapter that points any fetch-based framework at Sable and captures the receiptPathsdk/middleware/

TypeScript: @sable-network/sdk

Sable extends the OpenAI client, so the whole OpenAI surface is inherited.

import { Sable, SableError } from "@sable-network/sdk";

const client = new Sable({ apiKey: process.env.SABLE_API_KEY! });

// The OpenAI surface, unchanged:
const resp = await client.chat.completions.create({
model: "sable-llama-3.3-70b",
messages: [{ role: "user", content: "hi" }],
});

// Sable extensions:
const run = await client.runCode({ language: "python", code: "print(6*7)" });
const credit = await client.credit(); // this key's balance and runway
MethodWhat it does
chat.completions.create(...)What it doesOpenAI-compatible chat, inherited from the base client. See Chat completions.
embeddings.create(...)What it doesOpenAI-compatible embeddings, inherited. See Embeddings.
runCode({...})What it doesRun code in a metered sandbox via POST /v1/sandboxes. See Sandbox compute.
credit()What it doesThe calling key's balance via GET /v1/credit, so an agent can see its runway over REST.
delegateKey({...})What it doesMint a bounded child of the calling key. See Delegated sub-keys.
chatWithReceipt({...})What it doesA non-streaming completion plus its signed receipt, captured from the response headers in one call.
verifyReceipt(...)What it doesExported helper: verify a signed receipt through the public POST /v1/receipts/verify. See Verifiable receipts.
verifyReceiptLocally(...)What it doesExported helper: verify a receipt offline against a pinned signer address (EIP-191 recovery via @noble, no network, no trusting the party being verified).
verifyAttestation()What it doesExported helper: pre-flight check of the live confidential attestation via GET /v1/attestation. See Privacy tiers.
xPaymentHeader(...) / settleWith(...)What it doesExported helpers: build the X-PAYMENT header for inline x402 settlement. See Paying with USDT.

Requests carry sensible default timeouts (per-call timeoutMs override), so a hung gateway call fails instead of blocking an agent forever.

Python: sable-network

The same shape: a wrapper around openai-python, plus the Sable methods in snake_case.

from sable_network import Sable, SableError

client = Sable(api_key=SABLE_API_KEY)

# The OpenAI surface, unchanged:
resp = client.chat.completions.create(
  model="sable-llama-3.3-70b",
  messages=[{"role": "user", "content": "hi"}],
)

# Sable extensions:
run = client.run_code(language="python", code="print(6*7)")
credit = client.credit()
MethodWhat it does
chat.completions.create(...)What it doesOpenAI-compatible chat, inherited.
embeddings.create(...)What it doesOpenAI-compatible embeddings, inherited.
run_code(...)What it doesRun code in a metered sandbox.
credit()What it doesThe calling key's balance and runway.
delegate_key(...)What it doesMint a bounded child key.
chat_with_receipt(...)What it doesA non-streaming completion plus its signed receipt in one call.
verify_receipt(...)What it doesModule helper: verify a signed receipt through the public endpoint.
verify_receipt_locally(...)What it doesModule helper: verify a receipt offline against a pinned signer. Needs the optional extra: pip install "sable-network[verify]".
verify_attestation()What it doesPre-flight the live confidential attestation.
x_payment_header(...)What it doesModule helper: build the X-PAYMENT header for inline x402 settlement.

Every other endpoint: the generated client

The methods above are hand-written for the routes agents use most. Beneath them, both SDKs carry a generated client with one method for every operation in the gateway's OpenAPI document, grouped by tag and reached as client.api.<group>.<operation>().

const sable = new Sable({
apiKey: process.env.SABLE_API_KEY!,
sessionToken: process.env.SABLE_SESSION, // optional; see below
});

// GET /v1/memory/collections  (API key)
const { collections } = await sable.api.memory.getMemoryCollections();

// GET /v1/runs/{run_id}  (session token: an account-management route)
const run = await sable.api.runs.getRunsRunId({ run_id: "nightly-eval" });

// GET /v1/community/public/{slug}/candles  (public: no credential is sent)
const candles = await sable.api.community.getCommunityPublicSlugCandles({ slug: "sabre", interval: "1h" });
  • Names are mechanical. The HTTP method plus the path segments, with v1 dropped: GET /v1/runs/{run_id} is getRunsRunId in TypeScript and get_runs_run_id in Python.
  • The right credential per route. API-key routes send the API key, account-management routes send a session token (a sess_… from POST /v1/auth/verify), and public routes send nothing. A call that needs a credential you did not supply fails with MissingCredentialError before any request is made. With no key at all, new SableApi() (SableApi() in Python) reaches every public route.
  • Same errors and deadlines. A non-2xx answer raises the same SableError. Defaults are 15 s for GET and DELETE, 180 s for writes, and none for streams.
  • Python is synchronous only, like the rest of that package.

Response types are only as precise as the OpenAPI document, and no more. The document describes 448 JSON responses. 165 of them are typed all the way down, 55 partly, and 228 are described only in prose: those come back as a free-form object (Record<string, unknown> in TypeScript, a plain dict in Python) with the prose in the method's doc comment. The SDK does not invent fields for them. Where a shape is declared, nothing in the gateway checks it against the handler that produces the response, and Python validates nothing at runtime, so read a declared type as documentation rather than a guarantee.

The client is generated from an OpenAPI document emitted from the gateway's source, not fetched from a running deployment. So it covers every operation the build knows about, including ones a given deployment has switched off; such a call answers 404 or 501 there.

Launchpad helpers

Both SDKs can read the public coin endpoints and build transactions for a coin's bonding curve. The rule they are built on:

The SDK never holds a key, never signs and never sends a transaction. Every builder returns an unsigned { to, data, value, chainId } for your own wallet to complete, sign and send.

import { Launchpad, SableApi, planBuy, parseUnits, toRpcTransaction } from "@sable-network/sdk";

const lp = new Launchpad(new SableApi());          // public; or `sable.launchpad`
const coin = await lp.record("sabre");             // launch, curve, market
const { candles } = await lp.candles("sabre", { interval: "1h" });

// Plan a buy of 0.01 ETH. The reads go through YOUR RPC, not Sable's.
const plan = await planBuy({
ethCall: (call) => provider.request({ method: "eth_call", params: [call, "latest"] }),
curve: coin.curve!.curve_address!,
quoteToken: coin.curve!.quote_token,             // null = paired with ETH
quoteIn: parseUnits("0.01", 18)!,
slippageBps: 100,
chainId: coin.curve!.chain_id!,
});
for (const step of plan.transactions) {
// an exact approval first when the coin is paired with an ERC-20, then the buy
await provider.request({ method: "eth_sendTransaction", params: [toRpcTransaction(step.tx, myAddress)] });
}
TypeScriptPython
Reads (Launchpad)TypeScriptconfig, directory, record, market, curve, candles, holders, trades, quotePythonthe same names
BuildersTypeScriptbuildBuy, buildSell, buildApprove, buildCreate (and buildCreateApproval), buildGraduate, buildClaimCreatorFeesPythonbuild_buy, build_sell, build_approve, build_create (and build_create_approval), build_graduate, build_claim_creator_fees
PlanningTypeScriptplanBuy, planSellPythonplan_buy, plan_sell
HelpersTypeScriptapplySlippage, parseUnits, formatUnits, findLaunchCreated, toRpcTransactionPythonapply_slippage, parse_units, format_units, find_launch_created

planBuy and planSell read the curve's own preview through your eth_call, apply the slippage floor, and return the steps to send. Three rules are built in:

  • No minimum is ever zero. The buy and sell builders refuse a non-positive slippage floor.
  • An approval is for exactly the amount, never unlimited.
  • A figure the gateway could not read is null (None in Python), never zero.

The calldata is pinned byte for byte to Foundry cast vectors. A preview is a reading at the moment it was taken, not a price guarantee; the floor is what protects you. Sable operates this curve and takes a fee on trades. The helpers have been tested against those vectors and a local listener, not against a live chain or a real wallet.

Error handling

Both SDKs raise a SableError that keeps the gateway's error information intact. The case worth branching on is credit exhaustion: an out-of-credit 402 carries x402 payment terms, and the error exposes them so an agent can settle and retry: isInsufficientCredit in TypeScript, is_insufficient_credit in Python. Both are true only for type: "insufficient_credit", which is deliberate — four unrelated conditions answer 402, and the other three (a spend cap reached, a circuit breaker tripped, type: "circuit_breaker_tripped", and a plan that does not include what was asked for, type: "plan_required") carry no terms and are not clearable by paying. Settling on those funds the runaway the breaker exists to stop, so branch on the predicate, never on the status code. See Paying with USDT for what the terms contain and how settlement works. On a 429, the error also carries the Retry-After value (retryAfterSecs / retry_after_secs), so a backoff loop doesn't have to guess.

try {
await client.chat.completions.create({ /* ... */ });
} catch (err) {
if (err instanceof SableError && err.isInsufficientCredit) {
  // err carries the 402's x402 "accepts" terms; settle, then retry.
}
throw err;
}

If you'd rather not take a dependency at all, the plain OpenAI SDK with base_url="https://api.buildsable.com/v1" covers chat and embeddings (see the Quickstart), and every Sable extension is reachable as an ordinary HTTP call.

Shipped
Sable Launchpad — launch a coin with a character, on Robinhood ChainProof of Backing — continuous reserve verification, published wholeSupport Program register opened to SABL holdersEight models added, including the Claude 5 and Gemini 3.8 familiesKnow Your Agent — a machine-checkable credential at a passport handleAutopilot — receipted evidence for a cheaper configurationThe Verifiable Arena — every score backed by a real receipted runTime Machine — record a request, replay it, diff the resultSable Notary — sign and anchor work Sable did not runIntelligence Engine — compile a configuration into an immutable buildSealed Calls — commit a prediction now, reveal it laterAgent Post — cross-account mail between agents, with postageSable Legacy — a dead man's switch for sealed contentThe Judge — a panel of models, one signed verdictSable Memory, Agent Runtime and the non-custodial payment railPersistent sandbox sessions with exec, files and snapshotsMCP Gateway — every tool call allowlisted, metered and receiptedOAuth 2.1 + dynamic client registration for one-click MCP connectSable Vault — a private registry with public settlement