fivecell
API

Reference.

Every endpoint takes an x-fivecell-token capability JWT. Tokens are minted by the operator against the bootstrap x-operator-key; everything else runs on capability tokens. Full deployment walkthrough lives in LAUNCH.md.

Public discovery (no token)

GET/.well-known/fivecell.json

Top-level discovery anchor. One JSON document listing operator contact, every public endpoint URL (inventory, listings, JWKS, OpenAPI, …), supported transaction modes, data formats, and build version. Cached one hour. An agent that doesn't already know our URL conventions starts here.

curl https://fivecell.com/.well-known/fivecell.json

GET/openapi.json

Full OpenAPI 3.1 specification of every endpoint, request/response schema, and security scheme. Hand-written, ~17 KB. Agent SDKs (LangChain tools, MCP servers, OpenAI / Claude function-calling) can ingest it and generate a typed client without further instruction. Cached one hour.

curl https://fivecell.com/openapi.json | jq .info

GET/v1/inventory

Public paginated catalogue. Same search filters as /v1/listings (?q=, ?tag=, ?seller=, ?dataset=) plus ?limit= (default 100, max 500) and ?offset=. No token required — listings are public catalogue entries by design.

curl 'https://fivecell.com/v1/inventory?q=clearinghouse&limit=20'

GET/v1/inventory/:id

Single listing. Returns the full record (terms, pricing, sample, tags, schema) plus an acquire block — a machine-readable call template (method, URL, headers, body template, required scope, mint-token-at) the agent can follow without combining other docs. Public read; no token.

curl https://fivecell.com/v1/inventory/$ID | jq .acquire

POST/v1/feedback

Submit a free-form note: a data request, a characteristic request (“add a counterparty column”), a platform bug, anything. Token OPTIONAL — agents and curious humans without a token can submit; if a token is attached, the submitter's URN is captured as reporter_urn. Triggers a feedback_filed webhook event addressed to the note's target.

curl https://fivecell.com/v1/feedback -X POST \
  -H "content-type: application/json" \
  -d '{
    "target":  "any seller",
    "kind":    "data_request",
    "subject": "FX volatility surface at 1m bars",
    "body":    "Looking for implied vols at the front of the curve; will pay per query."
  }'

GET/v1/feedback

Browse the open feedback channel — public. Prospective sellers use this to see what data buyers are asking for. Optional filters: ?target=, ?kind=, ?status=, ?limit=.

curl 'https://fivecell.com/v1/feedback?kind=data_request'

Buyer (authenticated)

GET/v1/listings

Same shape as /v1/inventory but token-gated. Useful when the operator wants to attribute discovery traffic to a principal (e.g. for rate-limiting or audit). Most agent use cases should hit /v1/inventory instead.

curl https://api.fivecell.com/v1/listings?q=clearinghouse \
  -H "x-fivecell-token: $BUYER_TOKEN"

GET/v1/listings/:id

One listing in full — terms, pricing, sample, tags.

curl https://api.fivecell.com/v1/listings/$ID \
  -H "x-fivecell-token: $BUYER_TOKEN"

POST/v1/q  on api.fivecell.com

The engine's entry. Scope, terms, and quota enforced inline; sigchain header returned for the verifier.

curl https://api.fivecell.com/v1/q -X POST -i \
  -H "x-fivecell-token: $BUYER_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "op":      "aggregate",
    "dataset": "fivecell://global-financials/clearinghouse/facts@2026Q1",
    "columns": ["notional_usd"],
    "aggregate": "sum"
  }' | fivecell-verify --keys ./jwks.json

POST/v1/issues

File a data-quality issue against a dataset. The seller's tenant sees it at once; reporter and seller close the loop via PATCH /v1/issues/:id.

curl https://fivecell.com/v1/issues -X POST \
  -H "x-fivecell-token: $BUYER_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "dataset_urn": "fivecell://global-financials/clearinghouse/facts@2026Q1",
    "summary":     "Row 42 has identity_id=null in 2026Q1 snapshot",
    "body":        "Repro: SELECT * WHERE row_id=42. Expected non-null."
  }'

POST/v1/subscriptions

Webhook push for dataset appends, issue resolution, quota threshold crossings. Operator-side retry with exponential backoff.

curl https://fivecell.com/v1/subscriptions -X POST \
  -H "x-fivecell-token: $BUYER_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "dataset_pattern": "fivecell://global-financials/clearinghouse/*",
    "webhook_url":     "https://buyer.example.com/hook",
    "event_types":     ["dataset_appended","issue_resolved"]
  }'

Seller

POST/v1/datasets

Register where queries for a URN should be sent — Arrow Flight + REST endpoints plus a schema fingerprint.

curl https://fivecell.com/v1/datasets -X POST \
  -H "x-fivecell-token: $SELLER_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "urn": "fivecell://global-financials/clearinghouse/facts@2026Q1",
    "shard_locations": [{
      "flight_uri": "grpc+tls://flight.fivecell.com",
      "rest_uri":   "https://api.fivecell.com",
      "role":       "primary"
    }],
    "schema_fingerprint": "deadbeef"
  }'

POST/v1/listings

Publish a listing. The terms object is checked on every query; pricing is yours. The optional schema block tells agents the column shape so they can evaluate fit before acquiring.

curl https://fivecell.com/v1/listings -X POST \
  -H "x-fivecell-token: $SELLER_TOKEN" \
  -H "content-type: application/json" \
  -d '{
    "dataset_urn": "fivecell://global-financials/research/q1@2026Q1",
    "title":       "Q1 2026 Research Dataset",
    "description": "Refreshed quarterly.",
    "terms": {
      "license":          { "kind": "internal_use_only" },
      "allowed_ops":      ["scan","aggregate","text_search","vector_search"],
      "export_bytes_cap": 10485760,
      "privacy_mode":     { "mode": "raw" }
    },
    "pricing": { "model": { "model": "per_query", "cents": 50 } },
    "schema": {
      "columns": [
        { "name": "ts",           "dtype": "timestamp[ns]", "nullable": false, "description": "trade time" },
        { "name": "notional_usd", "dtype": "float64",       "nullable": false },
        { "name": "counterparty", "dtype": "utf8",          "nullable": true  }
      ],
      "row_count_estimate": 16000000,
      "format":             "parquet"
    },
    "tags": ["test","research"]
  }'

DELETE/v1/listings/:id

Retire a listing. Search-invisible at once; the underlying dataset is unaffected.

curl https://fivecell.com/v1/listings/$ID -X DELETE \
  -H "x-fivecell-token: $SELLER_TOKEN"

GET/v1/issues  with ?status=open

Open issues against the seller's datasets.

curl 'https://fivecell.com/v1/issues?status=open' \
  -H "x-fivecell-token: $SELLER_TOKEN"

Verifier

fivecell-verify reads stdin — the full HTTP response, headers included — and confirms the RSA signature against a cached JWKS. No round trip; no operator trust at verification time.

# once
cargo install --git https://github.com/interregna/fivecell fivecell-verify
curl https://fivecell.com/.well-known/jwks.json > jwks.json

# per query
curl https://api.fivecell.com/v1/q -X POST -i \
  -H "x-fivecell-token: $BUYER_TOKEN" \
  -H "content-type: application/json" \
  -d '{ … }' | fivecell-verify --keys ./jwks.json
# → verified: 1 entry(s)

A failure is one of: tampered response, tampered header, wrong JWKS, expired key, revoked jti. Any failure is a hard refusal — don't surface the result downstream.