> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sigilcore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Api reference

# API Reference

Sigil Open Framework - Reference Implementation

The Sigil Sign reference implementation exposes the SOF conformance surface plus the operational endpoints used by Sigil Open Framework clients. Every request that mutates state requires a Sigil API key presented as `Authorization: Bearer sk_sigil_...`. Production keys are rejected with `SIGIL_API_KEY_WRONG_ENV` on the test environment, and vice versa.

**Version:** 2.0.0
**Base URL:** [https://sign.sigilcore.com](https://sign.sigilcore.com)

## Endpoints

### `GET /.well-known/jwks.json`

JWKS for Intent Attestation verification

Public JSON Web Key Set used by clients to verify Ed25519 signatures on Intent Attestations. The endpoint is intentionally unauthenticated per the SOF conformance specification.

**Responses:**

* `200` - JWKS document

### `GET /.well-known/sigil-pqc-keys.json`

ML-DSA public keys for PQC attestation verification

Canonical app route for the public ML-DSA-65 key set used by PQC-aware clients to verify the `pqc.sig` claim embedded in approved Intent Attestation JWTs. Hosted clients should use `/v1/pqc-keys` while production edge exposure of this `.well-known` path is pending nginx config deployment. The Ed25519 JWKS remains the compatibility verification path.

**Responses:**

* `200` - PQC key set document

### `GET /.well-known/sof-conformance.json`

SOF conformance descriptor

Canonical app route for the conformance classes, attestation algorithms, and public verification endpoints supported by this signer. Hosted clients should use `/v1/sof-conformance` while production edge exposure of this `.well-known` path is pending nginx config deployment.

**Responses:**

* `200` - Conformance descriptor

### `POST /bundler/{chainId}`

ERC-4337 bundler gateway

Forwards ERC-4337 user-operation traffic to the upstream bundler for the specified chain.

**Parameters:**

* `chainId` (path, integer, required)

**Request body:**
application/json - object

**Responses:**

* `200` - Bundler response
* `401` - Missing, malformed, or invalid API key

### `GET /healthz`

Liveness probe

Returns 200 with `{ "status": "ok" }` when the service is ready.

**Responses:**

* `200` - Service is ready

### `POST /rpc/{chainId}`

JSON-RPC gateway, scoped to an allowlisted EVM chain

Forwards a JSON-RPC payload to the upstream provider for the specified chain. Only chains on the validator allowlist are accepted (chainId 1 and 8453 as of this spec version). Authenticated.

**Parameters:**

* `chainId` (path, integer, required): EVM chain id (e.g. 1 for mainnet, 8453 for Base)

**Request body:**
application/json - object

**Responses:**

* `200` - JSON-RPC response
* `400` - Chain not allowlisted
* `401` - Missing, malformed, or invalid API key

### `POST /v1/authorize`

Submit an intent for policy authorization

Evaluates an Intent against the deployed warranty.md policy bound to the presenting API key. On approval the response includes a signed Intent Attestation JWT. On denial the response carries a machine-readable error code (e.g. `SIGIL_POLICY_VIOLATION_AMOUNT_EXCEEDED`). Before authentication, a coarse IP guard allows 1,000 requests per 60 seconds on sign-test and 1,500 on production. After API-key validation, the per-key limit allows 120 requests per 60 seconds on sign-test and 180 on production. The effective capacity depends on both limits and shared IP traffic; these settings do not promise a universal request capacity.

**Request body:**
application/json - object

**Responses:**

* `200` - Allowed authorization decision
* `202` - Contract response 202
* `400` - Contract response 400
* `401` - Missing, malformed, or invalid API key
* `403` - Policy violation, or the presenting API key does not carry the authorize scope. A policy violation returns AuthorizeResponse with status DENIED. A scope refusal returns ErrorResponse with error\_code SIGIL\_API\_KEY\_SCOPE\_INSUFFICIENT.
* `409` - No policy deployed for this API key
* `429` - Rate limit exceeded; retry per the documented window
* `500` - Internal server error
* `503` - Per-key rate-limit store unavailable; authorization fails closed

### `POST /v1/authorize/test-run`

Simulate authorization with no side effects

Evaluates the same Intent shape as /v1/authorize against the deployed policy, without issuing an attestation, creating a hold, or firing webhooks. Always returns the strict enforcement-mode decision; if SIGIL\_MODE=OBSERVE is active, the response includes `observe_mode_active: true` so callers can distinguish strict from observed outcomes. Test-run rows are excluded from monthly quotas. Before authentication, a coarse IP guard allows 1,000 requests per 60 seconds on sign-test and 1,500 on production. After API-key validation, the per-key limit allows 120 requests per 60 seconds on sign-test and 180 on production. The effective capacity depends on both limits and shared IP traffic; these settings do not promise a universal request capacity.

**Request body:**
application/json - object

**Responses:**

* `200` - Simulated decision
* `400` - Contract response 400
* `401` - Missing, malformed, or invalid API key
* `403` - The presenting API key authenticated successfully but does not carry the scope this route requires. error\_code is SIGIL\_API\_KEY\_SCOPE\_INSUFFICIENT. Keys issued before scoped key classes existed carry the full tenant grant and are unaffected; keys registered through POST /v1/keys/register carry authorize and warranty:deploy only, so webhook administration is unreachable with them until a Pro subscription confers the webhook scopes.
* `409` - Contract response 409
* `429` - Rate limit exceeded; retry per the documented window
* `500` - Contract response 500
* `503` - Per-key rate-limit store unavailable; authorization fails closed

### `GET /v1/capabilities`

Sigil Sign policy and feature capabilities

Returns the unauthenticated, schema-versioned capability registry used by authoring clients for advisory deployment preflight.

**Responses:**

* `200` - Current Sigil Sign capability registry

### `GET /v1/holds/{holdId}`

Fetch the state of a consensus hold

Returns the current non-authoritative state of a hold (PENDING, ALLOWED, DENIED, EXPIRED) plus the source intent and policy reference. A hold poll cannot authorize execution; clients must re-call /v1/authorize and validate its authoritative decision before executing. Requires the API key that created the hold: a hold is owned by the key it was issued to, and a key can read only its own holds. Holds expire at 24 hours by default. Rate-limited to 30 lookups per minute.

**Parameters:**

* `holdId` (path, string, required): The hold identifier returned when the policy required consensus

**Responses:**

* `200` - Hold state
* `401` - Missing, malformed, or invalid API key
* `403` - The presenting API key authenticated successfully but does not carry the scope this route requires. error\_code is SIGIL\_API\_KEY\_SCOPE\_INSUFFICIENT. Keys issued before scoped key classes existed carry the full tenant grant and are unaffected; keys registered through POST /v1/keys/register carry authorize and warranty:deploy only, so webhook administration is unreachable with them until a Pro subscription confers the webhook scopes.
* `404` - Hold not found, expired, or owned by a different API key. A hold belonging to another key is reported as not found rather than forbidden, so the endpoint never confirms an identifier to a caller that does not own it.
* `429` - Rate limit exceeded; retry per the documented window
* `503` - Usage metering is unavailable; the lookup fails closed

### `POST /v1/keys/register`

Register a new Sigil API key

Public registration endpoint. Subject to a strict rate limit (5 attempts per hour per source). Returns a new sk\_sigil\_... API key on success. The issued key carries the authorize and warranty:deploy scopes: it can evaluate intents and deploy its own warranty.md policy, which it must do before /v1/authorize will return anything other than SIGIL\_NO\_POLICY\_DEPLOYED. It cannot administer webhooks; those scopes are conferred when a Pro subscription activates. Scopes are server-assigned and cannot be requested in the body.

**Request body:**
application/json - object

**Responses:**

* `201` - Key issued. The response body reports the assigned scopes.
* `400` - Invalid registration payload
* `429` - Rate limit exceeded; retry per the documented window

### `GET /v1/pqc-keys`

Hosted ML-DSA public keys for PQC attestation verification

Hosted public ML-DSA-65 key set used by PQC-aware clients to verify the `pqc.sig` claim embedded in approved Intent Attestation JWTs. This route is exposed through the existing `/v1/*` edge proxy while the canonical `.well-known` route remains available inside the application and in origin nginx configuration.

**Responses:**

* `200` - PQC key set document

### `GET /v1/service-metadata`

Immutable deployed service commit

Returns the full Git commit SHA embedded in the deployed release artifact, pinning the running deployment to an exact build. The sigil-sign source repository is not public, so this value is not independently verifiable against published source. It establishes deployment parity and build pinning for parties that already hold repository access, such as auditors under an active engagement. Clients relying on it for parity evidence must require a 200 response and compare `serviceCommit` to the expected commit. The endpoint sends `Cache-Control: no-store` so an intermediary cannot serve stale deployment metadata.

**Responses:**

* `200` - Deployed artifact commit metadata
* `503` - The deployed artifact has no commit binding.

### `GET /v1/sof-conformance`

Hosted SOF conformance descriptor

Returns the conformance classes, attestation algorithms, and public verification endpoints supported by this signer. The hosted descriptor points `pqc_keys_uri` at `/v1/pqc-keys`.

**Responses:**

* `200` - Conformance descriptor

### `POST /v1/warranty/deploy`

Deploy a signed warranty.md policy to an API key

Atomically binds a signed warranty.md to the presenting API key. The body carries the exact signed warranty.md bytes and the operator public key that verifies them. Destructive Policy 2.1 profiles also require a short-lived trusted-shim activation JWT supplied separately from the signed policy. Future calls to /v1/authorize for this key evaluate against the deployed policy. Attempt-rate controls are enforced at the edge and do not mutate activation state.

**Request body:**
application/json - object

**Responses:**

* `200` - Policy deployed; response includes policyHash bound to the key
* `400` - Invalid warranty.md or signature
* `401` - Missing, malformed, or invalid API key
* `403` - Deployment refused. Either the presenting API key does not carry the warranty:deploy scope, in which case error\_code is SIGIL\_API\_KEY\_SCOPE\_INSUFFICIENT, or the key is pinned to a different operator public key, in which case error\_code is SIGIL\_WARRANTY\_OPERATOR\_KEY\_MISMATCH. The first successful deployment for a key establishes its operator public key; every later deployment must be signed with that same key. Rotation is an operator procedure and is not available through this endpoint.
* `409` - Activation denied. error\_code is one of SIGIL\_TRUSTED\_SHIM\_REQUIRED, SIGIL\_TRUSTED\_SHIM\_ATTESTATION\_REQUIRED, SIGIL\_TRUSTED\_SHIM\_ATTESTATION\_MALFORMED, SIGIL\_TRUSTED\_SHIM\_UNTRUSTED, SIGIL\_TRUSTED\_SHIM\_VERSION\_REQUIRED, SIGIL\_TRUSTED\_SHIM\_VERSION\_UNSUPPORTED, SIGIL\_TRUSTED\_SHIM\_ISSUER\_INVALID, SIGIL\_TRUSTED\_SHIM\_AUDIENCE\_INVALID, SIGIL\_TRUSTED\_SHIM\_IDENTITY\_INVALID, SIGIL\_TRUSTED\_SHIM\_ATTESTATION\_EXPIRED, SIGIL\_TRUSTED\_SHIM\_NONCE\_INVALID, SIGIL\_TRUSTED\_SHIM\_ATTESTATION\_REPLAYED, SIGIL\_TRUSTED\_SHIM\_CAPABILITY\_MISSING, or SIGIL\_TRUSTED\_SHIM\_POLICY\_BINDING\_INVALID.
* `503` - Usage or activation backing state is unavailable

### `GET /v1/webhooks`

List webhooks registered to the presenting key

**Responses:**

* `200` - Array of webhook descriptors
* `401` - Missing, malformed, or invalid API key
* `403` - The presenting API key authenticated successfully but does not carry the scope this route requires. error\_code is SIGIL\_API\_KEY\_SCOPE\_INSUFFICIENT. Keys issued before scoped key classes existed carry the full tenant grant and are unaffected; keys registered through POST /v1/keys/register carry authorize and warranty:deploy only, so webhook administration is unreachable with them until a Pro subscription confers the webhook scopes.

### `POST /v1/webhooks`

Register a webhook

**Request body:**
application/json - object

**Responses:**

* `201` - Webhook registered
* `401` - Missing, malformed, or invalid API key
* `403` - Refused for one of two distinct reasons. Either the presenting API key does not carry the webhooks:write scope, in which case error\_code is SIGIL\_API\_KEY\_SCOPE\_INSUFFICIENT, or the key is on the Free plan, in which case error\_code is SIGIL\_TIER\_INSUFFICIENT. Webhook registration requires a Pro plan, and activating a Pro subscription is also what confers the webhook scopes.

### `DELETE /v1/webhooks/{id}`

Remove a registered webhook

**Parameters:**

* `id` (path, string, required)

**Responses:**

* `204` - Webhook removed
* `401` - Missing, malformed, or invalid API key
* `403` - The presenting API key authenticated successfully but does not carry the scope this route requires. error\_code is SIGIL\_API\_KEY\_SCOPE\_INSUFFICIENT. Keys issued before scoped key classes existed carry the full tenant grant and are unaffected; keys registered through POST /v1/keys/register carry authorize and warranty:deploy only, so webhook administration is unreachable with them until a Pro subscription confers the webhook scopes.
* `404` - Webhook not found
