Skip to content

API endpoints

Sill exposes its public surface from a single edge origin, https://edge.sill.so. All routes are unversioned at the host level; per-route versioning is in the path (/v1/… for the discovery and transactional routes, /.well-known/jwks.json for the verifier-facing key set). Every signed response is independently verifiable against the public JWKS using off-the-shelf ed25519 tooling — no Sill SDK is required, and there is no Sill SDK to vendor.

The transactional mandate endpoint and the rest of the surface are both live for every verified site.

MethodPathPurposeAuthDoc
POST/v1/discovery/ingestDiscovery beacon from the embed script.None (public, site-key scoped).Embed script
GET/v1/agent-card/{site_key}.jsonPer-site signed A2A-compatible agent card.None.Agent card
GET/v1/catalog/{site_key}.jsonPer-site signed ARD ai-catalog.json.None.ARD catalog
POST/v1/mcp/{site_key}Per-site Streamable-HTTP MCP server.None at the transport; tool invocation is policy-gated.MCP server
POST/v1/m/{site_key}/mandateTransactional mandate submission.ed25519-signed mandate in the request body.Signed mandates
GET/.well-known/jwks.jsonPublic JWKS for verifying every Sill-issued signature.None.Public JWKS

{site_key} is the public per-site credential issued in the dashboard. It is a public identifier, not a secret — the same posture as a Stripe publishable key. The site-key shape is validated at the edge against ^(sk_)?[A-Za-z0-9_-]{43}$ before any KV read.

  • Transport: HTTPS only. The edge does not serve plaintext.
  • CORS: every route handles its own OPTIONS preflight at the edge. Public GET surfaces (agent-card, catalog, jwks) reply with Access-Control-Allow-Origin: *. The mandate endpoint echoes the request Origin and never sets Access-Control-Allow-Credentials — authentication is the signed mandate body, not a cookie.
  • Caching: signed GET surfaces carry Cache-Control: public, max-age=60 (cards and catalogs) or public, max-age=300 (JWKS). Error responses are no-store to prevent intermediate caches from pinning a 404 for a key that may become valid.
  • Method-not-allowed: any method outside the documented set returns 405 with an Allow: header naming the supported methods.
  • Anti-fingerprinting: 404 responses for unknown or malformed site keys return a constant body ({"error":"not_found"}) so probing cannot distinguish “never existed” from “exists but you can’t see it”.
  • Timestamps: every timestamp on the wire is ISO-8601 UTC.
  • Encoding: signatures and public keys use unpadded base64url per RFC 4648 §5.
  • Canonicalization: every signed payload is canonicalized with RFC 8785 JCS before signing.
flowchart LR
    Browser[Merchant page<br/>+ embed.js] -->|POST /v1/discovery/ingest| Edge
    Agent[AI agent] -->|GET, POST /v1/...| Edge
    Verifier[Third-party verifier] -->|GET /.well-known/jwks.json| Edge
    Edge[Edge · edge.sill.so] -->|signed records| Origin[Sill origin]

The edge owns the latency-sensitive surface — manifest serving, MCP transport, mandate verification, signing-key publication. The origin owns audit storage, the identity registry, and connectors. Every signed surface uses the same ed25519 key, so a single JWKS resolves both an agent card and an ARD trust manifest.

POST https://edge.sill.so/v1/discovery/ingest

The fire-and-forget beacon the embed script emits once per page load. The body is a small JSON envelope (schema_version: "1") carrying the site key, an observed_at timestamp, and nested identification and page_context objects; success is 204 No Content with no body.

Terminal window
curl -i -X POST https://edge.sill.so/v1/discovery/ingest \
-H 'Content-Type: application/json' \
--data '{
"schema_version": "1",
"site_key": "sk_EXAMPLE_REPLACE_WITH_YOURS_AAAAAAAAAAAAAAAAA",
"observed_at": "2026-06-22T14:03:11.000Z",
"identification": {},
"page_context": {}
}'

The merchant’s bundle does not have to call this directly — the embed script handles it via navigator.sendBeacon. The endpoint is documented here for completeness and for diagnostic curl. See Identifying agents for how Sill classifies the caller.

GET https://edge.sill.so/v1/agent-card/{site_key}.json

Per-site, signed, A2A-compatible agent card. protocol_version: "a2a/1.2". The card’s skills[] is backing-filtered — only skills the site actually exposes through a wired backing appear.

Terminal window
curl -i "https://edge.sill.so/v1/agent-card/SITE_KEY.json"

Response headers (200):

HTTP/2 200
content-type: application/json; charset=utf-8
cache-control: public, max-age=60
access-control-allow-origin: *

The card body includes a detached ed25519 JWS (alg: EdDSA, crv: Ed25519) over the canonical card payload. To verify, follow the Verify a signature recipe; the JWKS is at /.well-known/jwks.json.

404 is returned uniformly for unknown, malformed, or non-existent site keys; 503 is returned when an upstream dependency is unreachable on a cold path.

GET https://edge.sill.so/v1/catalog/{site_key}.json

Per-site signed Agentic Resource Discovery ai-catalog.json. specVersion: "1.0". host.identity: did:web:{domain} per the did:web method. Each trustManifest carries a compact, detached ed25519 JWS over the JCS-canonical manifest body.

Terminal window
curl -i "https://edge.sill.so/v1/catalog/SITE_KEY.json"

Response headers (200) match the agent card’s: Cache-Control: public, max-age=60, CORS *. The signature attests the host identity + provenance; the per-entry url / capabilities / description fields are not inside the signed payload — their integrity rests on did:web + TLS per the ARD spec.

POST https://edge.sill.so/v1/mcp/{site_key}

Per-site Streamable-HTTP Model Context Protocol server. Implements the standard MCP handshake (initialize), tool discovery (tools/list, backing-filtered to match the agent card), and tool invocation (tools/call, recorded in the site’s signed audit log).

Terminal window
curl -i -X POST "https://edge.sill.so/v1/mcp/SITE_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "example-client", "version": "0.0.0" }
}
}'

MCP responses themselves are not signed — only the agent card’s pointer to the MCP endpoint is signed. Money-movement skills (e.g. place_order) require a signed mandate and route through the same transactional pipeline as /v1/m/{site_key}/mandate. See MCP server for the scope of claim.

POST https://edge.sill.so/v1/m/{site_key}/mandate

Submits a signed mandate for verification, policy evaluation, and (on approval) processor authorization. Authentication is the ed25519 signature carried inside the request body — there is no Authorization header, no cookie, and no API key. The body is capped at 8 KB; oversize requests reject before parsing.

Terminal window
curl -i -X POST "https://edge.sill.so/v1/m/SITE_KEY/mandate" \
-H 'Content-Type: application/json' \
-H 'X-Sill-Request-Id: req_example_correlate_me' \
--data @mandate.json

mandate.json is the two-part { signed, envelope } shape described in Signed mandates.

The response body is a small JSON envelope discriminated by an outcome field. The HTTP status is the secondary signal — the outcome is load-bearing.

OutcomeHTTPWhen
approved200Policy passed; the processor was asked to authorize.
escalated202Policy escalated to human-in-the-loop review.
rejected400 / 401 / 403 / 404 / 413 / 415Mapped to the rejection class — see Signed mandates → rejection taxonomy.
rate_limited429Per-account or per-site limiter tripped; includes retry_after_seconds.
unavailable503Verifier dependencies (nonce store, registry) could not answer. Fail-closed.

Example approved response:

HTTP/2 200
content-type: application/json
cache-control: no-store
{
"outcome": "approved",
"mandate_id": "mnd_01J9XYZABCDEFGHJKMNPQRSTVW",
"record_id": "rec_01J9XYZAAAAAAAAAAAAAAAAAAA",
"evaluated_at": "2026-06-22T14:03:11.000Z",
"rules_evaluated": 28
}

Example rejected response (signature failed verification):

HTTP/2 401
content-type: application/json
cache-control: no-store
{
"outcome": "rejected",
"reason": "signature_invalid"
}

Clients may send an X-Sill-Request-Id header for correlation; if present, it is echoed back on the response.

GET https://edge.sill.so/.well-known/jwks.json

The public ed25519 signing key in RFC 7517 JWKS form. The same key signs every Sill-published agent card and ARD trust manifest, so a verifier resolves both surfaces with a single fetch.

Terminal window
curl -i "https://edge.sill.so/.well-known/jwks.json"

Response (truncated for the example x value):

HTTP/2 200
content-type: application/jwk-set+json; charset=utf-8
cache-control: public, max-age=300, s-maxage=300
access-control-allow-origin: *
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"use": "sig",
"kid": "<key-id>",
"x": "<base64url-32-byte-public-key>"
}
]
}

Algorithm + key type follow RFC 8037 (EdDSA in JOSE) and JWS. See Verify a signature for the end-to-end recipe.

The signed-surface endpoints are designed so that a third party can prove provenance without trusting Sill’s word for it.

sequenceDiagram
    autonumber
    participant Verifier as Third-party verifier
    participant Edge as edge.sill.so
    Verifier->>Edge: GET /v1/agent-card/:site_key.json
    Edge-->>Verifier: 200 + signed card (detached JWS)
    Verifier->>Edge: GET /.well-known/jwks.json
    Edge-->>Verifier: 200 + JWKS (ed25519 public key)
    Verifier->>Verifier: JCS-canonicalize card payload
    Verifier->>Verifier: Reconstruct JWS signing input
    Verifier->>Verifier: ed25519.verify(sig, input, jwk.x)

The same flow applies to the ARD catalog’s trust manifest. The transactional mandate uses an analogous recipe but with a different domain-separation tag (sill-mandate-v1) and the agent’s registered key, not Sill’s; see Signed mandates → verifying a mandate signature.

Is there a Sill SDK? No. The verifier path uses only standard tools: any RFC 8785 (JCS) canonicalizer and any reputable ed25519 library (@noble/ed25519, pynacl, tweetnacl, OpenSSL). Sill publishes no client SDK for the verifier surface, and there is no Sill code in the verifier path.

Do I authenticate to call these endpoints? The GET surfaces (agent-card, catalog, jwks) and the discovery ingest are unauthenticated. The MCP transport is unauthenticated; tool invocation is policy-gated, and money-movement tools require a signed mandate. The transactional mandate endpoint authenticates via the ed25519 signature inside the request body — there is no API key or bearer token.

Why is the site key in client-side HTML? The site key is a public identifier, not a secret. It is analogous to a Stripe publishable key. It identifies which Sill site an interaction belongs to; it does not authorize any privileged action on its own.

What happens if Sill’s verifier can’t answer? The mandate endpoint returns 503 unavailable. Sill is fail-closed by design — an unreachable registry or nonce store never produces an approval. See Signed mandates → replay defense.

Which protocols does the mandate endpoint accept? Currently a2a, ap2, and mcp on the edge allowlist. acp, ucp, and x402 are on the roadmap; see Protocols reference.

Is there a status page? The origin liveness check is at https://api.sill.so/healthz. Sill does not currently publish an uptime SLA for the edge endpoints; see Security overview.