Well-known documents
3 operations under /.well-known (GET): parameters, status codes, refusal bodies, and the same request in curl, HTTP, Python and TypeScript.
Base URL: https://api.codespar.dev
None of the operations below takes a credential: the published document declares them open. See Authentication for the rest of the API.
GET /.well-known/codespar-receipt-keys.json
https://api.codespar.dev/.well-known/codespar-receipt-keys.jsonReceipt verification keys (JWKS)
The public half of the key that signs agentic receipts, so a party with no CodeSpar credential can check one. An agentic receipt carries two seals: receipt_sig, an HMAC under the consumer's own secret, which proves the payment to whoever holds that secret and to nobody else; and receipt_sig_ed25519, made with the key published here.
To verify: take receipt_sig_kid from the receipt, find the key with that kid below, build codespar-receipt:v1:<receipt_id>:<chain> and check the base64url signature over those UTF-8 bytes with any Ed25519 verifier. x is the raw 32-byte public key, base64url (RFC 8037).
Keys ROTATE and a retired key stays listed — that is what keeps an old receipt verifiable. A revoked key is removed entirely: its private half is assumed compromised, so signatures it made are worth nothing. An empty keys means this deployment publishes none, which is consistent with its receipts carrying a null receipt_sig_kid.
Every kid names the DEPLOYMENT that minted it — <did>#<namespace>-<n>, and key_namespace repeats the namespace this document serves. A receipt_sig_kid that is ABSENT here therefore means the receipt came from another environment (a sandbox receipt checked against production, most often), which is unknown_key and NOT a tampered receipt. Before ent#1641 the id was <did>#<n> in every environment, so receipts sealed then carry an id that more than one document may claim; they keep verifying against the document of the environment that issued them and nowhere else.
Collect receipts (the receipt a human payer of a Collect link gets) are signed by the same keys. collect_receipt states their signing string and digest recipe.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
algorithm | string | yes | — |
collect_receipt | object | yes | How a Collect receipt is verified with the keys above (ent#1848). A Collect receipt is signed by the same keys; its signing string is codespar-collect-receipt:v1:<receipt_id>:<digest>, where digest is SHA-256 of the RFC 8785 canonical JSON of the receipt's document. Appended after keys: every field above is unchanged. |
issuer | string | yes | — |
key_namespace | string | yes | The deployment whose keys this document serves, and the namespace every kid below carries. A kid from another namespace is unknown here, not forged. |
keys | array of object | yes | — |
signing_string | string | yes | The template of the bytes a signature covers, stated so a verifier needs nothing else from us. |
curl -X GET https://api.codespar.dev/.well-known/codespar-receipt-keys.json \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /.well-known/codespar-receipt-keys.json HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/.well-known/codespar-receipt-keys.json",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/.well-known/codespar-receipt-keys.json", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/.well-known/codespar-receipt-keys.json");{
"issuer": "string",
"key_namespace": "Example",
"algorithm": "string",
"signing_string": "string",
"keys": [
{
"kid": "string",
"kty": "string",
"crv": "string",
"x": "string",
"alg": "string",
"use": "string",
"status": "string",
"created_at": "string",
"retired_at": "string"
}
],
"collect_receipt": {
"signing_string": "string",
"digest_recipe": {
"canonicalization": "string",
"digest": "string",
"document_fields": [
"string"
]
},
"signature": "string"
}
}GET /.well-known/oauth-authorization-server
https://api.codespar.dev/.well-known/oauth-authorization-serverAuthorization-server metadata (RFC 8414)
The three endpoint URLs below, absolute under the issuer, plus what this server supports: response_types_supported: ["code"], grant_types_supported: ["authorization_code", "refresh_token"], code_challenge_methods_supported: ["S256"] and token_endpoint_auth_methods_supported: ["none"].
Read the last two together: this is a PUBLIC client with no secret, and PKCE S256 is mandatory rather than preferred. plain is never advertised and is refused at the authorize endpoint. Host-aware, like the document above.
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
authorization_endpoint | string | yes | — |
code_challenge_methods_supported | array of string | yes | — |
grant_types_supported | array of string | yes | — |
issuer | string | yes | — |
registration_endpoint | string | yes | — |
response_types_supported | array of string | yes | — |
scopes_supported | array of string | yes | — |
token_endpoint | string | yes | — |
token_endpoint_auth_methods_supported | array of string | yes | — |
curl -X GET https://api.codespar.dev/.well-known/oauth-authorization-server \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /.well-known/oauth-authorization-server HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/.well-known/oauth-authorization-server",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/.well-known/oauth-authorization-server", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/.well-known/oauth-authorization-server");{
"issuer": "string",
"authorization_endpoint": "https://example.com/hook",
"token_endpoint": "https://example.com/hook",
"registration_endpoint": "https://example.com/hook",
"response_types_supported": [
"string"
],
"grant_types_supported": [
"string"
],
"code_challenge_methods_supported": [
"string"
],
"token_endpoint_auth_methods_supported": [
"https://example.com/hook"
],
"scopes_supported": [
"string"
]
}GET /.well-known/oauth-protected-resource
https://api.codespar.dev/.well-known/oauth-protected-resourceProtected-resource metadata (RFC 9728)
The document an MCP client fetches after a 401, from the resource_metadata the WWW-Authenticate header points at resource and authorization_servers are the same origin: this API is its own authorization server.
Host-aware. The issuer is derived from the request's Host, so the copy served on a custom domain advertises that domain and not this document's servers[0] — a client that pasted one origin gets tokens minted for it (RFC 8707 resource indicators).
Responses
| Status | Body | Description |
|---|---|---|
200 | object | OK |
Response 200
| Field | Type | Required | Description |
|---|---|---|---|
authorization_servers | array of string | yes | — |
bearer_methods_supported | array of string | yes | — |
resource | string | yes | — |
scopes_supported | array of string | yes | — |
curl -X GET https://api.codespar.dev/.well-known/oauth-protected-resource \
-H "Authorization: Bearer $CODESPAR_API_KEY"GET /.well-known/oauth-protected-resource HTTP/1.1
Host: api.codespar.dev
Authorization: Bearer $CODESPAR_API_KEYimport os
import requests
res = requests.get(
"https://api.codespar.dev/.well-known/oauth-protected-resource",
headers={"Authorization": f"Bearer {os.environ['CODESPAR_API_KEY']}"},
)
res.raise_for_status()
data = res.json()const res = await fetch("https://api.codespar.dev/.well-known/oauth-protected-resource", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.CODESPAR_API_KEY}`,
},
});
const data = await res.json();const result = await cs.api.get("/.well-known/oauth-protected-resource");{
"resource": "string",
"authorization_servers": [
"string"
],
"bearer_methods_supported": [
"string"
],
"scopes_supported": [
"string"
]
}