satohub-ai Signing API

Response signing. Every Preflight verdict and every Sato Route decision is returned with a detached Ed25519 signature — `Sato-Signature` over the exact response bytes plus `Sato-Signed-At`, and the same signature under `meta.signature` for clients that only see the JSON. Keys are published as a JWK Set; the construction is published beside them. A valid signature proves origin, integrity and the stamped time. It is not a safety claim and it does not make a verdict more true. `meta.signature: null` with no header means the deployment has no signing key — unknown, never a placeholder.

Operations 4

GET /.well-known/jwks.json Public keys that verify signed responses #
GET /.well-known/sato-score-roots.json The daily Merkle roots over every published Sato Score #
GET /api/verify/{slug}/proof A Merkle proof that one listing's Sato Score was in that day's published root #
GET /.well-known/sato-signing.json What is signed, how, and for how long the keys stay checkable #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/satohub-ai:satohub-ai-signing-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

satohub-ai-signing-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Sato Hub public Signing API
  version: 1.1.0
  summary: Read access to the onchain agent library, its change feed, and the Agent Passport registry.
  description: 'Public reads need no key and no account; the projects and listings routes take a bearer key. A Sato API key is optional on every metered endpoint and only raises limits. What is free, what is paid (the plans are Sato Check Team and Platform & Data), every limit and the fee schedule are listed on the pricing page, and the same sheet is machine-readable at /api/pricing.json.


    Pricing: https://satohub.ai/pricing


    License: Catalog data CC-BY-4.0 — attribution: data by satohub.ai. Scores/liveness are informational signals, not safety or quality guarantees.


    Scores and liveness are informational signals computed from public evidence. They are not safety ratings, audits, or claims about returns. `verification_status` distinguishes self-reported from independently checked; self-reported is never presented as verified.


    Every shape this API returns is also published as a versioned JSON Schema (draft 2020-12) at https://satohub.ai/schemas — catalogue at https://satohub.ai/schemas/index.json. They are referenced from components.schemas below, and v1 is additive-only: a breaking change becomes v2 at a new URL. Validate with additional properties allowed.


    The same data is available to agents over MCP (Streamable HTTP, JSON-RPC) at https://satohub.ai/api/mcp — documented at https://satohub.ai/mcp. That endpoint is not described here because it is not REST.


    INTERPRETED ANSWERS. A route template sent literally (`{slug}`), an empty query or body, a parameter under another name (`?package=` for `?target=`) or another spelling of a chain (`8453`, `eth`, `bsc`) is answered with the closest honest thing instead of a bare 4xx, and always LABELLED: the body carries an `interpretation` object (`interpreted_as`, `requested`, `rule`, `note`; the same four keys are repeated at the top level when they cannot collide with the record''s own fields), and the response carries the header `X-Sato-Interpreted: ` (plus `Content-Location` when there is a canonical URL). An interpreted answer is never an exact match. Nothing that moves money is ever interpreted beyond a chain spelling: no amount, token, taker, recipient, deployer or slippage is defaulted, a missing chain is never assumed, a chain is never guessed from an address, and a generic network word (`mainnet`, `testnet`) is never read as a chain (the answer lists the candidates). Writes are never interpreted, and a receipt is never served for a template.'
  contact:
    name: Sato Hub
    url: https://satohub.ai
  x-guidance: 'Free: search and read the onchain-agent catalog (/api/export/index.json, /api/changes, /api/resolve) and use the MCP server at /api/mcp. Paid over x402 (USDC on Base, no account or key): GET /api/x402/history/{slug} for one listing''s daily Sato Score record, GET /api/x402/agent-economy for the weekly agent-economy series. Each paid read is priced per unit delivered (daily points or rows returned): the 402 states the exact amount, the unit, the quantity and the formula, which is also on /pricing. The Sato Score measures how open, active and verifiable a project is; it is not a safety or returns grade.'
  x-status:
    page: https://satohub.ai/status/sato-hub
    json: https://satohub.ai/api/status.json
    feed: https://satohub.ai/status/feed.xml
    note: Success ratios and latencies of Sato Hub's own hourly checks. Not a service-level agreement.
  license:
    name: CC-BY-4.0
    url: https://creativecommons.org/licenses/by/4.0/
servers:
- url: https://satohub.ai
security: []
tags:
- name: Signing
  description: 'Response signing. Every Preflight verdict and every Sato Route decision is returned with a detached Ed25519 signature — `Sato-Signature` over the exact response bytes plus `Sato-Signed-At`, and the same signature under `meta.signature` for clients that only see the JSON. Keys are published as a JWK Set; the construction is published beside them. A valid signature proves origin, integrity and the stamped time. It is not a safety claim and it does not make a verdict more true. `meta.signature: null` with no header means the deployment has no signing key — unknown, never a placeholder.'
paths:
  /.well-known/jwks.json:
    get:
      tags:
      - Signing
      operationId: getJwks
      summary: Public keys that verify signed responses
      description: 'An RFC 7517 JWK Set of RFC 8037 OKP keys (kty OKP, crv Ed25519, x the raw 32-byte public key base64url, kid, use sig, alg EdDSA). The active key is first; a retired key stays published for 90 days so a signature stored in a CI log last month still verifies. `{ "keys": [] }` means this deployment signs nothing.'
      parameters: []
      responses:
        '200':
          description: The key set.
          content:
            application/jwk-set+json:
              schema:
                type: object
                required:
                - keys
                properties:
                  keys:
                    type: array
                    items:
                      type: object
                      required:
                      - kty
                      - crv
                      - x
                      - kid
                      - use
                      - alg
                      properties:
                        kty:
                          type: string
                          enum:
                          - OKP
                        crv:
                          type: string
                          enum:
                          - Ed25519
                        x:
                          type: string
                          description: Raw 32-byte public key, base64url, unpadded.
                        kid:
                          type: string
                          description: First 8 hex characters of SHA-256 over x's raw bytes.
                        use:
                          type: string
                          enum:
                          - sig
                        alg:
                          type: string
                          enum:
                          - EdDSA
  /.well-known/sato-score-roots.json:
    get:
      tags:
      - Signing
      operationId: getScoreRoots
      summary: The daily Merkle roots over every published Sato Score
      description: 'One root per UTC day, newest first, for the last 90 days. Each root commits to every Sato Score published that day, is signed with the key at /.well-known/jwks.json, and — where an attestation key is configured — carries the EAS attestation on Base that wrote it to a chain.


        The file also states the leaf and node encoding in full, so an independent implementation can rebuild a root without reading our code. Pair it with /api/verify/{slug}/proof, and fold the proof against THIS file rather than against the proof response: a proof checked only against a root served in the same reply proves nothing.


        WHAT A ROOT PROVES: what we published, and when. Not that a score is correct, and not a safety, quality or returns grade. A root with no `onchain` block was not written to a chain and says why in `onchain_note`; `signature: null` means no signing key was configured that day. Absent is published as absent.'
      parameters: []
      responses:
        '200':
          description: The published roots.
          content:
            application/json:
              schema:
                type: object
                required:
                - version
                - method
                - encoding
                - signature
                - roots
                properties:
                  version:
                    type: string
                    enum:
                    - sato-score-roots/v1
                  issuer:
                    type: string
                    format: uri
                  window_days:
                    type: integer
                  method:
                    type: string
                    description: The hashing recipe, named on every root.
                  encoding:
                    type: object
                    description: Leaf preimage, node function, ordering, and which listings have a leaf at all.
                  signature:
                    type: object
                    description: What the root signature covers, and where the keys are.
                  onchain:
                    type: object
                    description: The EAS contracts on Base and the schema, with their documentation sources.
                  unavailable:
                    type:
                    - string
                    - 'null'
                    description: Non-null when the history could not be read. Unknown, never an empty history.
                  count:
                    type: integer
                  roots:
                    type: array
                    items:
                      type: object
                      required:
                      - captured_date
                      - root
                      - leaf_count
                      - method
                      properties:
                        captured_date:
                          type: string
                          format: date
                        root:
                          type: string
                          description: Lowercase hex, no 0x.
                        leaf_count:
                          type: integer
                        method:
                          type: string
                        signature:
                          type:
                          - object
                          - 'null'
                        onchain:
                          type: object
                          description: Present only when the root was written to a chain.
                        onchain_note:
                          type: string
                          description: Why it was not.
  /api/verify/{slug}/proof:
    get:
      tags:
      - Signing
      operationId: getScoreProof
      summary: A Merkle proof that one listing's Sato Score was in that day's published root
      description: 'Returns the listing''s record as committed, its leaf digest and preimage, the sibling digests that fold it to the root, and the root itself with its signature and any on-chain attestation.


        With no `date`, answers for the newest day that has a published root, looking back at most seven days. The response is itself signed (Sato-Signature), which is not the interesting part: verify the proof against an archived copy of /.well-known/sato-score-roots.json instead.


        `proof: []` is legitimate on a single-listing day, where the leaf is the root. A listing with no leaf answers 404 with the reason — editorial listings (research papers, newsletters, communities) are never scored and therefore never have one.


        The route TEMPLATE itself (`{slug}` sent literally) answers with a sample proof for one listing (a real proof from that day''s commitment, chosen deterministically), labelled `sample: true`, `rule: template-placeholder`, with `interpreted_as` naming the listing''s own proof URL (header X-Sato-Interpreted). It verifies, and it says nothing about any other listing.'
      parameters:
      - name: slug
        in: path
        required: true
        schema:
          type: string
        example: coinbase-agentkit
        description: A listing slug from /api/export/index.json.
      - name: date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: UTC day, YYYY-MM-DD. Defaults to the newest published root.
      responses:
        '200':
          description: The proof.
          content:
            application/json:
              schema:
                type: object
                required:
                - slug
                - captured_date
                - record
                - leaf
                - proof
                - root
                properties:
                  slug:
                    type: string
                  captured_date:
                    type: string
                    format: date
                  record:
                    type: object
                    properties:
                      slug:
                        type: string
                      trust_score:
                        type: integer
                      trust_tier:
                        type: string
                      captured_date:
                        type: string
                  leaf:
                    type: string
                  leaf_preimage:
                    type: string
                    description: The exact bytes the leaf digest was taken over.
                  proof:
                    type: array
                    items:
                      type: string
                    description: Sibling digests, bottom-up. Fold with node(a,b) = SHA256(domain || min || max).
                  root:
                    type: string
                  leaf_count:
                    type: integer
                  method:
                    type: string
                  rebuilt_root:
                    type: string
                    description: The root recomputed from the snapshot at request time.
                  matches_published_root:
                    type: boolean
                    description: False means the committed root and a rebuild disagree — an integrity problem, surfaced rather than hidden.
                  root_signature:
                    type:
                    - object
                    - 'null'
                  onchain:
                    type:
                    - object
                    - 'null'
                  onchain_note:
                    type:
                    - string
                    - 'null'
        '404':
          description: No root for that day, or no leaf for that listing. The body says which.
        '503':
          description: This deployment holds no score history.
  /.well-known/sato-signing.json:
    get:
      tags:
      - Signing
      operationId: getSigningPolicy
      summary: What is signed, how, and for how long the keys stay checkable
      description: 'The JWKS says which key. This says what it covers: the header names, the two signed messages (the exact bytes, and the canonical body with meta.signature removed), the canonicalisation rule, which surfaces are signed, the rotation policy, and what a valid signature does NOT mean.'
      parameters: []
      responses:
        '200':
          description: The signing policy.
          content:
            application/json:
              schema:
                type: object
                required:
                - version
                - algorithm
                - keys
                - headers
                - covered
                - rotation
                - surfaces
                properties:
                  version:
                    type: string
                    enum:
                    - sato-signing/v1
                  issuer:
                    type: string
                    format: uri
                  algorithm:
                    type: object
                  keys:
                    type: object
                    description: jwks_url, active_kid (null when unsigned), and how the kid is derived.
                  headers:
                    type: object
                  covered:
                    type: object
                    description: The exact message constructions and the canonical-JSON rule.
                  unsigned:
                    type: object
                  rotation:
                    type: object
                  surfaces:
                    type: array
                    items:
                      type: object
                      properties:
                        path:
                          type: string
                        what:
                          type: string
                  limits:
                    type: array
                    items:
                      type: string
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'Two different credentials ride this header, and they are told apart by their shape.


        • A SATO API KEY, `sk_sato_…` — optional on every metered endpoint. It is NEVER required: each of those endpoints answers an anonymous caller at the free limits, and a key only raises the caps and attributes the calls to an account. A key may also be sent as `X-Sato-Key`. An unknown or revoked key is served as if no key were present, never refused. Limits and plans: /pricing.


        • A PRIVY ACCESS TOKEN for a signed-in account — required by the claim endpoints and the key-management endpoints. A token that is not a Sato key is treated as one of these.'
    satoKey:
      type: apiKey
      in: header
      name: X-Sato-Key
      description: 'The same optional Sato API key, on its own header. Equivalent to `Authorization: Bearer sk_sato_…`.'
externalDocs:
  description: The published Sato Hub formats, as JSON Schema.
  url: https://satohub.ai/schemas