Skip to content

API endpoints

Sill’s public, agent-facing endpoints are all served from https://edge.sill.so. Each signed response can be verified against the public JWKS with any standard ed25519 library; you do not need a Sill SDK. The mandate endpoint is part of Transactional, which is in early access.

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. Tools that move money need a signed mandate.MCP server
GET/v1/llms/{site_key_hash}.txtPer-site generated llms.txt.None.llms.txt
GET/v1/agent/{site_key_hash}Per-site HTML /agent page for HTML-first assistants.None.The /agent page
GET/v1/agent/domain/{domain}Redirects (302) a verified domain to its /agent page.None.The /agent page
POST/v1/m/{site_key}/mandateTransactional mandate submission.ed25519-signed mandate in the request body.Signed mandates
GET/.well-known/jwks.jsonPublic key for verifying agent cards and catalogs.None.Public JWKS

{site_key} is your site key from Settings → Install (for example us_ followed by 43 characters; older keys start with sk_). It is a public identifier, like a Stripe publishable key, not a secret. Routes marked {site_key_hash} take the 43-character hash of the key instead. The agent card, catalog and MCP routes accept either form.

  • Transport: HTTPS only. The edge does not serve plaintext.
  • CORS: every route answers OPTIONS preflights. The public GET routes (agent-card, catalog, jwks) send Access-Control-Allow-Origin: *. The mandate endpoint echoes the request Origin and does not allow credentials; it is authenticated by the signed body, not a cookie.
  • Caching: the agent card and catalog send Cache-Control: public, max-age=60; the JWKS is cached at the edge for five minutes (s-maxage=300). Error responses are no-store, so a 404 is not cached after the site goes live.
  • Wrong method: returns 405 with an Allow header listing the methods the route supports.
  • Unknown site key: returns 404. The agent card and catalog answer with the body {"error":"not_found"}.
  • 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
    Page[Your pages with the embed] -->|POST /v1/discovery/ingest| Edge[edge.sill.so]
    Agent[AI agent] -->|agent card, catalog, MCP, mandates| Edge
    Verifier[Anyone verifying a signature] -->|GET /.well-known/jwks.json| Edge

The agent card and the catalog are signed with the same ed25519 key, so one JWKS fetch verifies both. Audit records and exported bundles use a separate key, published at https://sill.so/.well-known/sill-audit-envelope-v1.pub (see Audit envelope).

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": "us_EXAMPLE_REPLACE_WITH_YOURS_AAAAAAAAAAAAAAAA",
"observed_at": "2026-06-22T14:03:11.000Z",
"identification": {},
"page_context": {}
}'

You do not call this yourself; the embed script sends it with navigator.sendBeacon. It is listed here so you can test with curl. See Identifying agents for how Sill classifies the caller.

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

Your site’s signed, A2A-compatible agent card, with protocol_version: "a2a/1.2". skills[] lists only the skills your site actually offers (none if no skill is live), the same set as the MCP server’s tools/list, the catalog and the /agent page. site.sill_endpoint is the mandate URL, https://edge.sill.so/v1/m/{site_key}/mandate, and signing.expires_at is 15 minutes after signing.issued_at.

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 carries a raw ed25519 signature in signing.envelope_signature, over the JCS-canonical card without that field. To verify, follow the Verify a signature recipe; the JWKS is at /.well-known/jwks.json.

Errors:

  • 404 with {"error":"not_found"} — Sill does not recognize the key.
  • 404 with {"status":"pending_verification",…} — the site exists but the domain is not verified yet.
  • 503 — Sill could not build or sign the card right now; retry shortly.

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

Per-site signed Agentic Resource Discovery ai-catalog.json. specVersion: "1.0". host.identifier: 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 match the agent card’s: Cache-Control: public, max-age=60 and CORS *. The signature covers the trust manifest (who publishes the catalog); each entry’s url, capabilities and description are outside it and rely on did:web and TLS, as the ARD specification defines.

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

Your site’s Model Context Protocol server over Streamable HTTP. It supports initialize, tools/list (the same skills as your agent card) and tools/call (recorded in your 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-06-18",
"capabilities": {},
"clientInfo": { "name": "example-client", "version": "0.0.0" }
}
}'

MCP responses are not signed; the agent card that points here is. Actions that move money (for example place_order) need a signed mandate and are evaluated exactly like a mandate sent to /v1/m/{site_key}/mandate. Too many calls in one session return a rate_limited error with retry_after_seconds. See MCP server.

GET https://edge.sill.so/v1/llms/{site_key_hash}.txt

Your site’s generated llms.txt (llmstxt.org): your enabled skills, discovery endpoints, and the optional description and custom sections you write in the dashboard. Served as text/plain.

Terminal window
curl -i "https://edge.sill.so/v1/llms/SITE_KEY_HASH.txt"

The Cloudflare Worker and WordPress plugin serve the same body at https://your-domain/llms.txt. See llms.txt.

GET https://edge.sill.so/v1/agent/{site_key_hash} — an HTML page for assistants that read pages rather than JSON. It lists your discovery endpoints and skills, with the agent card embedded as JSON-LD.

GET https://edge.sill.so/v1/agent/domain/{domain} — redirects (302) to the page above for a verified domain, so an agent only needs your domain name.

Terminal window
curl -i "https://edge.sill.so/v1/agent/SITE_KEY_HASH"
curl -i "https://edge.sill.so/v1/agent/domain/example.com"

See The /agent page.

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

Submits a signed mandate. Sill verifies it, evaluates your policy and, if approved, asks your payment processor to authorize the charge. The ed25519 signature in the body is the authentication; there is no API key, Authorization header or cookie. Bodies over 8 KB are rejected with 413.

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.

Every response is a small JSON object with an outcome field. Branch on outcome; the HTTP status follows it.

OutcomeHTTPWhen
approved200Policy passed; the processor was asked to authorize.
escalated202Policy escalated to human-in-the-loop review.
rejected400 / 401 / 403 / 404 / 413 / 415 / 429The reason field says why — see Signed mandates → rejection reasons. 429 with reason agent_temporarily_locked means the agent has failed too often and must wait.
rate_limited429Too many mandates for this site. Wait retry_after_seconds (60) and retry.
unavailable503Sill could not complete its checks, so nothing was approved. Retry later.

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": 29
}

An escalated response carries escalation_id and evaluated_rule_id (the rule that asked for review) in place of record_id and rules_evaluated.

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 key that signs every agent card and catalog trust manifest, as an RFC 7517 key set. Pick the key whose kid matches the card’s signing.signing_key_id or the trust manifest’s JWS header kid. The key’s ID is sill-card-ed25519-v1-5f03060ef5f77675. During a transition the key set also lists the same key, with the same x, under its previous ID foyer/edge/card-signing-v1, until at least 2026-11-05, so cards and catalogs signed before the change keep verifying. Refresh your key set if a kid is not found.

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
access-control-allow-origin: *
{
"keys": [
{
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"use": "sig",
"kid": "sill-card-ed25519-v1-5f03060ef5f77675",
"x": "<base64url-32-byte-public-key>"
},
{
"kty": "OKP",
"crv": "Ed25519",
"alg": "EdDSA",
"use": "sig",
"kid": "foyer/edge/card-signing-v1",
"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.

Anyone can check that a card came from Sill and was not changed, without trusting Sill:

sequenceDiagram
    participant Verifier as Verifier
    participant Edge as edge.sill.so
    Verifier->>Edge: GET /v1/agent-card/{site_key}.json
    Edge-->>Verifier: Signed card
    Verifier->>Edge: GET /.well-known/jwks.json
    Edge-->>Verifier: Public key set
    Note over Verifier: Remove signing.envelope_signature,<br/>canonicalize the rest (JCS),<br/>verify the ed25519 signature

The same steps verify the catalog’s trust manifest. A mandate is verified the same way but with the domain-separation tag sill-mandate-v1 and the agent’s registered key instead of Sill’s; see Signed mandates → Sign it.

Is there a Sill SDK? No. Verifying a signature needs only an RFC 8785 (JCS) canonicalizer and an ed25519 library such as @noble/ed25519, pynacl, tweetnacl or OpenSSL.

Do I authenticate to call these endpoints? No for the GET routes, the discovery ingest and MCP. MCP tools that move money need a signed mandate. The mandate endpoint is authenticated by the ed25519 signature in 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, like a Stripe publishable key. It says which Sill site a request belongs to and cannot authorize anything on its own.

What happens if Sill’s verifier can’t answer? The mandate endpoint returns 503 with outcome: unavailable. Nothing is approved when Sill cannot complete its checks; retry later.

Which protocols does the mandate endpoint accept? a2a, ap2 and mcp. See Supported agent protocols.

Is there a status page? A health check is at https://api.sill.so/healthz. Sill does not publish an uptime SLA.