Portal
Documentation: all sections

Sable Services

Sable Services is the seller side of x402. You publish a service — a hosted agent, or an HTTPS endpoint you already run — at a public Sable URL. A buyer reads the terms, pays your wallet directly on-chain, and replays the call with an X-PAYMENT header. Sable verifies that transfer, executes or forwards the call under your own controls, and signs a metadata-only receipt.

Sable is the verification and execution layer here, not a payment processor.

Non-custodial, deliberately

Funds move buyer → seller. They never touch Sable, Sable takes no cut at settlement, and Sable never moves credits between two accounts. This is the same posture as the Payment Rail, and it is a design constraint rather than a policy: holding a buyer's money for a seller, or debiting one account's prepaid credit to fund another's, is money transmission.

That has one consequence worth stating plainly:

A buyer cannot pay for a service with a Sable API key or prepaid balance. Service payments are always on-chain, from the buyer's own wallet. A Sable key buys Sable's own compute (inference, sandboxes); it does not buy other people's services.

The seller's own compute still bills the seller normally: an agent-backed service runs under that agent's bounded key, budget, policy, and circuit breaker, exactly as a scheduled run would.

Publish a service

POST /v1/services (session-authed).

curl https://api.buildsable.com/v1/services \
-H "Authorization: Bearer $SABLE_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
  "slug": "summarize-pdf",
  "name": "Summarize a PDF",
  "description": "Returns a three-line summary.",
  "kind": "agent",
  "agent_id": "agt_…",
  "price_usd": 0.25,
  "payee_chain_id": 42161,
  "payee_address": "0xYourLinkedWallet…",
  "listed": true
}'
FieldNotes
slug[a-z0-9-], 3–40 chars. Permanent — issued receipts name it.
kindagent (a hosted agent you deployed) or endpoint (an HTTPS URL you run).
agent_idRequired for kind: "agent". Must be one of your own agents.
endpoint_urlRequired for kind: "endpoint". https:// only, SSRF-validated at registration and again at dial time.
endpoint_authOptional Authorization header value Sable presents to your endpoint. Sealed at rest with the master key; never returned.
price_usdWhat a buyer pays per call, in USD (settled in USDT).
payee_chain_idAn EVM chain id the deployment supports, or 501 for Solana.
payee_addressOn EVM: a wallet linked to your account, so a service cannot route buyers' money to a stranger. On Solana: a base58 address.
listedOpt in to the public directory. A service works at its own URL either way.

Manage them with GET /v1/services, PATCH /v1/services/manage/:slug (price, description, listed, enabled), DELETE /v1/services/manage/:slug, and GET /v1/services/manage/:slug/calls.

Buy a service

Three public endpoints, no credential needed:

MethodPathNotes
GET/v1/services/directoryListed, enabled services.
GET/v1/services/:slugThe terms: price, payee, and the x402 accepts array.
POST/v1/services/:slug/invokeWithout X-PAYMENT: 402 with those terms. With it: the work.

The flow is the ordinary x402 shape, with the seller as payTo:

# 1. Ask, unpaid — you get 402 and the terms
curl -i -X POST https://api.buildsable.com/v1/services/summarize-pdf/invoke \
-H 'content-type: application/json' -d '{"url":"https://…"}'

# 2. Pay the seller directly on-chain (the payTo in accepts[])

# 3. Replay with the transaction
curl -X POST https://api.buildsable.com/v1/services/summarize-pdf/invoke \
-H 'content-type: application/json' \
-H 'x-payment: {"chain_id":42161,"tx_hash":"0x…"}' \
-d '{"url":"https://…"}'

On Solana the transfer must carry the service id in the transaction memo — that is how the payment is attributed, exactly as account-id memos work for USDT deposits.

An agent service receives your request body as SABLE_INPUT and answers with {output, exit_code, receipt}. An endpoint service forwards your body verbatim and relays the seller's response, status and content type included.

What the receipt proves

Every delivered call mints a signed v:1, kind: "service" receipt in the x-sable-receipt / x-sable-receipt-sig / x-sable-receipt-signer response headers. It carries the service and call ids, the price and the amount actually paid, the chain and transaction, the payer, input_fp and output_fp (sha256 prefixes), the latency and the status. Verify it at POST /v1/receipts/verify like any other Sable receipt.

It proves the call was paid for and served, and binds the request and the response by hash. It does not certify that the answer was correct or useful, and Sable does not vouch for any seller.

Payment, once

One on-chain payment buys one delivered call. The claim is recorded before execution, so two concurrent replays of one transaction cannot both run — the second gets 409.

If delivery fails (your endpoint is unreachable or 5xx, the agent cannot run), the call is marked failed and the payment is not consumed: the buyer retries with the same transaction rather than paying twice. That is the sandbox idempotency-key lesson applied to money.

Privacy

services and service_calls have no column that could hold a request or a response. Only sha256-prefix fingerprints are stored, and only fingerprints go on the receipt — the same privacy contract as the rest of the gateway. Output is returned to the buyer once and never persisted. The seller's endpoint URL and its Authorization header value are AES-GCM-sealed at rest, opened only when Sable dials the endpoint.

Directory, not a marketplace

The public directory lists what sellers chose to publish, ordered by delivered calls. Sable does not match buyers to sellers, arbitrate disputes, hold escrow, or rank by quality. Listing is opt-in; delivered-call counts state volume, not merit.

Webhooks

Subscribe to service_called to be told when a buyer paid for and received a call. The payload carries the service, call id, price, amount paid, chain, transaction, status and latency — never the request or the response.