satohub-ai Check API

Sato Check: what a package, MCP server, skill, repo or x402 endpoint can do with an agent's keys and funds. A profile describes what was read and run, with dates — never a safety rating.

Operations 3

GET /api/check Read what a package, MCP server, skill, repo or x402 endpoint can do with your… #
POST /api/check/install Check every target in an install command or MCP config before installing it #
POST /api/check/batch Sato Check a list of targets or an install block against an org custody policy #

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-check-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-check-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Sato Hub public Check 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: Check
  description: 'Sato Check: what a package, MCP server, skill, repo or x402 endpoint can do with an agent''s keys and funds. A profile describes what was read and run, with dates — never a safety rating.'
paths:
  /api/check:
    get:
      tags:
      - Check
      operationId: checkCustody
      summary: Read what a package, MCP server, skill, repo or x402 endpoint can do with your…
      description: 'Returns a sato.custody/v1 profile with four answers: does it take your key, does your key leave, can it move funds on its own, what changed. Each evidence line carries one class — declared (the project says so), traced (our static read of the published artifact) or observed (a sandbox run with planted test keys). A stored profile answers first; an unstored subject gets the declared and traced lanes live, capped at 10 s; the observed lane never runs from a request. Signed like /api/preflight. A profile is not a safety rating, an audit or an endorsement, and `unknown` means we could not look.'
      parameters:
      - name: target
        in: query
        required: false
        schema:
          type: string
        description: Without it (and without an alias) the answer is a 200 `needs_input`. Also read, labelled `param-alias`, from ?package=, ?repo=, ?url=, ?endpoint=, ?skill=, ?x402=, ?name= or ?q= (the name gives the kind when `kind` is absent). npm:<name>, a bare package name, pypi:<name>, a github.com URL, an https MCP endpoint, a ClawHub/skills.sh URL, or an x402 resource URL.
      - name: kind
        in: query
        required: false
        schema:
          type: string
          enum:
          - package
          - mcp
          - skill
          - repo
          - x402
        description: Disambiguates the target, e.g. kind=x402 for a paid URL.
      responses:
        '200':
          description: 'The profile, its summary and the four answers (schema: https://satohub.ai/schemas/custody/v1.json), OR, when no target was sent, a `needs_input` help body with no profile and no verdict (header X-Sato-Interpreted: needs-input).'
          content:
            application/json:
              schema:
                oneOf:
                - $ref: '#/components/schemas/custody'
                - $ref: '#/components/schemas/needs-input'
        '400':
          description: The target could not be read; the body says why in one sentence.
        '503':
          description: The custody engine is not switched on for this deployment.
        '504':
          description: The live lanes ran past their 10-second budget.
  /api/check/install:
    post:
      tags:
      - Check
      operationId: checkInstall
      summary: Check every target in an install command or MCP config before installing it
      description: 'Parses npm/pnpm/yarn/bun add|install, npx -y, pip install, uvx, `claude mcp add …` and {"mcpServers": …} JSON, and returns one custody summary with the four answers per target. Targets that could not be profiled are listed in `unresolved` with the reason. `has_observed_key_egress` is true only when a planted test key was seen leaving. Body cap 16 KB; rate limited per caller. Nothing from the body is recorded.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                command:
                  type: string
                  description: A shell install command.
                config:
                  type: string
                  description: An MCP config block (JSON as text).
      responses:
        '200':
          description: 'One entry per target, OR a `needs_input` help body (no verdict) when the body was empty, not JSON, or carried no usable key. A bare string body is read as `command`; install / cmd / package / text as `command` (`package: x` is read as `npm i x`, assumed npm); mcp / mcpServers (or a whole MCP config as the body) as `config` — each labelled with `interpretation` and X-Sato-Interpreted.'
          content:
            application/json:
              schema:
                oneOf:
                - type: object
                  required:
                  - schema
                  - subjects
                  - unresolved
                  - has_observed_key_egress
                  properties:
                    schema:
                      const: sato.custody/v1
                    subjects:
                      type: array
                      items:
                        type: object
                        required:
                        - subject
                        - summary
                        - answers
                        properties:
                          subject:
                            type: object
                            description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json#/properties/profile/properties/subject'
                            externalDocs:
                              url: https://satohub.ai/schemas/custody/v1.json#/properties/profile/properties/subject
                          summary:
                            type: object
                            description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json#/properties/summary'
                            externalDocs:
                              url: https://satohub.ai/schemas/custody/v1.json#/properties/summary
                          answers:
                            type: object
                            description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json#/properties/answers'
                            externalDocs:
                              url: https://satohub.ai/schemas/custody/v1.json#/properties/answers
                          solana_receipt:
                            type: object
                            description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json#/properties/solana_receipt'
                            externalDocs:
                              url: https://satohub.ai/schemas/custody/v1.json#/properties/solana_receipt
                    unresolved:
                      type: array
                      items:
                        type: object
                        required:
                        - input
                        - reason
                        properties:
                          input:
                            type: string
                          reason:
                            type: string
                    has_observed_key_egress:
                      type: boolean
                      description: True only when a planted test key was seen leaving for some target.
                    interpretation:
                      $ref: '#/components/schemas/interpretation'
                - $ref: '#/components/schemas/needs-input'
        '400':
          description: The command or config could not be read (an empty body is a 200 needs_input, not a 400).
        '413':
          description: The body is larger than 16 KB.
        '429':
          description: Too many requests from this address.
        '503':
          description: The custody engine is not switched on for this deployment.
  /api/check/batch:
    post:
      tags:
      - Check
      operationId: checkBatch
      security:
      - {}
      - bearerAuth: []
      - satoKey: []
      summary: Sato Check a list of targets or an install block against an org custody policy
      description: 'Bulk custody check for platforms and CI. Takes `targets` (strings, as GET /api/check takes them) and/or `install` (install commands or an MCP config block), plus an optional `policy` — the .sato/policy.json shape `{version:1, fail_on:[…], allow_hosts?, allow_subjects?}`; with no policy, only `key_egress_observed` matches. Stored profiles answer first; unstored subjects are profiled live (declared + traced) up to the caller''s live budget, and anything past it is listed in `unresolved` WITH the reason. Target cap and live budget follow the API-key tiers (free 50/10; more with a key via `Authorization: Bearer sk_sato_…` or `X-Sato-Key`); over the cap is refused, never truncated. `exit_code` is 1 only when a policy rule matched a reading; `unknown` never matches a rule. `?format=sarif` returns a SARIF 2.1.0 log. A match names the rule and its evidence rule id; it is a description of what was read, not a safety rating.'
      parameters:
      - name: format
        in: query
        required: false
        schema:
          type: string
          enum:
          - json
          - sarif
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                targets:
                  type: array
                  items:
                    type: string
                install:
                  type: string
                  description: Install commands or an MCP config block.
                policy:
                  type: object
                  properties:
                    version:
                      type: integer
                      enum:
                      - 1
                    fail_on:
                      type: array
                      items:
                        type: string
                        enum:
                        - key_egress_observed
                        - undeclared_key_read
                        - unlimited_fund_action
                        - install_script_added
                        - new_host
                    allow_hosts:
                      type: array
                      items:
                        type: string
                    allow_subjects:
                      type: array
                      items:
                        type: string
      responses:
        '200':
          description: Per-subject summary, answers and policy result; or a SARIF log with ?format=sarif.
          content:
            application/json:
              schema:
                oneOf:
                - $ref: '#/components/schemas/needs-input'
                - type: object
                  required:
                  - schema
                  - policy
                  - summary
                  - exit_code
                  - results
                  - unresolved
                  properties:
                    schema:
                      const: sato.custody/v1
                    policy:
                      type: object
                      description: 'The policy applied — the caller''s, or the default `{version: 1, fail_on: ["key_egress_observed"]}`.'
                      properties:
                        version:
                          type: integer
                          enum:
                          - 1
                        fail_on:
                          type: array
                          items:
                            type: string
                        allow_hosts:
                          type: array
                          items:
                            type: string
                        allow_subjects:
                          type: array
                          items:
                            type: string
                    summary:
                      type: object
                      required:
                      - total
                      - profiled
                      - unresolved
                      - matched
                      properties:
                        total:
                          type: integer
                        profiled:
                          type: integer
                        unresolved:
                          type: integer
                        matched:
                          type: integer
                    exit_code:
                      type: integer
                      enum:
                      - 0
                      - 1
                      description: 1 only when a policy rule matched a reading.
                    results:
                      type: array
                      items:
                        type: object
                        required:
                        - subject
                        - summary
                        - answers
                        - check_url
                        - source
                        - policy
                        properties:
                          subject:
                            type: object
                            description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json#/properties/profile/properties/subject'
                            externalDocs:
                              url: https://satohub.ai/schemas/custody/v1.json#/properties/profile/properties/subject
                          summary:
                            type: object
                            description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json#/properties/summary'
                            externalDocs:
                              url: https://satohub.ai/schemas/custody/v1.json#/properties/summary
                          answers:
                            type: object
                            description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json#/properties/answers'
                            externalDocs:
                              url: https://satohub.ai/schemas/custody/v1.json#/properties/answers
                          check_url:
                            type: string
                            format: uri
                          source:
                            type: string
                            enum:
                            - stored
                            - live
                          policy:
                            type: object
                            required:
                            - ok
                            - violations
                            properties:
                              ok:
                                type: boolean
                              violations:
                                type: array
                                items:
                                  type: object
                                  required:
                                  - rule
                                  - detail
                                  - evidence_rule
                                  properties:
                                    rule:
                                      type: string
                                    detail:
                                      type: string
                                    evidence_rule:
                                      type: string
                    unresolved:
                      type: array
                      items:
                        type: object
                        required:
                        - input
                        - reason
                        properties:
                          input:
                            type: string
                          reason:
                            type: string
                    checked_at:
                      type: string
                      format: date-time
                    caveat:
                      type: string
                    interpretation:
                      $ref: '#/components/schemas/interpretation'
        '400':
          description: Invalid policy, an unreadable `targets`, or over the target cap (the body then carries `max_targets` and `targets_requested`). An empty or non-JSON body, or one with neither targets nor install, is a 200 needs_input. A bare array is read as `targets`, a bare string as `install`; items / list → targets and command / cmd / package / text → install, labelled. An interpreted body is never quoted or settled over x402.
        '402':
          description: 'Payment Required. A call with no key (or a free key) that is over the free target cap but within 500 targets gets, instead of the 400, an x402 v2 PAYMENT-REQUIRED challenge (USDC) priced for THIS request, beside the same error text and a `payment_required` block (`price`, `amount`, `formula`, `lines` per unit, what it buys, and the free alternative: split into calls within the free cap). Retry the same request with PAYMENT-SIGNATURE: the payment is verified and its authorisation locked to single use before any check runs, the call runs once at the paid limits, and it is settled only on a 200. Every quoted live check that starts counts as delivered, whatever it reads: an unreadable package or an unreachable endpoint is an answer, given with its reason. A paid call runs its live checks in parallel, each capped in time, so a slow target cannot keep the others from starting. If Sato Hub''s own processing before the checks leaves too little time to run them all, or the response is not finished in time, the call is served and not charged. If the payment cannot be settled, the response is withheld and nothing is charged. Each payment authorisation is used once: a second request carrying it is refused, settled or not. (A call not charged says so in `X-Sato-Payment: not-charged; <why>`.) A payment presented a second time is answered 409 `payment_already_used`; if the lock cannot be written the call is refused 503 `payment_lock_unavailable`, and if the live checks cannot be counted 503 `quote_unavailable` — nothing runs and nothing is charged in either case. A payment may also be presented on a call within the target cap to raise its live-check budget for that call. Also answered with `payment_invalid` and the facilitator''s reason when a payment is rejected, and with `price_changed` and a fresh quote when the body now prices differently (never charged). A key whose plan already gives these limits is never asked to pay within them. Price: Free for up to 50 targets and 10 live checks per call. Beyond either: $0.0005 per target above 50 plus $0.005 per live check above 10, minimum $0.005 per paid call: price = max($0.005, $0.0005 x max(0, t - 50) + $0.005 x max(0, l - 10)), where t is the distinct targets in the request and l the live checks the call runs (Sato Check: subjects with no stored reading, profiled during the call; Preflight: endpoints and agents probed during the call). One paid call takes up to 500 targets and 50 live checks; above that, split the request. Rounded up to the USDC base unit. Only while the x402 rail is configured; otherwise this case is the 400.'
        '413':
          description: The body is larger than 256 KB.
        '429':
          description: Daily limit for this key's tier reached.
components:
  schemas:
    custody:
      type: object
      description: 'JSON Schema: https://satohub.ai/schemas/custody/v1.json'
      externalDocs:
        url: https://satohub.ai/schemas/custody/v1.json
    needs-input:
      type: object
      description: 'The 200 answer to a call that sent nothing usable: the accepted forms and exact example calls. It carries NO verdict, NO profile and NO pick. Header `X-Sato-Interpreted: needs-input`.'
      required:
      - status
      - needs_input
      - interpretation
      - accepted
      - examples
      properties:
        status:
          type: string
          enum:
          - needs_input
        needs_input:
          type: boolean
          enum:
          - true
        interpretation:
          $ref: '#/components/schemas/interpretation'
        endpoint:
          type: string
          format: uri
        accepted:
          type:
          - object
          - array
        examples:
          type: array
        docs:
          type: string
          format: uri
      additionalProperties: true
    interpretation:
      type: object
      description: 'How the request was read. Present only on an interpreted answer; the response also carries `X-Sato-Interpreted: <rule>`.'
      required:
      - interpreted_as
      - requested
      - rule
      - note
      properties:
        interpreted_as:
          type: string
          description: 'What was served: a URL, a canonical value, `needs_input`, `supported` or `candidates`.'
        requested:
          type: string
          description: What was sent, decoded and cut to 200 characters.
        rule:
          type: string
          enum:
          - template-placeholder
          - near-miss
          - alias
          - needs-input
          - example-goal
          - param-alias
          - body-alias
          - bare-body
          - chain-spelling
          - candidates
        note:
          type: string
      additionalProperties: true
  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