> ## Documentation Index
> Fetch the complete documentation index at: https://sigil-10dddbf2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> HTTP middleware for gating API endpoints with Sigil credentials.

`@sigil-xyz/x402` implements a request authentication flow on top of HTTP 402 Payment Required. It lets any server verify that an incoming request comes from a credentialed, in-budget agent — without a separate auth service.

## How it works

```mermaid theme={null}
sequenceDiagram
    participant A as Agent
    participant API as Protected API
    participant SOL as Solana

    A->>A: buildSigilHeaders(keypair, method, path, amount)
    A->>API: POST /endpoint + x-sigil-* headers
    API->>API: verify ed25519 signature
    API->>API: check timestamp (replay guard)
    API->>SOL: verifySigil(agent, capability, maxSpend)
    SOL-->>API: credential valid
    API->>SOL: recordSpend(agent, amount)
    SOL-->>API: spend recorded
    API-->>A: 200 OK
```

<Steps>
  <Step title="Agent builds signed headers">
    The agent calls `buildSigilHeaders` with its keypair, the principal public key, the request method, path, and spend amount. This produces four HTTP headers containing the agent's public key, the principal's public key, a timestamp, and an ed25519 signature.
  </Step>

  <Step title="Server receives request">
    The middleware extracts the four `x-sigil-*` headers and validates them.
  </Step>

  <Step title="Signature verification">
    The middleware reconstructs the signed message `{timestamp}:{METHOD}:{path}:{spendAmount}` and verifies the ed25519 signature against the agent's public key.
  </Step>

  <Step title="Timestamp check">
    Rejects the request if the timestamp is older than `maxRequestAgeMs` (default 60 seconds). Prevents replay attacks.
  </Step>

  <Step title="On-chain Sigil check">
    Calls `client.verifySigil(agentPubkey, { requiredCapability, maxSpendAmount })`. Returns 402 if the Sigil is missing, revoked, expired, or lacking the required capability.
  </Step>

  <Step title="Record spend">
    If `spendAmount > 0`, calls `client.recordSpend(agentPubkey, amount)` on-chain. This debits the agent's daily limit and rejects the request if the limit would be exceeded.
  </Step>

  <Step title="Request forwarded">
    On success, the verified agent public key is attached to the request object (`req.sigilAgent`) and the next handler is called.
  </Step>
</Steps>

## Required headers

Agents must attach these four headers to every request:

| Header              | Value                                                     |
| ------------------- | --------------------------------------------------------- |
| `x-sigil-agent`     | Base58 agent public key                                   |
| `x-sigil-principal` | Base58 principal public key that issued the agent's Sigil |
| `x-sigil-timestamp` | Unix timestamp in milliseconds                            |
| `x-sigil-signature` | Base58 ed25519 signature                                  |

### Signature payload

```
{timestamp}:{METHOD}:{path}:{spendAmount}
```

Example: `1714300000000:POST:/api/generate:50000`

The message is UTF-8 encoded and signed with the agent's ed25519 secret key using `nacl.sign.detached`.

## 402 response body

When authorization fails, the middleware returns HTTP `402` with:

```json theme={null}
{
  "protocol": "sigil-v1",
  "message": "reason for rejection",
  "requiredCapability": "image-generation",
  "spendAmount": "50000",
  "credentialProgram": "ZFK63KBXDhGCYm5orVo5QiTBaBhWD4PUcUDBG6fjTkH",
  "network": "devnet",
  "docs": "https://docs.sigil.xyz/x402"
}
```

## SigilMiddlewareConfig

All adapters (Express and Next.js) share the same config interface:

```ts theme={null}
interface SigilMiddlewareConfig {
  connection: Connection                   // Solana RPC connection
  serverWallet: AnchorProvider['wallet']   // signs record_spend transactions
  requiredCapability?: string              // capability the agent must have
  spendAmount?: BN                         // micro-USDC to debit per request (0 = verify only)
  maxRequestAgeMs?: number                 // max timestamp age, default 60_000
  network?: 'devnet' | 'mainnet-beta' | 'testnet' // returned in 402 responses, default 'devnet'
}
```

Setting `spendAmount` to zero (or omitting it) runs verification only — the Sigil is checked but no spend is recorded.
