## Facts

| | |
| --- | --- |
| Price per call | $0.005 USD for POST /v1/judge and POST /v1/consensus. |
| Free tier | 100 calls per UTC day per key. FREE_DAILY_LIMIT defaults to 100. |
| Networks and assets | Base (eip155:8453) USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp) USDC mint EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v. |
| Auth | Authorization: Bearer API key, or the x402 PAYMENT-SIGNATURE header. Signup is optional. |
| Endpoints | POST /v1/judge, POST /v1/consensus, and POST /v1/signup. |
| MCP URL | https://screwtape.ai/mcp |
| Input limits | task 8000 characters; agent_output and consensus output 16000 characters; evidence up to 20 items of 4000 characters; criteria up to 20 items of 500 characters; answers 2 to 16; agent_id 80 characters; request body 100000 bytes; signup name 80 characters; signup email 200 characters. |
| Data retention | Judged content is not stored unless retain is true and the caller uses an API key. Keyless x402 calls cannot turn retention on. The default is 24 hours. A key can set retention_days to 0, 1, 30, or 90. Day 0 stores neither content nor receipts. DELETE /v1/keys/me deletes the key, its retained rows, and its receipt log. |
| Independence | Screwtape is not tied to an agent framework or a model provider. A successful judge or consensus call includes a signed receipt when receipt signing is configured. POST /v1/receipts/verify checks that receipt. |
| Status | GET /v1/status, /status.json, and /status/ report measured response time for judge and consensus. uptime is successful calls divided by successes, 5xx responses, and timeouts. It is null when that denominator is 0. Unpaid 402 responses are a separate count and are not failures. 401 and 429 responses are not failures either. |

# Screwtape.ai

Screwtape.ai returns a pass, fail, or retry verdict on another agent's output, and scores consensus across several agents, including false-consensus risk. It is the agent validation API, not the arXiv paper also named Screwtape. Authenticate with Authorization: Bearer or, for a single call, an x402 PAYMENT-SIGNATURE header. That payment is $0.005 USDC on Base or Solana. Base (eip155:8453) is paid to 0x028Bb9A57B8C27c22f6B39dd00e7eFF1FB60344B. Solana payTo is the wallet owner. USDC is sent to that owner's associated token account for the mint. Solana is listed when that payee is set and settles through https://x402.dexter.cash. Payments are verified and settled through a facilitator. The worker does not hold a private key. When a model key is configured, the default model is openai/gpt-oss-120b, with qwen/qwen3.8-27b tried once if the primary model returns 404 or 5xx. Judged content is not stored unless retain is true. A successful judge or consensus response includes a receipt when RECEIPT_SIGNING_KEY is set. The receipt is Ed25519 over the canonical JSON of its fields. Public keys are at /.well-known/screwtape-receipt-keys.json. POST /v1/receipts/verify checks one receipt. When the secret is unset, the response omits receipt. The public spec omits operator-only routes.

Base URL: https://screwtape.ai

## GET /v1/health

Health check (no key)

Liveness. Needs no API key and reports no secrets. The body includes paypal_configured. GET /health returns the same body. Not a substitute for GET /v1/keys/me when you need quota.

- `200`: Service is up.

## GET /health

Health check alias

Same response as GET /v1/health.

- `200`: Service is up.

## POST /v1/judge

Judge one agent output: pass, fail or retry

Returns an independent verdict (pass, fail or retry), a score from 0 to 1, reasons, the engine that judged (llm, deterministic or demo), a request_id, a retention object, and a receipt when signing is configured. Call it before returning or acting on one agent's output. Send the evidence the output should be grounded in, and criteria it must meet. It does not search the web. For several answers to the same task, use POST /v1/consensus. On retry, feed the reasons back and call again with a small cap. On fail, stop. Auth: Authorization: Bearer <key> (free key: 100 calls per UTC day), or with no key, an x402 PAYMENT-SIGNATURE of $0.005 USDC on Base or Solana. Without either, GET, HEAD, and POST return 402 with x402Version, accepts, and error at the top level, plus signup_url and pricing_url. Auth and payment are checked before the body is validated.

```json
{
  "task": "State the crew size and launch site.",
  "agent_output": "The crew size was 4. The mission launched from Cape Canaveral.",
  "evidence": [
    "The mission launched on 2024-03-01 from Cape Canaveral.",
    "The crew size was 4."
  ],
  "criteria": [
    "crew size",
    "Cape Canaveral"
  ]
}
```

- `200`: Verdict.
- `400`: Invalid request.
- `401`: API key was not recognized.
- `402`: Payment required. The body has x402Version, accepts, and a string error at the top level, plus error_detail, signup_url, pricing_url, and pricing_json_url. PAYMENT-REQUIRED carries the x402 requirements, including the Bazaar discovery extension. GET and HEAD return this same 402.
- `429`: Daily quota or rate limit exceeded.

## GET /v1/judge

Judge payment requirements

Returns the same HTTP 402 as an unpaid POST. The PAYMENT-REQUIRED header is the x402 requirements object: accepts for Base and Solana, plus Bazaar discovery metadata whose input schema describes the POST body. This method does not run the call.

- `402`: Same payment requirements as an unpaid POST. error is a string. error_detail is the structured object. PAYMENT-REQUIRED carries accepts and extensions.bazaar, including the input schema.

## HEAD /v1/judge

Judge payment requirements

Returns the same HTTP 402 as an unpaid POST. The PAYMENT-REQUIRED header is the x402 requirements object: accepts for Base and Solana, plus Bazaar discovery metadata whose input schema describes the POST body. This method does not run the call.

- `402`: Same payment requirements as an unpaid POST. error is a string. error_detail is the structured object. PAYMENT-REQUIRED carries accepts and extensions.bazaar, including the input schema.

## POST /v1/consensus

False-consensus check: score agreement across 2 to 16 agent answers

Returns consensus_answer, agreement_score, false_consensus_risk (low, medium or high), reasons, clusters, engine, request_id, retention, and a receipt when signing is configured. In that receipt, verdict is false_consensus_risk and score is agreement_score. Optional evidence is a string or an array of up to 20 strings, each at most 4000 characters, and is used when scoring. Call it when several agents or samples answered the same task and you are about to accept the majority. High agreement with medium or high risk suggests copied or thin agreement: verify consensus_answer with POST /v1/judge before you use it. Not for a single answer. Same auth and price as POST /v1/judge: a Bearer key (free tier 100 calls per UTC day) or $0.005 USDC per call with x402 on Base or Solana. GET and HEAD return the same 402 as an unpaid POST.

```json
{
  "task": "State the crew size.",
  "answers": [
    {
      "agent_id": "alpha",
      "output": "The crew size was 4."
    },
    {
      "agent_id": "beta",
      "output": "The crew size was 4."
    },
    {
      "agent_id": "gamma",
      "output": "The crew size was 4."
    }
  ]
}
```

- `200`: Consensus.
- `400`: Invalid request.
- `401`: API key was not recognized.
- `402`: Payment required. The body has x402Version, accepts, and a string error at the top level, plus error_detail, signup_url, pricing_url, and pricing_json_url. PAYMENT-REQUIRED carries the x402 requirements. GET and HEAD return this same 402.
- `429`: Daily quota or rate limit exceeded.

## GET /v1/consensus

Consensus payment requirements

Returns the same HTTP 402 as an unpaid POST. The PAYMENT-REQUIRED header is the x402 requirements object: accepts for Base and Solana, plus Bazaar discovery metadata whose input schema describes the POST body. This method does not run the call.

- `402`: Same payment requirements as an unpaid POST. error is a string. error_detail is the structured object. PAYMENT-REQUIRED carries accepts and extensions.bazaar, including the input schema.

## HEAD /v1/consensus

Consensus payment requirements

Returns the same HTTP 402 as an unpaid POST. The PAYMENT-REQUIRED header is the x402 requirements object: accepts for Base and Solana, plus Bazaar discovery metadata whose input schema describes the POST body. This method does not run the call.

- `402`: Same payment requirements as an unpaid POST. error is a string. error_detail is the structured object. PAYMENT-REQUIRED carries accepts and extensions.bazaar, including the input schema.

## GET /.well-known/screwtape-receipt-keys.json

Receipt signing public keys

JWKS-style Ed25519 public keys for verdict receipts. keys is empty when RECEIPT_SIGNING_KEY is unset. The private key is not in this document.

- `200`: Public keys.

## GET /v1/status

Measured response time, uptime, rate limits, and facilitator health

Returns measured p50 and p95 response time for POST /v1/judge and POST /v1/consensus over the last hour and the last 24 hours. uptime is successes divided by successes, 5xx responses, and timeouts (408 and 504). It is null when that denominator is 0. unpaid counts HTTP 402 and is not part of uptime. 401 and 429 responses are samples only. Facilitator health is the last cached probe of GET /supported on https://facilitator.xpay.sh and https://x402.dexter.cash, refreshed in the background with a 1.5 second timeout, and null until a probe finishes. The same JSON is at /status.json. A human page is at /status/. The 99.9% figure on the enterprise page is a target, not this measurement.

- `200`: Status.

## GET /status.json

Status document

Same body as GET /v1/status.

- `200`: Status.

## GET /v1/receipts

List signed receipts for the current key

Returns the signed receipts stored for this API key. The row is the receipt plus expires_at. It does not include the prompt or the output. Query from and to are ISO-8601 bounds on issued_at. limit defaults to 50 and offset defaults to 0. Keyless calls are not in this log. Requires a Bearer key.

- `from`: Inclusive ISO-8601 lower bound on issued_at.
- `to`: Inclusive ISO-8601 upper bound on issued_at.
- `limit`: Page size. Default 50.
- `offset`: Rows to skip. Default 0.

- `200`: Receipt page.
- `401`: API key was not recognized.

## GET /v1/receipts/export

Export signed receipts as JSONL or CSV

Same rows as GET /v1/receipts. format=jsonl returns application/x-ndjson. format=csv returns text/csv with columns id, kid, issued_at, endpoint, verdict, score, and expires_at. The file does not include the prompt or the output.

- `format`: jsonl or csv.
- `from`: 
- `to`: 
- `limit`: 
- `offset`: 

- `200`: JSONL or CSV export of the receipt log.
- `400`: format must be jsonl or csv.
- `401`: API key was not recognized.

## GET /v1/account/settings

Read retention and anonymous-stats settings

retention_days is 0, 1, 30, or 90. The default is 1. contribute_anonymous_stats defaults to false.

- `200`: Settings.
- `401`: API key was not recognized.

## PATCH /v1/account/settings

Update retention and anonymous-stats settings

Send retention_days as 0, 1, 30, or 90, and contribute_anonymous_stats as a boolean. Omitted fields stay as they are. Existing stored rows keep their original expiry.

```json
{
  "retention_days": 30,
  "contribute_anonymous_stats": false
}
```

- `200`: Updated settings.
- `401`: API key was not recognized.
- `422`: retention_days or contribute_anonymous_stats was not one of the allowed values.

## POST /v1/enterprise/inquiry

Ask about enterprise pricing

Stores name, email, optional organization, and message. JSON returns {id, received, request_id}. A form post returns HTML that says Request received. name is required and at most 80 characters. organization is optional and at most 120 characters. message is required and at most 2000 characters.

```json
{
  "name": "Ada",
  "email": "ada@example.com",
  "organization": "Example",
  "message": "We need a monthly invoice."
}
```

- `200`: Inquiry stored.
- `400`: A field was missing or too long.

## POST /v1/receipts/verify

Verify a verdict receipt

POST the receipt object. The response is {valid:true} when the Ed25519 signature matches the current public key, and {valid:false} otherwise. This call does not require an API key and does not store the receipt.

- `200`: Signature check.

## GET /v1/keys

Read how to create a free API key

GET /v1/keys does not create a key. POST the same path with optional email and name and a required pow_nonce. The human form is /signup/.

- `200`: Signup instructions, including the proof-of-work challenge.

## POST /v1/keys

Create a free API key (alias of POST /v1/signup)

Identical to POST /v1/signup. Prefer /v1/signup in new code.

```json
{
  "name": "crew-judge",
  "pow_nonce": "compute-me"
}
```

- `201`: Key created. The secret is in this response only.
- `400`: Invalid signup or proof of work. When the proof is missing, the body includes a pow object.
- `429`: Signup rate limit, daily cap, or email cap.

## GET /v1/signup

Read how to create a free API key

GET /v1/signup does not create a key. POST the same path with optional email and name and a required pow_nonce. The human form is /signup/.

- `200`: Signup instructions, including the proof-of-work challenge.

## POST /v1/signup

Create a free API key (agent signup with proof of work)

Creates a free-tier key (100 calls per UTC day) with no human approval. Send pow_nonce such that SHA-256 of the UTF-8 string screwtape-signup-v1:<current UTC YYYY-MM-DD>:<nonce> has 12 leading zero bits and the nonce matches ^[A-Za-z0-9_-]{8,64}$. Email and name are optional. A 400 without a valid nonce includes a pow object describing the challenge. Returns 201 with api_key (shown once; only its SHA-256 hash is stored). Limits: 5 keys per IP per hour, 5 per IP per UTC day, 3 active keys per email when an email is sent. Same call as POST /v1/keys. Free; no key or payment needed.

```json
{
  "name": "crew-judge",
  "pow_nonce": "compute-me"
}
```

- `201`: Key created. The secret is in this response only.
- `400`: Invalid signup or proof of work. When the proof is missing, the body includes a pow object.
- `429`: Signup rate limit, daily cap, or email cap.

## GET /v1/keys/me

Show usage and credit balance for the current key

Returns metadata, usage and credit balance for the Bearer key. The secret is never returned. Call it to check remaining quota before a batch of judge or consensus calls. Requires a Bearer key; free.

- `200`: Key metadata.
- `401`: API key was not recognized.

## DELETE /v1/keys/me

Delete the current key and all its data

Permanently deletes the key hash, daily counters, payment records and any retained judgments for the Bearer key. Irreversible: only call it when the user asks to delete their key or data.

- `200`: Deleted.
- `401`: API key was not recognized.

## GET /v1/retention

List calls this key chose to retain (24 hours)

Returns judge and consensus calls made with retain: true by this key. Rows expire after 24 hours. Calls without retain are never stored, so they never appear here. Requires a Bearer key.

- `200`: Retained rows.
- `401`: API key was not recognized.

## DELETE /v1/retention/{id}

Delete one retained call

Deletes one retained judge or consensus call by its retention id (from retention.id in the original response or from GET /v1/retention). Requires the Bearer key that created it.

- `id`: Retention id returned when retain was true.

- `200`: Deleted.
- `401`: API key was not recognized.
- `404`: No such row for this key.

## POST /v1/packs/{pack}/buy

Buy a prepaid pack with USDC on Base or Solana

Starter is $30 USD for 10000 calls. Builder is $100 USD for 50000 calls. The price is paid in USDC on Base or Solana. Base is eip155:8453 to the address in pricing.json. Solana payTo is the wallet owner, and USDC is sent to that owner's associated token account for the mint. Solana is listed in x402.accepts when that payee is set, and those payments settle through https://x402.dexter.cash. With no PAYMENT-SIGNATURE the response is 402 and accepts lists each offered network, including Bazaar metadata for the pack. accepts[0] is Base. With a settled payment, the calls are credited to api_key, or a new key is returned once. A replay of the same network and transaction, or the same nonce, does not credit twice. Card checkout is Pay with PayPal at POST /v1/packs/checkout.

- `pack`: starter or builder.
- `PAYMENT-SIGNATURE`: Base64 JSON x402 payload. Omit it to receive the 402 requirements.

```json
{
  "email": "you@example.com"
}
```

- `200`: Pack credited, or this payment was already credited.
- `400`: The JSON body is not valid.
- `401`: API key was not recognized.
- `402`: Payment required. x402Version, accepts, and a string error are top-level fields for this pack's USD price in USDC. Structured detail is error_detail.

## GET /a2a

Describe the A2A JSON-RPC endpoint

GET does not judge and does not require a payment. POST message/send maps to a judge verdict.

- `200`: Endpoint description.

## POST /a2a

A2A message/send

JSON-RPC method message/send, or the SendMessage alias. A data part with task and agent_output, or text parts, maps to POST /v1/judge. An unpaid POST returns HTTP 402 with x402Version, accepts, and error at the top level. A successful result is an agent message with text and data parts.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [
        {
          "kind": "data",
          "data": {
            "task": "State the crew size and launch site.",
            "agent_output": "The crew size was 4. The mission launched from Cape Canaveral.",
            "evidence": [
              "The mission launched on 2024-03-01 from Cape Canaveral.",
              "The crew size was 4."
            ],
            "criteria": [
              "crew size",
              "Cape Canaveral"
            ]
          }
        }
      ]
    }
  }
}
```

- `200`: JSON-RPC result. The result is an agent message.
- `400`: The JSON-RPC request could not be judged.
- `402`: Payment required. x402Version, accepts, and a string error are top-level fields. Structured detail is error_detail.
- `429`: Rate limit exceeded.

## POST /v1/packs/checkout

Start a PayPal checkout for a Starter or Builder pack

Starter is $30 for 10,000 calls. Builder is $100 for 50,000 calls. The same packs can be bought with USDC on Base or Solana at POST /v1/packs/{pack}/buy. Send pack and either an email or an api_key, not both. The response url is the PayPal approval page, where PayPal can also take a card. After approval, the return page captures the order and adds the calls to the key. If you sent an email and no key, that page shows a new API key once. A JSON body gets a JSON url. A form post is redirected to PayPal. This route returns 503 until PayPal is configured. PayPal capture and refund notifications are verified on a separate webhook that this public document omits.

```json
{
  "pack": "starter",
  "email": "dev@example.com"
}
```

- `200`: Approval URL.
- `303`: Form post. The Location header is the PayPal approval URL.
- `400`: Pack, email, or api_key was not valid.
- `401`: API key was not recognized.
- `503`: PayPal checkout is not configured.

## POST /mcp

MCP Streamable HTTP endpoint (judge, consensus, get_api_key, buy_pack)

JSON-RPC for initialize, tools/list, and tools/call. Tools are judge, consensus, get_api_key, and buy_pack. An unpaid judge or consensus tools/call returns HTTP 200 with result.isError true and structuredContent set to the PaymentRequirements. Pay by retrying with params._meta['x402/payment']. Settlement is echoed in result._meta['x402/payment-response']. GET is reserved and returns 405 because this server does not open an SSE stream. OPTIONS /mcp allows any Origin.

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}
```

- `200`: JSON-RPC result. An unpaid judge or consensus tools/call is HTTP 200 with result.isError true and structuredContent set to the PaymentRequirements. content[0].text is that same JSON. A settled call echoes result._meta['x402/payment-response']. get_api_key does not require a payment.
- `202`: Notification accepted.
