{"openapi":"3.1.0","info":{"title":"Superstables Index API","version":"1.5.0","description":"An index of services advertising stablecoin payments over x402 and MPP, with HTTP endpoints probed for payment-challenge signals. Free, no key, CORS open (except creating a purchase, which only a server or sandbox may do). Use it to find payable data, compute, tools and agent services.\n\nErrors: every 4xx/5xx response is JSON with the Error schema ({ error: { code, message } }); never an HTML page.\n\nRate limits: a soft advisory limit of 300 requests per minute per client, advertised on every response with RateLimit-Limit and RateLimit-Policy (IETF RateLimit header fields). A 429 carries Retry-After. Responses are CDN-cached for 300 seconds, so polling faster than that returns the same data.\n\nVersioning: the version is the URL path prefix (/api/v1). Within a version, fields of the index API are only ever added; existing field names and types are a stable contract and are not renamed or removed. Nothing is deprecated today. Deprecation policy: before an index endpoint or API version is retired, its responses carry a Deprecation header (RFC 9745) with the deprecation date and a Sunset header (RFC 8594) with the retirement date, at least 90 days before that date. The changelog announces it when the headers first appear. The purchase preview (/api/v1/purchase, /api/v1/purchases) may still change before it handles real money; every change is listed in the changelog (https://github.com/superstables/superstables/blob/main/CHANGELOG.md).\n\nSandbox: the purchase preview and demo services use test USDC on testnets. Get test USDC at https://faucet.circle.com. For integration testing, find demo services and their x402 terms at https://www.superstables.com/discovery/resources, and their parameters at https://www.superstables.com/api/demo/catalogue.\n\nHTTP purchase preview (/api/v1/purchase, /api/v1/purchases): an agent with nothing installed buys a service Superstables operates, with plain HTTP. It lists what can be bought with the exact x402 payment facts, creates a purchase (no key; the answer carries a bearer token for that purchase and an approval link), and the owner approves on superstables.com with their own wallet. Testnet only: test USDC on Base Sepolia, and where the list includes them, test USDC on Arc Testnet (x402 exact), test pathUSD on Tempo Moderato (MPP tempo.charge) or test USDC on Solana devnet (x402 exact). An agent can ask; only the owner can pay. Wallets: a browser wallet whose account is a regular key (an EOA), such as the MetaMask extension on a computer, or the browser inside a wallet's phone app, such as MetaMask Mobile (open the full approval link there). Not yet: smart-contract wallets, such as Coinbase Smart Wallet or a Safe; a phone's ordinary browser, which has no wallet. The owner needs test USDC on Base Sepolia, at least the amount of the payment (free at https://faucet.circle.com); no ETH is needed. Quickstart: https://www.superstables.com/docs/purchase.md","contact":{"name":"Superstables on X","url":"https://x.com/superstables"},"termsOfService":"https://www.superstables.com/pricing.md"},"externalDocs":{"description":"Human and markdown documentation","url":"https://www.superstables.com/docs"},"servers":[{"url":"https://www.superstables.com"}],"paths":{"/api/v1/services":{"get":{"operationId":"listServices","summary":"List payable services","description":"Filterable list of indexed services. live is true when the endpoint returned HTTP 402 or a detected payment-challenge signal (a payment-challenge header or challenge body) at its last unpaid probe; it does not establish payment compatibility, settlement, delivery or output quality. Pagination is cursor-based: follow page.next_cursor (pass it as cursor) until it is null. A cursor marks the last service read, not a position, so services moving ahead of it do not shift the next page. Offset paging (limit and offset, following page.next_offset) still works.","parameters":[{"name":"rail","in":"query","schema":{"type":"string","enum":["x402","mpp","acp"]}},{"name":"chain","in":"query","schema":{"type":"string"},"example":"base"},{"name":"asset","in":"query","schema":{"type":"string"},"example":"USDC"},{"name":"live","in":"query","schema":{"type":"boolean"},"description":"true returns only services whose last probe detected a payment challenge"},{"name":"q","in":"query","schema":{"type":"string"},"description":"Free-text search over name, category and endpoint"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"description":"Page size"},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"Opaque: page.next_cursor from the previous response. Leave it out for the first page. When given, offset is ignored."},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Number of services to skip; use page.next_offset from the previous response. Prefer cursor."}],"responses":{"200":{"description":"Matching services with census counts and pagination","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceList"}}}},"400":{"description":"The cursor is invalid (code: invalid_cursor)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/services/batch":{"post":{"operationId":"batchGetServices","summary":"Bulk lookup: up to 100 services by id in one request","description":"Read-only batch operation. Send an array of service ids and get back every record found (list shape, without probe history) plus the ids that are not in the index. Use it instead of many single-record calls when reconciling a list of endpoints.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"string"},"description":"Service ids (the slugs shown by /api/v1/services); case-insensitive, duplicates ignored"}}},"example":{"ids":["10x402.com","example.invalid"]}}}},"responses":{"200":{"description":"Records found and ids missing","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"400":{"description":"Invalid request (codes: invalid_json, invalid_endpoint, invalid_ids, missing_query)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"batchGetServicesByQuery","summary":"Bulk lookup over GET for clients that cannot send a body","parameters":[{"name":"ids","in":"query","required":true,"schema":{"type":"string"},"description":"Comma-separated service ids, 1 to 100","example":"10x402.com,example.invalid"}],"responses":{"200":{"description":"Records found and ids missing","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchResult"}}}},"400":{"description":"Invalid request (codes: invalid_json, invalid_endpoint, invalid_ids, missing_query)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/services/{id}":{"get":{"operationId":"getService","summary":"One service with probe history and listed endpoints","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"example":"10x402.com"},{"name":"resources_after","in":"query","schema":{"type":"string","pattern":"^[a-f0-9]{64}$"},"description":"The resources.next_after value from the previous page."},{"name":"resources_limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}}],"responses":{"200":{"description":"The service plus its last 20 liveness probes","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceDetail"}}}},"400":{"description":"Invalid resource pagination (code: invalid_resource_pagination)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown id or path (code: not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/stats":{"get":{"operationId":"getStats","summary":"Census counts","responses":{"200":{"description":"Totals for the whole index","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Deprecation":{"$ref":"#/components/headers/Deprecation"},"Sunset":{"$ref":"#/components/headers/Sunset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stats"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/purchase":{"get":{"operationId":"getPurchasePreview","summary":"HTTP purchase preview: the journey, the supported payment combination, and who does each step","description":"Read-only. Describes the steps of a purchase (discover, create, approve, result, reconcile, receipts), who does each and where, and links to the services, the purchases endpoint and the quickstart. Testnet only.","responses":{"200":{"description":"The preview's entry document","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"type":"object"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/purchase/services":{"get":{"operationId":"listPurchaseServices","summary":"Services an agent can buy through the HTTP purchase preview, with exact payment facts","description":"Every service with its request (GET endpoint, closed-set query parameters) and the payment its seller's 402 will ask for. Cached 60 seconds. Testnet only; services with simulated: true return prepared, simulated output.","responses":{"200":{"description":"The purchasable services","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"type":"object","required":["generated_at","notice","supported","services"],"properties":{"generated_at":{"type":"string","format":"date-time"},"notice":{"type":"string"},"authority":{"type":"string"},"supported":{"type":"object","description":"The one protocol, network and asset combination this preview supports"},"links":{"type":"object"},"services":{"type":"array","items":{"$ref":"#/components/schemas/PurchaseService"}}}}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/purchase/services/{id}":{"get":{"operationId":"getPurchaseService","summary":"One service of the HTTP purchase preview","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{0,99}$"},"example":"superstables-demo-market-data"}],"responses":{"200":{"description":"The service","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"type":"object","properties":{"notice":{"type":"string"},"service":{"$ref":"#/components/schemas/PurchaseService"}}}}}},"400":{"description":"Not a service id (code: invalid_id)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown id or path (code: not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/purchase/status":{"get":{"operationId":"getPurchaseStatus","summary":"Whether purchases can work right now","description":"Purchases switched on, the database answering, and the Base Sepolia chain moving. Cached for 30 seconds.","responses":{"200":{"description":"status: ok","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"ok"},"checked_at":{"type":"string","format":"date-time","description":"When the database and chain were last checked (at most 20 seconds ago)"},"purchases_enabled":{"type":"boolean"},"checks":{"type":"object","properties":{"database":{"type":"string"},"chain":{"type":"object"}}},"notice":{"type":"string"}}}}}},"503":{"description":"status: degraded; purchases may fail or wait","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"degraded"}}}}}}}}},"/api/v1/purchases":{"post":{"operationId":"createPurchase","summary":"Create a purchase: the owner will be asked to approve it","description":"No key. Refuses a service above max_amount (409 price_above_max), then reads the seller's actual 402 terms and refuses (409 terms_mismatch) unless they equal the listing. Nothing is signed or paid. The answer carries access_token (a bearer token for this purchase only, shown once) and approval.url for the owner, which must be passed on exactly as written, including the part after #. The owner has 10 minutes. Limits without a key: 10 creations per 10 minutes and 3 purchases waiting for an owner per caller network (IPv4 address or IPv6 /64). Not available to scripts on other websites: no CORS headers, and the body must be sent as application/json.","parameters":[{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean"},"description":"true: run every check a creation makes (inputs, max_amount, the seller's live terms against the listing) and create nothing: no purchase, no approval link, the owner is not asked. 200 with dry_run: true, the request and the terms the owner would sign. Its own limit: 30 per 10 minutes. Idempotency-Key is ignored."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":16,"maxLength":255},"description":"Use a UUID, made once before the first attempt and kept: a retry after a lost answer must send the same key (a new key creates a second purchase). Within an hour of creation, repeat the same request with the same key, from the same network, to get the same purchase back (200, replayed: true, Idempotent-Replayed: true) with a new access_token; the old one stops working. Before the owner acts, the approval link is new too; after, no approval link is sent and the owner keeps theirs. The same key with a different request: 422 idempotency_key_reused; after an hour: 409 idempotency_key_expired."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["service_id"],"additionalProperties":false,"properties":{"service_id":{"type":"string","example":"superstables-demo-market-data"},"params":{"type":"object","additionalProperties":{"type":"string"},"example":{"asset":"BTC"},"description":"Only the listed names and values are accepted"},"max_amount":{"type":"string","example":"0.01","description":"Optional: the most the agent agrees to pay, in USDC, at most 6 decimals. A service above it is refused before anything is created."}}}}}},"responses":{"200":{"description":"The same purchase, replayed for a repeated Idempotency-Key, with a new access_token (and, before the owner started approving, a new approval link); the earlier ones stop working","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"},"Idempotent-Replayed":{"schema":{"type":"string","const":"true"}}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Purchase"},{"type":"object","required":["access_token","approval","replayed"],"properties":{"access_token":{"type":"string","description":"Bearer token for this purchase (sspt_test_...). It can read, reconcile and cancel it; it cannot approve or pay."},"approval":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Absent on a replay after the owner started approving: they keep the approval link they have"},"match_code":{"type":"string","description":"The owner picks this code from three on the approval page"},"expires_at":{"type":"string","format":"date-time"}}},"message_for_owner":{"type":"string","description":"Absent when approval.url is"},"replayed":{"type":"boolean"},"replay_note":{"type":"string","description":"On a replay after the owner started approving: why no approval link is sent"}}}]}}}},"202":{"description":"Accepted: the purchase exists and is waiting for the owner's approval. Acceptance does not mean payment or delivery has completed. Poll the Location URL (also links.self) with GET ?wait=20 until final is true, then check the outcome.","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"},"Location":{"description":"The purchase to poll: /api/v1/purchases/{id}","schema":{"type":"string","format":"uri"}}},"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Purchase"},{"type":"object","required":["access_token","approval","replayed"],"properties":{"access_token":{"type":"string","description":"Bearer token for this purchase (sspt_test_...). It can read, reconcile and cancel it; it cannot approve or pay."},"approval":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Absent on a replay after the owner started approving: they keep the approval link they have"},"match_code":{"type":"string","description":"The owner picks this code from three on the approval page"},"expires_at":{"type":"string","format":"date-time"}}},"message_for_owner":{"type":"string","description":"Absent when approval.url is"},"replayed":{"type":"boolean"},"replay_note":{"type":"string","description":"On a replay after the owner started approving: why no approval link is sent"}}}]}}}},"400":{"description":"Invalid request (codes: invalid_json, invalid_request, invalid_params, unknown_parameter, invalid_idempotency_key)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Purchases without a key are switched off on this deployment (code: anonymous_purchases_disabled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown service (code: unknown_service)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"codes: price_above_max, terms_mismatch, service_unavailable, idempotency_key_expired (the key created a purchase over an hour ago; send a new key), idempotency_conflict (retry after Retry-After seconds)","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The body is over 4,000 bytes (code: body_too_large)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"The body was not sent as application/json (code: unsupported_media_type)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"This Idempotency-Key was used for a different request (code: idempotency_key_reused)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests (code: rate_limited), or 3 purchases from this caller already wait for an owner (code: too_many_pending). Wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"The seller could not be reached or did not answer with its terms (code: seller_error)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/purchases/{id}":{"get":{"operationId":"getPurchase","summary":"Read a purchase; wait for it to become final","description":"Every read first expires an unanswered approval and reconciles an unknown payment from the chain (at most every 20 seconds). Repeat until final is true: one read with wait=20 at a time is enough, and it returns as soon as the purchase is final. Reads are limited to 120 a minute per caller. Never buy again while payment.status is unknown or unconfirmed.","security":[{"purchaseToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"wait","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":20,"default":0},"description":"Seconds to hold the answer until the purchase is final"}],"responses":{"200":{"description":"The purchase","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Purchase"}}}},"401":{"description":"No Authorization header (code: missing_token): send the purchase's access_token as a bearer token","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"schema":{"type":"string"},"example":"Bearer realm=\"superstables\""}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No purchase with this id for this token (code: not_found); check both against the response that created it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/purchases/{id}/reconcile":{"post":{"operationId":"reconcilePurchase","summary":"Check an unknown or unconfirmed payment against the chain now","description":"Reads only; never pays. At most one chain read per purchase every 20 seconds. A GET does the same.","security":[{"purchaseToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The purchase","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Purchase"}}}},"401":{"description":"No Authorization header (code: missing_token): send the purchase's access_token as a bearer token","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"schema":{"type":"string"},"example":"Bearer realm=\"superstables\""}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No purchase with this id for this token (code: not_found); check both against the response that created it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/purchases/{id}/cancel":{"post":{"operationId":"cancelPurchase","summary":"Withdraw a purchase nobody has signed","security":[{"purchaseToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The purchase, now denied with reason_code agent_cancelled","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Purchase"}}}},"401":{"description":"No Authorization header (code: missing_token): send the purchase's access_token as a bearer token","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"WWW-Authenticate":{"schema":{"type":"string"},"example":"Bearer realm=\"superstables\""}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No purchase with this id for this token (code: not_found); check both against the response that created it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The purchase is no longer awaiting approval (code: not_awaiting_approval)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/budget/links":{"post":{"operationId":"createBudgetLink","summary":"Ask to add an agent to its owner's account (budgets on superstables.com, testnet)","description":"An add-agent request (kind link), signed with the agent's key. The owner opens approval.url, signs in, picks the match code and adds the agent; adding it moves no funds. With then, the same page asks the owner's wallet for a gas top-up and a grant, in order, each judged like an approval of its kind; the request is final once the add-agent action and every step are. Testnets only: rail evm on six EVM chains, tempo on moderato, solana on devnet (where the owner's Solana wallet also signs an owner-proof message). https://www.superstables.com/docs/budgets.md","parameters":[{"name":"Superstables-Agent","in":"header","required":true,"schema":{"type":"string","pattern":"^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$"},"description":"The agent's address (evm, tempo) or its ed25519 public key in base58 (solana); must equal the body's agent and the signer"},{"name":"Superstables-Agent-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"},"description":"Unix seconds, within 300 seconds of the server's clock"},{"name":"Superstables-Agent-Nonce","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"},"description":"16 random bytes in lowercase hex, new for every request; accepted once per agent within 10 minutes"},{"name":"Superstables-Agent-Signature","in":"header","required":true,"schema":{"type":"string","pattern":"^0x([0-9a-fA-F]{130}|[0-9a-fA-F]{128})$"},"description":"Agent request proof v2. EIP-191 personal_sign by the agent key (evm, tempo), or a 64-byte ed25519 signature over the UTF-8 text (solana), of \"Superstables agent request v2\\norigin: https://www.superstables.com\\n<METHOD> <path>\\n<sha256 hex of the raw body>\\n<timestamp>\\n<nonce>\". The origin line is this site's own origin, not the request's Host."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":16,"maxLength":255},"description":"Same agent, key and body within an hour: 409 duplicate_request naming the request already made; no new token is issued"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rail","chain","agent"],"additionalProperties":false,"properties":{"rail":{"type":"string","enum":["evm","tempo","solana"]},"chain":{"type":"string","enum":["base-sepolia","arc-testnet","arbitrum-sepolia","polygon-amoy","skale-base-sepolia","ethereum-sepolia","robinhood-testnet","moderato","devnet"],"description":"evm: the seven EVM testnets; tempo: moderato; solana: devnet"},"agent":{"type":"string"},"label":{"type":"string","maxLength":40},"then":{"type":"array","maxItems":2,"description":"Wallet steps after the agent is added, in order; each kind at most once (on tempo, a grant only). Each has transaction, or on solana solana","items":{"type":"object","required":["kind"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["fund_agent","grant"]},"transaction":{"type":"object","required":["to"],"additionalProperties":false,"properties":{"to":{"type":"string"},"data":{"type":"string","default":"0x"},"value":{"type":"string","default":"0x0","description":"Hex quantity"}}},"solana":{"type":"object","additionalProperties":false,"description":"Solana only, instead of transaction: USDC atomic units for a grant, lamports for fund_agent, absent for a revoke","properties":{"amount_atomic":{"type":"string","pattern":"^[0-9]{1,20}$"}}}}}}}}}}},"responses":{"200":{"description":"The agent has already been added on this chain: a new final request recording the existing account relationship (state linked, already_linked: true, owner, owner_proof naming the original add-agent request and its match code, a new access_token; no approval.url)","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BudgetRequest"},{"type":"object","properties":{"access_token":{"type":"string","pattern":"^ssbt_test_[A-Za-z0-9_-]{43}$"},"approval":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"match_code":{"type":"string","example":"KPT-RWD"},"expires_at":{"type":"string","format":"date-time"}}},"message_for_owner":{"type":"string"},"replayed":{"type":"boolean"}}}]}}}},"201":{"description":"Created; waiting for the owner","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BudgetRequest"},{"type":"object","properties":{"access_token":{"type":"string","pattern":"^ssbt_test_[A-Za-z0-9_-]{43}$"},"approval":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"match_code":{"type":"string","example":"KPT-RWD"},"expires_at":{"type":"string","format":"date-time"}}},"message_for_owner":{"type":"string"},"replayed":{"type":"boolean"}}}]}}}},"400":{"description":"Invalid request (codes: invalid_json, unknown_parameter, unsupported_rail, unsupported_chain, invalid_label, invalid_idempotency_key, invalid_then: a step of then breaks a rule; step and reason_code name it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"code: agent_proof, with reason missing, malformed, stale, nonce, signature (also another origin, method, path or body), agent_mismatch or replayed (the nonce was used in the last 10 minutes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"codes: request_open, proof_reused, duplicate_request, idempotency_conflict, already_linked (an add-agent request with then for an agent already added on this chain; owner names the account and owner_proof holds the owner's signature over the original owner-proof message)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The body is over 4,000 bytes (code: body_too_large)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"The body was not sent as application/json (code: unsupported_media_type)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/budget/approvals":{"post":{"operationId":"createBudgetApproval","summary":"Ask the owner to send one transaction: grant, revoke or fund_agent","description":"Signed with the agent's key; the agent must have been added on this chain. The server decodes the transaction and accepts only approve(agent, cap) on the chain's USDC (cap above 0, at most 100 test USDC), approve(agent, 0), or a gas top-up to the agent (at most 0.05 native; on Arc Testnet at most 1 USDC, also as transfer(agent, amount)). On tempo: authorizeKey or revokeKey on the keychain, as the client writes them. On solana the agent sends intent (solana.amount_atomic) and the server builds the transaction when the owner approves. https://www.superstables.com/docs/budgets.md","parameters":[{"name":"Superstables-Agent","in":"header","required":true,"schema":{"type":"string","pattern":"^(0x[0-9a-fA-F]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$"},"description":"The agent's address (evm, tempo) or its ed25519 public key in base58 (solana); must equal the body's agent and the signer"},{"name":"Superstables-Agent-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+$"},"description":"Unix seconds, within 300 seconds of the server's clock"},{"name":"Superstables-Agent-Nonce","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"},"description":"16 random bytes in lowercase hex, new for every request; accepted once per agent within 10 minutes"},{"name":"Superstables-Agent-Signature","in":"header","required":true,"schema":{"type":"string","pattern":"^0x([0-9a-fA-F]{130}|[0-9a-fA-F]{128})$"},"description":"Agent request proof v2. EIP-191 personal_sign by the agent key (evm, tempo), or a 64-byte ed25519 signature over the UTF-8 text (solana), of \"Superstables agent request v2\\norigin: https://www.superstables.com\\n<METHOD> <path>\\n<sha256 hex of the raw body>\\n<timestamp>\\n<nonce>\". The origin line is this site's own origin, not the request's Host."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":16,"maxLength":255},"description":"Same agent, key and body within an hour: 409 duplicate_request naming the request already made; no new token is issued"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","rail","chain","agent"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["grant","revoke","fund_agent"]},"rail":{"type":"string","enum":["evm","tempo","solana"]},"chain":{"type":"string","enum":["base-sepolia","arc-testnet","arbitrum-sepolia","polygon-amoy","skale-base-sepolia","ethereum-sepolia","robinhood-testnet","moderato","devnet"],"description":"evm: the seven EVM testnets; tempo: moderato; solana: devnet"},"agent":{"type":"string"},"transaction":{"type":"object","required":["to"],"additionalProperties":false,"properties":{"to":{"type":"string"},"data":{"type":"string","default":"0x"},"value":{"type":"string","default":"0x0","description":"Hex quantity"}}},"solana":{"type":"object","additionalProperties":false,"description":"Solana only, instead of transaction: USDC atomic units for a grant, lamports for fund_agent, absent for a revoke","properties":{"amount_atomic":{"type":"string","pattern":"^[0-9]{1,20}$"}}}}}}}},"responses":{"201":{"description":"Created; waiting for the owner","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/BudgetRequest"},{"type":"object","properties":{"access_token":{"type":"string","pattern":"^ssbt_test_[A-Za-z0-9_-]{43}$"},"approval":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"match_code":{"type":"string","example":"KPT-RWD"},"expires_at":{"type":"string","format":"date-time"}}},"message_for_owner":{"type":"string"},"replayed":{"type":"boolean"}}}]}}}},"400":{"description":"Invalid request (codes: invalid_json, unknown_parameter, unsupported_rail, unsupported_chain, invalid_label, invalid_kind, invalid_transaction, invalid_solana, invalid_idempotency_key)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"code: agent_proof, with reason missing, malformed, stale, nonce, signature (also another origin, method, path or body), agent_mismatch or replayed (the nonce was used in the last 10 minutes)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"code: agent_not_linked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"codes: request_open (one open request per agent and chain; open_request names it), proof_reused (this signed request was already received; id names its request), duplicate_request (this Idempotency-Key already made request id), idempotency_conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"The body is over 4,000 bytes (code: body_too_large)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"The body was not sent as application/json (code: unsupported_media_type)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"The transaction is not one of the accepted shapes (codes: wrong_target, wrong_call, wrong_spender, wrong_recipient, wrong_amount, value_not_zero, cap_zero, cap_above_limit, unlimited_refused, amount_zero, amount_above_limit, expiry_passed), or idempotency_key_reused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/budget/requests/{id}":{"get":{"operationId":"getBudgetRequest","summary":"Read a budget request; wait for it to become final","description":"Every read expires an unanswered request and reads a reported transaction from the chain. confirmed means it succeeded and matches the stored transaction; read the chain yourself before reporting success. 120 reads a minute.","security":[{"budgetToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^b[la]_[0-9a-f]{32}$"}},{"name":"wait","in":"query","required":false,"schema":{"type":"integer","minimum":0,"maximum":20,"default":0}}],"responses":{"200":{"description":"The request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetRequest"}}}},"401":{"description":"No Authorization header (code: missing_token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No request with this id for this token (code: not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/budget/requests/{id}/cancel":{"post":{"operationId":"cancelBudgetRequest","summary":"Withdraw a request before the owner's wallet is asked","security":[{"budgetToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^b[la]_[0-9a-f]{32}$"}}],"responses":{"200":{"description":"The request, now cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BudgetRequest"}}}},"401":{"description":"No Authorization header (code: missing_token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No request with this id for this token (code: not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"Method not allowed (code: method_not_allowed); the Allow header and error.allowed name the methods that are","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"},"Allow":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"wallet_asked (the owner's wallet was asked; wallet_asked: true) or not_open","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"Request-Id":{"$ref":"#/components/headers/RequestId"},"RateLimit":{"$ref":"#/components/headers/PurchaseRateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/PurchaseRateLimitPolicy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure. If the purchase may already have paid, or its record could not be read, the code is payment_outcome_unknown (money_moved: \"unknown\", retryable: false): do not buy again; read the purchase. Otherwise, the code is internal_error; nothing was paid by this request (money_moved: false, retryable: true)","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","headers":{"Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/budget/services":{"get":{"operationId":"listBudgetServices","summary":"Services a budget approved on superstables.com can pay","description":"A curated list of testnet services checked by hand, and the Superstables demo services: x402 exact in test USDC (test USDG on Robinhood Chain Testnet), each on the chain it names (Base Sepolia, or Arc Testnet for one). ?chain= narrows the list to one chain. Public, no key, CORS open.","parameters":[{"name":"chain","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"The services","content":{"application/json":{"schema":{"type":"object","properties":{"services":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":"string"},"method":{"type":"string"},"price":{"type":"object","properties":{"amount":{"type":"string"},"asset":{"type":"string"},"atomic":{"type":"string"}}},"network":{"type":"string"},"chain":{"type":"string"},"category":{"type":"string"},"description":{"type":"string"},"operator":{"type":"string"},"sample":{"type":"boolean"}}}}}}}}},"400":{"description":"Unknown or mainnet chain (code: unsupported_chain)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/demo/market":{"get":{"operationId":"getDemoMarketData","summary":"Demo market data, paid per request with x402 (test USDC on Base Sepolia)","description":"A paid endpoint Superstables operates for testing x402 payments: spot price and 24h change for one asset. Without a PAYMENT-SIGNATURE header, a valid request returns 402 with the payment terms. With a signed payment payload, the service verifies and settles the payment through a public facilitator before returning the result. Parameters are checked before payment is demanded. Listed in /discovery/resources.","parameters":[{"name":"asset","in":"query","required":true,"schema":{"type":"string","enum":["BTC","ETH"]},"example":"BTC"}],"responses":{"200":{"description":"Paid and settled: the result, with the settlement in PAYMENT-RESPONSE","headers":{"PAYMENT-RESPONSE":{"description":"x402 v2 settlement response, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid parameters; nothing is demanded or charged. The body names the allowed values","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"allowed":{}}}}}},"402":{"description":"Payment required (x402 v2). PAYMENT-REQUIRED carries the terms as base64-encoded JSON, the same terms as the body: test USDC on Base Sepolia (eip155:84532). Sign a payment for these terms, then repeat the request with the signed payment payload in a PAYMENT-SIGNATURE header. Payment is complete only after settlement succeeds.","headers":{"PAYMENT-REQUIRED":{"description":"x402 v2 PaymentRequired, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/X402PaymentRequired"}}}},"503":{"description":"No payment address is configured on this deployment, or the upstream source is unavailable; nothing is charged","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/demo/market/arc":{"get":{"operationId":"getDemoMarketDataArc","summary":"Demo market data, paid per request with x402 (test USDC on Arc Testnet)","description":"The same market data, sold over x402 v2 exact in test USDC on Arc Testnet (eip155:5042002, USDC at 0x3600000000000000000000000000000000000000, EIP-712 domain USDC version 2). The payment is one EIP-3009 authorization the buyer signs; Circle's Facilitator Service settles it and pays the gas. Listed in /api/v1/purchase/services when this deployment operates it.","parameters":[{"name":"asset","in":"query","required":true,"schema":{"type":"string","enum":["BTC","ETH"]},"example":"BTC"}],"responses":{"200":{"description":"Paid and settled: the result, with the settlement in PAYMENT-RESPONSE","headers":{"PAYMENT-RESPONSE":{"description":"x402 v2 settlement response, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid parameters; nothing is demanded or charged. The body names the allowed values","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"allowed":{}}}}}},"402":{"description":"Payment required (x402 v2). PAYMENT-REQUIRED carries the terms as base64-encoded JSON, the same terms as the body: test USDC on Base Sepolia (eip155:84532). Sign a payment for these terms, then repeat the request with the signed payment payload in a PAYMENT-SIGNATURE header. Payment is complete only after settlement succeeds.","headers":{"PAYMENT-REQUIRED":{"description":"x402 v2 PaymentRequired, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/X402PaymentRequired"}}}},"503":{"description":"No payment address is configured on this deployment, or the upstream source is unavailable; nothing is charged","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/demo/market/robinhood":{"get":{"operationId":"getDemoMarketDataRobinhood","summary":"Demo market data, paid per request with x402 (test USDG on Robinhood Chain Testnet)","description":"The same market data, sold over x402 v2 exact in test USDG on Robinhood Chain Testnet (eip155:46630, USDG at 0x7E955252E15c84f5768B83c41a71F9eba181802F, EIP-712 domain Global Dollar version 1). The payment is one EIP-3009 authorization the buyer signs; no public facilitator settles this chain, so the seller submits it itself and pays the gas. Listed in /api/v1/purchase/services when this deployment operates it.","parameters":[{"name":"asset","in":"query","required":true,"schema":{"type":"string","enum":["BTC","ETH"]},"example":"BTC"}],"responses":{"200":{"description":"Paid and settled: the result, with the settlement in PAYMENT-RESPONSE","headers":{"PAYMENT-RESPONSE":{"description":"x402 v2 settlement response, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid parameters; nothing is demanded or charged. The body names the allowed values","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"allowed":{}}}}}},"402":{"description":"Payment required (x402 v2). PAYMENT-REQUIRED carries the terms as base64-encoded JSON, the same terms as the body: test USDC on Base Sepolia (eip155:84532). Sign a payment for these terms, then repeat the request with the signed payment payload in a PAYMENT-SIGNATURE header. Payment is complete only after settlement succeeds.","headers":{"PAYMENT-REQUIRED":{"description":"x402 v2 PaymentRequired, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/X402PaymentRequired"}}}},"503":{"description":"No payment address is configured on this deployment, or the upstream source is unavailable; nothing is charged","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/demo/market/tempo":{"get":{"operationId":"getDemoMarketDataTempo","summary":"Demo market data, paid per request with MPP (test pathUSD on Tempo Moderato)","description":"The same market data, sold over MPP with tempo.charge in test pathUSD on Tempo Moderato (eip155:42431). Without an Authorization: Payment credential, a valid request returns 402 with a WWW-Authenticate: Payment challenge. The payment is a pathUSD transfer with the challenge's memo, sent by the buyer (credential type hash) or signed for the server to send (type transaction); it is checked on chain before the result is returned, with a Payment-Receipt header. Listed in /api/v1/purchase/services when this deployment operates it.","parameters":[{"name":"asset","in":"query","required":true,"schema":{"type":"string","enum":["BTC","ETH"]},"example":"BTC"}],"responses":{"200":{"description":"The market data, after the payment was checked on chain. Payment-Receipt carries the receipt."},"400":{"description":"The request is invalid; nothing was asked for or charged."},"402":{"description":"Payment required: the WWW-Authenticate: Payment challenge (MPP, tempo.charge)."},"503":{"description":"Not configured on this deployment, or the price feed is down; the payment was not used."}}}},"/api/demo/market/solana":{"get":{"operationId":"getDemoMarketDataSolana","summary":"Demo market data, paid per request with x402 (test USDC on Solana devnet)","description":"The same market data, sold over x402 v2 exact in test USDC on Solana devnet (solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1). The terms name the facilitator's fee payer (extra.feePayer); the payment is a transaction the buyer signs as the owner of the USDC. Listed in /api/v1/purchase/services when this deployment operates it.","parameters":[{"name":"asset","in":"query","required":true,"schema":{"type":"string","enum":["BTC","ETH"]},"example":"BTC"}],"responses":{"200":{"description":"Paid and settled: the result, with the settlement in PAYMENT-RESPONSE","headers":{"PAYMENT-RESPONSE":{"description":"x402 v2 settlement response, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid parameters; nothing is demanded or charged. The body names the allowed values","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"allowed":{}}}}}},"402":{"description":"Payment required (x402 v2). PAYMENT-REQUIRED carries the terms as base64-encoded JSON, the same terms as the body: test USDC on Base Sepolia (eip155:84532). Sign a payment for these terms, then repeat the request with the signed payment payload in a PAYMENT-SIGNATURE header. Payment is complete only after settlement succeeds.","headers":{"PAYMENT-REQUIRED":{"description":"x402 v2 PaymentRequired, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/X402PaymentRequired"}}}},"503":{"description":"No payment address is configured on this deployment, or the upstream source is unavailable; nothing is charged","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/demo/services/{service}":{"get":{"operationId":"callDemoService","summary":"Prepared demo services, paid per request with x402 (test USDC on Base Sepolia; simulated output)","description":"Ten paid endpoints Superstables operates for testing x402 payments end to end. Each returns prepared, simulated output, marked as such. Each service's query parameters, closed value sets and price are in /api/demo/catalogue; the example below is a valid call. Unpaid, a valid request answers 402 with its terms.","parameters":[{"name":"service","in":"path","required":true,"schema":{"type":"string","enum":["wallet-briefing","contract-screening","web-search","whitepaper-extraction","specialist-research","image-creation","audio-transcription","product-search","job-search","website-performance"]},"example":"wallet-briefing"},{"name":"sample_wallet","in":"query","required":false,"schema":{"type":"string"},"description":"Which prepared sample wallet to brief on. These are fictional wallets, not the owner's. (wallet-briefing; other services take other parameters, see /api/demo/catalogue)","example":"demo-active"},{"name":"period","in":"query","required":false,"schema":{"type":"string"},"description":"How far back the briefing looks. (wallet-briefing; other services take other parameters, see /api/demo/catalogue)","example":"7d"}],"responses":{"200":{"description":"Paid and settled: the result, with the settlement in PAYMENT-RESPONSE","headers":{"PAYMENT-RESPONSE":{"description":"x402 v2 settlement response, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid parameters; nothing is demanded or charged. The body names the allowed values","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"allowed":{}}}}}},"402":{"description":"Payment required (x402 v2). PAYMENT-REQUIRED carries the terms as base64-encoded JSON, the same terms as the body: test USDC on Base Sepolia (eip155:84532). Sign a payment for these terms, then repeat the request with the signed payment payload in a PAYMENT-SIGNATURE header. Payment is complete only after settlement succeeds.","headers":{"PAYMENT-REQUIRED":{"description":"x402 v2 PaymentRequired, base64-encoded JSON","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/X402PaymentRequired"}}}},"503":{"description":"No payment address is configured on this deployment, or the upstream source is unavailable; nothing is charged","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/v1/submit":{"post":{"operationId":"submitService","summary":"Submit a service for listing","description":"Submit an HTTP(S) endpoint for review. We probe new submissions immediately. New submissions enter the review queue and require approval before they enter the directory crawl pipeline. Repeating an endpoint submission within 24 hours does not update the earlier submission; the response is { ok: true, deduplicated: true }.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["endpoint"],"properties":{"endpoint":{"type":"string","format":"uri"},"name":{"type":"string"},"contact":{"type":"string"}}}}}},"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Optional client key; repeated submissions of the same endpoint within 24h are deduplicated and echoed back."}],"responses":{"200":{"description":"Accepted into the moderation queue (deduplicated: true when a recent identical submission exists)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitResult"}}}},"400":{"description":"Invalid request (codes: invalid_json, invalid_endpoint, invalid_ids, missing_query)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"default":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Service":{"type":"object","properties":{"id":{"type":"string","description":"Stable slug of the endpoint host"},"name":{"type":"string"},"category":{"type":["string","null"]},"description":{"type":["string","null"]},"rails":{"type":"array","items":{"type":"string","enum":["x402","mpp","acp"]}},"chains":{"type":"array","items":{"type":"string"},"description":"base, solana, tempo, ... (unknown chains keep their CAIP-2 id)"},"assets":{"type":"array","items":{"type":"string"},"description":"USDC, EURC, USDT, PYUSD, USDG, ..."},"price":{"type":"object","description":"The listing's price, or, when the listing has none, the amount the service asked for in its last payment challenge (from: listing | challenge | null).","properties":{"display":{"type":["string","null"]},"usd":{"type":["number","null"]},"from":{"type":["string","null"],"enum":["listing","challenge",null]}}},"payment_terms":{"type":["object","null"],"description":"The x402 payment requirement the service offered in its last answered check, or null before one was read.","properties":{"x402_version":{"type":["integer","null"]},"scheme":{"type":["string","null"]},"network":{"type":["string","null"]},"asset":{"type":["string","null"]},"asset_address":{"type":["string","null"]},"amount_atomic":{"type":["string","null"]},"pay_to":{"type":["string","null"]},"other_offers":{"type":"integer","description":"Other chains or assets the same challenge offered"},"seen_at":{"type":["string","null"],"format":"date-time"}}},"endpoint":{"type":"string","description":"The URL that answers the payment challenge"},"facilitator":{"type":["string","null"]},"live":{"type":["boolean","null"],"description":"true = answered a payment challenge on the last probe; null = not yet probed (e.g. acp://)"},"last_seen_live":{"type":["string","null"],"format":"date-time"},"first_indexed":{"type":"string","format":"date-time"},"sources":{"type":"array","items":{"type":"string"},"description":"Directories this service was found in"}}},"BudgetRequest":{"type":"object","required":["id","kind","state","final","owner","tx_hash","wallet_asked","reason","next_action"],"properties":{"id":{"type":"string","example":"ba_5f0c2e7a9b1d4c3e8f6a0b2c4d6e8f10"},"kind":{"type":"string","enum":["link","grant","revoke","fund_agent"]},"state":{"type":"string","enum":["awaiting_owner","sending","sent","linked","confirmed","failed","unknown","rejected","expired","cancelled"],"description":"sending and unknown: the owner's wallet was asked and may have sent it. On solana, sent once the server recorded the signed transaction's signature, until the chain shows it or its blockhash expires (failed, not_landed), even if the network refused it (reason_code network_refused). An add-agent request with steps: sending or sent while its open step is with the wallet or the chain; otherwise, once a step may have moved funds and another has not completed, unknown with reason_code partly_done, final false while a step is still open (next_action wait_for_owner on the owner's turn). Read each step"},"final":{"type":"boolean"},"owner":{"type":["string","null"],"description":"The owner's account address (on solana, the Solana address the owner proved): set from creation for transactions, once the agent is added for add-agent requests (kind link)"},"owner_proof":{"type":["object","null"],"description":"Add-agent requests (kind link) only. Once the agent is added: the owner's wallet signature over the owner-proof message, \"Superstables: add an agent to my account\\nsite: <origin>\\nowner: <owner>\\nagent: <agent>\\nrail: <rail>\\nchain: <chain>\\nrequest: <bl_ id>\\ncode: <match code>\". Rebuild it from your own values, require it to equal message, and verify the signature for owner before you record an owner. Null until the agent is added.","required":["scheme","message","signature"],"properties":{"scheme":{"type":"string","enum":["eip191","ed25519"]},"message":{"type":"string"},"signature":{"type":"string","pattern":"^0x([0-9a-fA-F]{130}|[0-9a-fA-F]{128})$"}}},"tx_hash":{"type":["string","null"],"description":"On solana, the transaction's signature in base58. Always null for an add-agent request: its transactions are on its steps"},"wallet_asked":{"type":"boolean","description":"The owner's page asked the wallet to send it (on solana, to sign it). On evm and tempo it may have been sent from then on; on solana, once tx_hash is set"},"reason":{"type":["string","null"]},"reason_code":{"type":["string","null"],"description":"Why the request is in this state. partly_done: one step of an add-agent request may have moved funds and another is incomplete; read each step. network_refused, while sent on solana: the network refused the transaction, but it may still have been sent. While awaiting_owner, ethereum_account_required: an owner signed in with a Solana account tried to approve, and this chain needs an Ethereum account; the request stays open"},"next_action":{"type":"object","properties":{"type":{"type":"string","enum":["wait_for_owner","wait_for_chain","verify_on_chain","none"]},"poll":{"type":"string"},"tx_hash":{"type":"string"},"tx_hashes":{"type":"array","items":{"type":"string"},"description":"An add-agent request with steps: every transaction the owner's wallet reported"}}},"chain":{"type":"object"},"agent":{"type":"string"},"transaction":{"type":"object","description":"Transactions only: as stored and sent by the owner's wallet"},"terms":{"type":"object","description":"Transactions only: the server's decoding, as the owner's page shows it"},"link_state":{"type":"string","description":"An add-agent request with steps: the state of the add-agent action; state is then the whole request's"},"steps":{"type":"array","description":"An add-agent request with steps: each wallet step, in order. state also takes queued (not its turn yet) and skipped (never asked; nothing sent)","items":{"type":"object","properties":{"index":{"type":"integer"},"kind":{"type":"string","enum":["fund_agent","grant"]},"state":{"type":"string"},"tx_hash":{"type":["string","null"]},"reason":{"type":["string","null"]},"reason_code":{"type":["string","null"]},"wallet_asked":{"type":"boolean"},"transaction":{"type":"object"},"terms":{"type":"object"}}}},"expires_at":{"type":"string","format":"date-time"}}},"ServiceDetail":{"allOf":[{"$ref":"#/components/schemas/Service"},{"type":"object","properties":{"resources":{"type":"object","description":"Endpoints received from discovery sources, paged by stable resource ID. Methods, schemas and offers are source claims; these endpoints have no individual health result. A partial or failed crawl retains older entries. last_seen_at and source seen_at show when each entry was received.","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"next_after":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"endpoint":{"type":"string"},"method":{"type":["string","null"],"description":"Declared HTTP method, or null when not supplied. URLs may contain templates or identify only a provider."},"description":{"type":["string","null"]},"first_seen_at":{"type":"string","format":"date-time"},"last_seen_at":{"type":"string","format":"date-time"},"sources":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"source_url":{"type":["string","null"]},"seen_at":{"type":"string","format":"date-time"},"listings":{"type":"array","items":{"type":"object","additionalProperties":true,"description":"Last received source variants: endpoint, method, description, rails, chains, assets, priceUsd, priceDisplay, discovery schemas and offers."}}}}}}}}}},"source_urls":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"url":{"type":["string","null"]}}}},"probes":{"type":"array","description":"Last 20 liveness probes, newest first","items":{"type":"object","properties":{"probed_at":{"type":"string","format":"date-time"},"ok":{"type":"boolean"},"status_code":{"type":["integer","null"]},"method":{"type":["string","null"]},"latency_ms":{"type":["integer","null"]}}}}}}]},"ServiceList":{"type":"object","required":["generated_at","counts","page","services"],"properties":{"generated_at":{"type":"string","format":"date-time"},"counts":{"type":"object","properties":{"total":{"type":"integer"},"live":{"type":"integer"},"dual_rail":{"type":"integer"}}},"page":{"type":"object","description":"Pagination state for this response","required":["limit","offset","next_offset","next_cursor"],"properties":{"limit":{"type":"integer","description":"Page size actually applied (1-500)"},"offset":{"type":["integer","null"],"description":"Offset actually applied; null when the page was requested by cursor"},"next_offset":{"type":["integer","null"],"description":"Pass as offset to fetch the next page; null on the last page and when paging by cursor"},"next_cursor":{"type":["string","null"],"description":"Pass as cursor to fetch the next page; null when this was the last page"}}},"services":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}},"Stats":{"type":"object","properties":{"generated_at":{"type":"string","format":"date-time"},"total":{"type":"integer"},"live":{"type":"integer"},"probed":{"type":"integer"},"dual_rail":{"type":"integer"},"rails":{"type":"integer"},"last_probe_at":{"type":["string","null"]}}},"BatchResult":{"type":"object","required":["generated_at","requested","found","missing"],"properties":{"generated_at":{"type":"string","format":"date-time"},"requested":{"type":"integer","description":"Number of distinct ids looked up"},"found":{"type":"array","items":{"$ref":"#/components/schemas/Service"},"description":"Records found, in the order requested"},"missing":{"type":"array","items":{"type":"string"},"description":"Requested ids that are not in the index"}}},"SubmitResult":{"type":"object","required":["ok"],"properties":{"ok":{"type":"boolean"},"deduplicated":{"type":"boolean","description":"true when an identical recent submission already existed"}}},"PurchaseService":{"type":"object","required":["id","name","simulated","testnet","available","request","payment"],"properties":{"id":{"type":"string","description":"Stable id; pass it as service_id to POST /api/v1/purchases"},"name":{"type":"string"},"description":{"type":"string"},"operator":{"type":"string"},"simulated":{"type":"boolean","description":"true = the service returns prepared, simulated output"},"testnet":{"type":"boolean","const":true},"available":{"type":"boolean","description":"false = the seller cannot be paid right now; see unavailable_reason"},"unavailable_reason":{"type":"string"},"request":{"type":"object","properties":{"method":{"type":"string","const":"GET"},"endpoint":{"type":"string","format":"uri"},"params":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"in":{"type":"string","const":"query"},"required":{"type":"boolean"},"description":{"type":"string"},"enum":{"type":"array","items":{"type":"string"},"description":"The only accepted values"},"default":{"type":"string"},"example":{"type":"string"}}}},"unknown_params":{"type":"string","const":"rejected"},"example_url":{"type":"string","format":"uri"}}},"payment":{"type":"object","description":"What the seller's HTTP 402 must say. A buyer should refuse to sign anything else.","properties":{"protocol":{"type":"string","const":"x402"},"x402_version":{"type":"integer","const":2},"scheme":{"type":"string","const":"exact"},"network":{"type":"string","example":"eip155:84532"},"network_label":{"type":"string"},"asset":{"type":"object","properties":{"symbol":{"type":"string"},"address":{"type":"string"},"decimals":{"type":"integer"}}},"amount":{"type":"object","properties":{"decimal":{"type":"string","example":"0.003"},"atomic":{"type":"string","example":"3000"}}},"price_display":{"type":"string"},"pay_to":{"type":["string","null"],"description":"The recipient address; null when unavailable"},"max_timeout_seconds":{"type":"integer"}}},"returns":{"type":"object","additionalProperties":{"type":"string"}},"example_prompts":{"type":"array","items":{"type":"string"}},"links":{"type":"object","properties":{"self":{"type":"string"},"purchase":{"type":"string"},"quickstart":{"type":"string"}}}}},"X402PaymentRequired":{"type":"object","description":"x402 v2 payment terms (https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md)","required":["x402Version","resource","accepts"],"properties":{"x402Version":{"type":"integer","const":2},"error":{"type":"string"},"resource":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"description":{"type":"string"},"mimeType":{"type":"string"}}},"accepts":{"type":"array","items":{"type":"object","required":["scheme","network","amount","asset","payTo","maxTimeoutSeconds"],"properties":{"scheme":{"type":"string","example":"exact"},"network":{"type":"string","example":"eip155:84532"},"amount":{"type":"string","description":"Atomic units of the asset","example":"10000"},"asset":{"type":"string","description":"Token contract address"},"payTo":{"type":"string"},"maxTimeoutSeconds":{"type":"integer"},"extra":{"type":"object"}}}}}},"Purchase":{"type":"object","required":["id","state","final","livemode","service","request","terms","payment","delivery","message","next","next_action","links"],"properties":{"id":{"type":"string","format":"uuid"},"state":{"type":"string","enum":["awaiting_approval","submitting","settled","paid_service_failed","denied","expired","failed","uncertain"],"description":"Read final and payment.status together. Tempo uncertainty becomes final for the agent at the cap cutoff while payment reconciliation continues."},"final":{"type":"boolean","description":"true means the agent workflow has ended. An unresolved Tempo payment becomes final at challenge expiry plus three hours with next_action report_unknown; payment may still arrive and reconciliation continues. Otherwise false while awaiting approval, submitting, reconciling an uncertain or unconfirmed payment, or checking a pending receipt."},"livemode":{"type":"boolean","const":false,"description":"false: test money. The tokens say so too (sspt_test_, sspa_test_)."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"service":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"simulated":{"type":"boolean"},"testnet":{"type":"boolean"}}},"request":{"type":"object","properties":{"method":{"type":"string","const":"GET"},"url":{"type":"string","format":"uri"},"params":{"type":"object"}}},"terms":{"type":"object","description":"The payment as the seller charges it, checked against the listing","properties":{"amount":{"type":"object","properties":{"decimal":{"type":"string","example":"0.003"},"atomic":{"type":"string","example":"3000"}}},"asset":{"type":"object","properties":{"symbol":{"type":"string"},"address":{"type":"string"},"decimals":{"type":"integer"}}},"network":{"type":"string","example":"eip155:84532"},"network_label":{"type":"string"},"recipient":{"type":"string"},"protocol":{"type":"string","const":"x402"},"x402_version":{"type":"integer","const":2},"scheme":{"type":"string","const":"exact"},"authorization_window_seconds":{"type":"integer"}}},"approval":{"type":"object","properties":{"match_code":{"type":"string","example":"KPT-RWA"},"expires_at":{"type":"string","format":"date-time"}}},"payment":{"type":"object","properties":{"status":{"type":"string","enum":["awaiting_approval","submitting","paid","not_paid","unconfirmed","unknown"],"description":"unknown once a Tempo transfer was prepared for the owner's wallet and the chain does not show it, whatever the state, including while awaiting approval and on a purchase closed after that: next_action is wait_for_chain until the Tempo cutoff, then report_unknown. not_paid only where nothing was signed or handed out, or where the chain shows no payment happened and none can"},"basis":{"type":"string","description":"Whose word the status rests on"},"payer":{"type":"string"},"transaction":{"type":["string","null"]},"transaction_url":{"type":["string","null"]},"chain":{"type":"object","description":"What the chain says about this payment, read by Superstables: confirmed (the transaction shows the token's AuthorizationUsed and Transfer for exactly this authorization, in block), pending (not read yet, or not visible yet; the purchase is not final until it is, except that on Tempo Moderato the agent workflow becomes final at the cutoff, challenge expiry plus three hours, with next_action report_unknown, while the receipt stays pending), or contradicted (the seller reported it paid, but the authorization was never used and can no longer be; the purchase is then failed with reason_code settlement_contradicted, and payment.status not_paid)","properties":{"status":{"type":"string","enum":["confirmed","pending","contradicted"]},"block":{"type":["integer","null"]},"checked_at":{"type":["string","null"],"format":"date-time"}}},"authorization":{"type":"object","description":"Once the owner signed: the public half of their authorization. It can settle until valid_before; the chain is checked by this nonce.","properties":{"valid_before":{"type":["string","null"],"format":"date-time"},"nonce":{"type":"string"},"order":{"type":"string","description":"The order the nonce commits to: a JSON text naming this purchase, the service, the request, the payment terms and a random salt. keccak256 of its UTF-8 bytes is nonce, which the chain records with the transfer."}}}}},"delivery":{"type":"object","properties":{"status":{"type":"string","enum":["pending","delivered","failed","not_called","unknown"]},"simulated":{"type":"boolean"},"http_status":{"type":"integer"},"result":{"description":"The service's answer, as the seller sent it. For services marked simulated, the fields the listing names in returns are under result.data; the rest (service_id, mock, notice, summary, paid) is the service's own framing. The market data service puts them at the top level."}}},"receipt":{"type":["object","null"],"description":"Present when money moved; its id is the purchase id. There is no separate receipts endpoint: the agent reads it here, and the owner sees their latest 50 receipts on their account page.","properties":{"id":{"type":"string","format":"uuid"},"purchase_id":{"type":"string","format":"uuid"},"livemode":{"type":"boolean","const":false},"settled_at":{"type":["string","null"],"format":"date-time"},"service":{"type":"object"},"request_url":{"type":"string","format":"uri"},"terms":{"type":"object","description":"Amount, asset, network and recipient, as paid"},"payer":{"type":"string","description":"The address that signed the authorization"},"transaction":{"type":["string","null"]},"transaction_url":{"type":["string","null"]},"settlement_source":{"type":"string","enum":["facilitator","chain"],"description":"facilitator: its settlement report, through the seller; chain: the token contract shows the authorization used"},"chain":{"type":"object","description":"What the chain says about this payment, read by Superstables: confirmed (the transaction shows the token's AuthorizationUsed and Transfer for exactly this authorization, in block), pending (not read yet, or not visible yet; the purchase is not final until it is, except that on Tempo Moderato the agent workflow becomes final at the cutoff, challenge expiry plus three hours, with next_action report_unknown, while the receipt stays pending), or contradicted (the seller reported it paid, but the authorization was never used and can no longer be; the purchase is then failed with reason_code settlement_contradicted, and payment.status not_paid)","properties":{"status":{"type":"string","enum":["confirmed","pending","contradicted"]},"block":{"type":["integer","null"]},"checked_at":{"type":["string","null"],"format":"date-time"}}},"nonce":{"type":"string","description":"The EIP-3009 nonce the owner signed"},"order":{"type":"string","description":"The order that nonce commits to (see payment.authorization.order)"},"service_outcome":{"type":"string","enum":["ok","failed","unknown"]},"service_status":{"type":"integer"},"result":{"description":"The service's answer, as in delivery.result"},"notice":{"type":"string"}}},"reconciliation":{"type":"object","properties":{"at":{"type":"string"},"outcome":{"type":"string","enum":["included","reorg","settled","not_settled","pending_window","unavailable"]},"final":{"type":"boolean","description":"Whether the chain evidence is permanent; independent of workflow final"},"transaction":{"type":["string","null"]},"detail":{"type":"string"},"retry_after":{"type":["string","null"]},"search_complete":{"type":"boolean","description":"Tempo: false when the search for the payment's transfer has not covered every block yet; call reconcile again after 20 seconds"}}},"tempo_watch":{"type":"object","description":"Tempo, once payment instructions were handed to the owner's wallet","properties":{"cutoff_at":{"type":"string","format":"date-time","description":"Challenge expiry plus three hours: the daily-limit hold ends, and the agent workflow becomes final; it is not proof of non-payment"},"automatic_until":{"type":"string","format":"date-time","description":"Thirty days after the cutoff: automatic searches stop; reconcile still checks on demand"},"active":{"type":"boolean","description":"Whether automatic searches still run for this payment"},"rebuy_allowed":{"type":"boolean","description":"Whether the owner allowed a new purchase of this request from their account page"}}},"cap_release":{"type":"object","description":"Tempo: the owner's decision, after the cutoff, to allow a new purchase of this request. The original payment stays unconfirmed, and a late debit still counts"},"reason":{"type":"string","description":"Why the purchase is where it is, in words"},"reason_code":{"type":"string","description":"The same, for programs; absent while submitting and on a settled purchase, and while awaiting approval only owner_policy or ethereum_account_required is set. New codes may be added: act on final and payment.status, and treat an unknown code as information. Known: owner_policy (the limits on single purchases refused this payment when the owner tried to approve (a limit per payment or per day); it stays open until it expires, and is approved only if it fits them by then); ethereum_account_required (an owner signed in with a Solana account tried to approve this payment, and its chain needs an Ethereum account; it stays open until it expires, so an owner signed in with an Ethereum wallet can still approve it); owner_rejected (the owner rejected the payment on the approval page; nothing was signed); agent_cancelled (the agent cancelled the purchase before anyone signed); approval_expired (nobody approved within 10 minutes; nothing was signed); terms_changed (the seller changed its terms before the owner signed; nothing was signed); seller_refused_payment (the seller asked for payment again instead of settling; a signed credential was sent, so the chain decides); settlement_failed (the facilitator reported that the transfer did not settle); seller_unreachable (the signed credential was sent but the seller could not be reached; the chain decides); seller_no_receipt (the seller answered without a payment receipt; the chain decides); settlement_pending (the facilitator broadcast the transfer but could not confirm it yet; the chain decides); settlement_not_on_chain (the seller reported a settlement, but the transaction it named does not show this payment; the chain decides, by the authorization's nonce); settlement_contradicted (the seller reported the payment settled, but the chain shows the owner's authorization was never used, and it can no longer be: no payment happened); submission_interrupted (the submission stopped before it finished; the chain decides); authorization_cancelled (the owner cancelled the authorization on chain; no payment happened); not_settled (on chains other than Tempo: the chain shows the authorization was never used and its window has closed; no payment happened. On Tempo: no transfer with the payment's memo was found as of the last check, a transfer could still arrive, so payment.status stays unknown and the same-request guard needs owner confirmation after the cap cutoff); not_requested (the owner said they did not ask for this purchase; nothing was signed); match_code_mismatch (the owner picked a code other than this request's, so it was closed; nothing was signed); service_failed (the payment settled, but the service answered with an error); service_no_answer (the payment settled, but the service's answer never arrived); transaction_mismatch (the transaction the owner's wallet sent is not this payment (Tempo), so the seller was not called; the chain decides, by the payment's memo); wallet_may_have_sent (a Tempo transfer was prepared for the owner's wallet, but the chain does not show the payment yet. The daily-limit hold ends at tempo_watch.cutoff_at. The wallet call has no recorded expiry; searches continue with back-off for 30 days after the cutoff, then on demand).","examples":["owner_rejected","agent_cancelled"]},"message":{"type":"string","description":"One sentence the agent can repeat"},"next":{"type":"string","description":"What the agent should do now"},"next_action":{"type":"object","required":["type"],"description":"The same as next, for programs. New types may be added. Types: wait_for_owner (the owner has the approval link; poll poll_url until final, or until expires_at, in the same turn or after the owner replies); poll (poll poll_url until final); wait_for_chain (a signed credential or a transfer may still settle; do not buy again, poll poll_url; retry_at is the next useful check and is always in the future; missing chain data can delay the answer); done (the purchase is complete; use delivery.result and the receipt); report_to_owner (the payment happened but the service did not deliver; tell the owner, with the receipt, and do not pay again); stop (nothing was paid and nothing more will happen; buy again only if the owner asks); report_unknown (the payment is unconfirmed and can still arrive; tell the owner; check at retry_at when supplied, otherwise only when asked; do not buy again without owner confirmation from their account page).","properties":{"type":{"type":"string","examples":["wait_for_owner","done"]},"poll_url":{"type":"string","format":"uri"},"expires_at":{"type":["string","null"],"format":"date-time"},"retry_at":{"type":["string","null"],"format":"date-time","description":"The next useful check, always in the future. On Tempo the cap hold ends at challenge expiry plus three hours; final then means the agent workflow ended, not proof of unpaid. next_action becomes report_unknown: tell the owner and check at retry_at when supplied, otherwise only when asked. Search continues hourly for one day after cutoff, then daily until 30 days after cutoff, and on demand thereafter. Re-buy requires the owner's account-page acknowledgement of the reported evidence and late-payment risk. retry_at is omitted after automatic watching ends. Any late debit counts on its UTC block day, even above the cap."}}},"notice":{"type":"string"},"links":{"type":"object","properties":{"self":{"type":"string"}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine-readable code. New codes may be added, so accept any string. Sent today: not_found, invalid_endpoint, invalid_json, invalid_ids, invalid_id, missing_query, rate_limited, method_not_allowed, internal_error, invalid_request, invalid_params, unknown_parameter, invalid_idempotency_key, body_too_large, unsupported_media_type, missing_token, anonymous_purchases_disabled, unknown_service, service_unavailable, terms_mismatch, idempotency_key_reused, idempotency_conflict, too_many_pending, idempotency_key_expired, price_above_max, not_awaiting_approval, seller_error, payment_outcome_unknown.","examples":["not_found","too_many_pending"]},"message":{"type":"string","description":"Human-readable explanation with a pointer to recovery"},"endpoints":{"type":"array","items":{"type":"string"},"description":"On not_found for an unknown path: the paths that do exist"},"param":{"type":"string","description":"On unknown_parameter: the field that is not accepted"},"allowed":{"description":"On invalid_params, unknown_parameter and method_not_allowed: what is accepted"},"retry_after":{"type":"integer","description":"On rate_limited, too_many_pending and idempotency_conflict: seconds to wait, also sent as Retry-After"},"purchase_id":{"type":"string","description":"On idempotency_key_expired: the purchase the key created"},"mismatches":{"type":"array","description":"On terms_mismatch: how the seller's terms differ from the listing"},"price":{"description":"On price_above_max: what the service costs ({ atomic, decimal })"},"max_amount":{"description":"On price_above_max: the cap that was sent ({ atomic, decimal })"},"retryable":{"type":"boolean","description":"Purchase API: true when the same request can work later, after Retry-After when there is one"},"doc_url":{"type":"string","format":"uri","description":"Purchase API: where every code is explained"},"money_moved":{"enum":[false,"unknown"],"description":"Purchase API: payment context for this error. false means this request moved no money; it does not establish whether the purchase paid earlier. \"unknown\" when a payment may have left: on the owner's signature step, on a cancel or approval-page action refused because a Tempo transfer was already prepared for the owner's wallet, and on any error about a purchase your bearer token opens that may already have paid, a 429 included. This also applies when you repeat an Idempotency-Key. If the purchase record cannot be read, the answer is also \"unknown\". With \"unknown\", message is always \"this payment or transaction may have been sent; check its status before you try again\", and code says what refused the request. The purchase's payment.status is then the authority."},"request_id":{"type":"string","pattern":"^req_","description":"Purchase API: this request's id, also sent as the Request-Id header. Quote it when reporting a problem."}}}}}},"securitySchemes":{"budgetToken":{"type":"http","scheme":"bearer","description":"The access_token (ssbt_test_…) returned when the budget request was created. It reads and cancels that one request."},"purchaseToken":{"type":"http","scheme":"bearer","description":"The access_token returned when the purchase was created. It reads, reconciles and cancels that one purchase."}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per window (IETF RateLimit header fields)","schema":{"type":"string"},"example":"300"},"RateLimit-Policy":{"description":"Quota policy: limit;w=window-seconds","schema":{"type":"string"},"example":"300;w=60"},"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}},"Deprecation":{"description":"RFC 9745: the deprecation date for this endpoint, as @<unix seconds>","schema":{"type":"string"},"example":"@1798761600"},"Sunset":{"description":"RFC 8594: the retirement date for this endpoint. Our deprecation policy gives at least 90 days' notice before it","schema":{"type":"string"},"example":"Wed, 31 Mar 2027 00:00:00 GMT"},"RequestId":{"description":"This request's id (req_...), repeated as error.request_id; quote it when reporting a problem","schema":{"type":"string"},"example":"req_3kQ9vYt0bXn2Lw8a"},"PurchaseRateLimit":{"description":"What is left of this caller's limit (draft-ietf-httpapi-ratelimit-headers-11): \"name\";r=remaining;t=seconds-until-reset","schema":{"type":"string"},"example":"\"create\";r=7;t=412"},"PurchaseRateLimitPolicy":{"description":"The limit (draft-ietf-httpapi-ratelimit-headers-11): \"name\";q=quota;w=window-seconds","schema":{"type":"string"},"example":"\"create\";q=10;w=600"}},"responses":{"Error":{"description":"Any other error; body follows the Error schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadRequest":{"description":"Invalid request (codes: invalid_json, invalid_endpoint, invalid_ids, missing_query)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Unknown id or path (code: not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooManyRequests":{"description":"Rate limited (code: rate_limited); wait Retry-After seconds","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Unexpected failure (code: internal_error); safe to retry with backoff","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}