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.
Endpoint summary
Section titled “Endpoint summary”| Method | Path | Purpose | Auth | Doc |
|---|---|---|---|---|
POST | /v1/discovery/ingest | Discovery beacon from the embed script. | None (public, site-key scoped). | Embed script |
GET | /v1/agent-card/{site_key}.json | Per-site signed A2A-compatible agent card. | None. | Agent card |
GET | /v1/catalog/{site_key}.json | Per-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}.txt | Per-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}/mandate | Transactional mandate submission. | ed25519-signed mandate in the request body. | Signed mandates |
GET | /.well-known/jwks.json | Public 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.
Conventions
Section titled “Conventions”- Transport: HTTPS only. The edge does not serve plaintext.
- CORS: every route answers
OPTIONSpreflights. The publicGETroutes (agent-card,catalog,jwks) sendAccess-Control-Allow-Origin: *. The mandate endpoint echoes the requestOriginand 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 areno-store, so a404is not cached after the site goes live. - Wrong method: returns
405with anAllowheader 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.
Who calls what
Section titled “Who calls what”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).
Discovery ingest
Section titled “Discovery ingest”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.
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.
Agent card
Section titled “Agent card”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.
curl -i "https://edge.sill.so/v1/agent-card/SITE_KEY.json"Response headers (200):
HTTP/2 200content-type: application/json; charset=utf-8cache-control: public, max-age=60access-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:
404with{"error":"not_found"}— Sill does not recognize the key.404with{"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.
ARD catalog
Section titled “ARD catalog”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.
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.
MCP server
Section titled “MCP server”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).
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.
llms.txt
Section titled “llms.txt”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.
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.
/agent page
Section titled “/agent page”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.
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.
Transactional mandate
Section titled “Transactional mandate”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.
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.jsonmandate.json is the two-part { signed, envelope } shape described in Signed mandates.
Response taxonomy
Section titled “Response taxonomy”Every response is a small JSON object with an outcome field. Branch on outcome; the HTTP status follows it.
| Outcome | HTTP | When |
|---|---|---|
approved | 200 | Policy passed; the processor was asked to authorize. |
escalated | 202 | Policy escalated to human-in-the-loop review. |
rejected | 400 / 401 / 403 / 404 / 413 / 415 / 429 | The 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_limited | 429 | Too many mandates for this site. Wait retry_after_seconds (60) and retry. |
unavailable | 503 | Sill could not complete its checks, so nothing was approved. Retry later. |
Example approved response:
HTTP/2 200content-type: application/jsoncache-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 401content-type: application/jsoncache-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.
Public JWKS
Section titled “Public JWKS”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.
curl -i "https://edge.sill.so/.well-known/jwks.json"Response (truncated for the example x value):
HTTP/2 200content-type: application/jwk-set+json; charset=utf-8access-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.
Verifier flow
Section titled “Verifier flow”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.
Common questions
Section titled “Common questions”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.
See also
Section titled “See also”- Embed script — install snippet and beacon shape.
- Agent card — signed identity surface.
- ARD catalog — signed resource catalog.
- MCP server — per-site Streamable-HTTP MCP transport.
- Signed mandates — request shape and rejection reasons.
- Policy engine — how rules drive
approve | escalate | reject. - Public JWKS — verifier key set.
- Verify a signature — end-to-end recipe.
- Audit envelope — signed, Merkle-chained record format.
- Supported agent protocols — what each protocol supports today.
- Security — signing, audit log, data handling.
- External: RFC 7517 JWKS, RFC 8037 EdDSA in JOSE, RFC 8785 JCS, RFC 8032 ed25519, MCP, did:web.