{
  "openapi": "3.1.0",
  "info": {
    "title": "Payperwork API",
    "version": "0.4.0",
    "summary": "Pay-per-call services for AI agents over x402.",
    "description": "Every paid route answers an unpaid request with 402 and the exact price; the caller's wallet signs a USDC transfer for that amount and retries with PAYMENT-SIGNATURE. The call runs only after verification and is settled only if it succeeds. No account, no API key. Plain text version: /llms.txt. Security model: GET / (JSON) under `security`.",
    "contact": {
      "url": "https://payperwork.app"
    },
    "x-llms-txt": "https://payperwork.app/llms.txt",
    "x-mcp": {
      "front_door": "https://payperwork.app/mcp",
      "inference": "https://payperwork.app/ai/mcp",
      "drops": "https://payperwork.app/drop/mcp"
    }
  },
  "servers": [
    {
      "url": "https://payperwork.app"
    }
  ],
  "tags": [
    {
      "name": "inference",
      "description": "Payperwork Inference: chat completions on frontier models, priced per call from the request."
    },
    {
      "name": "drops",
      "description": "Payperwork Drops: temporary key-value storage and hand-off channels between agents."
    },
    {
      "name": "meta",
      "description": "Service description and documents for agents."
    }
  ],
  "components": {
    "securitySchemes": {
      "x402": {
        "type": "apiKey",
        "in": "header",
        "name": "PAYMENT-SIGNATURE",
        "description": "x402 v2 payment: base64 JSON PaymentPayload with an EIP-3009 authorisation for the amount the 402 quoted. Scheme exact, USDC on Base (eip155:8453)."
      }
    },
    "schemas": {
      "Unpaid": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "how": {
            "type": "string"
          },
          "docs": {
            "type": "string"
          },
          "quote": {
            "$ref": "#/components/schemas/Quote"
          }
        }
      },
      "Quote": {
        "type": "object",
        "description": "Inference only: the price the 402 asks for, derived from the request.",
        "properties": {
          "model": {
            "type": "string"
          },
          "price_usd": {
            "type": "number"
          },
          "input_tokens_est": {
            "type": "integer"
          },
          "max_tokens": {
            "type": "integer"
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "role",
          "content"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "system",
              "user",
              "assistant",
              "tool"
            ]
          },
          "content": {},
          "name": {
            "type": "string"
          },
          "tool_call_id": {
            "type": "string"
          }
        }
      },
      "ChatRequest": {
        "type": "object",
        "required": [
          "messages"
        ],
        "properties": {
          "model": {
            "type": "string",
            "default": "anthropic/claude-haiku-5.5",
            "enum": [
              "anthropic/claude-haiku-5.5",
              "google/gemini-3.8-flash",
              "openai/gpt-5.4-mini",
              "xai/grok-4.7",
              "anthropic/claude-sonnet-5.5",
              "openai/gpt-5.5",
              "openai/gpt-6-luna",
              "openai/gpt-6.1-terra",
              "openai/gpt-6.1-sol"
            ]
          },
          "messages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "max_tokens": {
            "type": "integer",
            "minimum": 1,
            "maximum": 8192,
            "default": 1024,
            "description": "Thinking models spend part of this on reasoning; give them 128 or more."
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2
          },
          "top_p": {
            "type": "number"
          },
          "stop": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            ]
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "tool_choice": {},
          "response_format": {
            "type": "object"
          },
          "reasoning_effort": {
            "type": "string",
            "enum": [
              "none",
              "low",
              "medium",
              "high"
            ],
            "description": "Default low for thinking models."
          }
        }
      },
      "ChatCompletion": {
        "type": "object",
        "description": "OpenAI chat completion shape for every provider.",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "chat.completion"
          },
          "created": {
            "type": "integer"
          },
          "model": {
            "type": "string"
          },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "message": {
                  "type": "object",
                  "properties": {
                    "role": {
                      "type": "string"
                    },
                    "content": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "tool_calls": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                },
                "finish_reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "prompt_tokens": {
                "type": "integer"
              },
              "completion_tokens": {
                "type": "integer",
                "description": "Includes hidden reasoning tokens where the provider bills them."
              },
              "total_tokens": {
                "type": "integer"
              }
            }
          }
        }
      },
      "ModelsList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "default": {
            "type": "string"
          },
          "pricing": {
            "type": "object",
            "properties": {
              "how": {
                "type": "string"
              },
              "markup": {
                "type": "number"
              },
              "fixed_usd": {
                "type": "number"
              },
              "min_usd": {
                "type": "number"
              },
              "chars_per_token_estimate": {
                "type": "number"
              }
            }
          },
          "limits": {
            "type": "object"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "owned_by": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "tier": {
                  "type": "string"
                },
                "context": {
                  "type": "integer"
                },
                "provider_usd_per_million_tokens": {
                  "type": "object",
                  "properties": {
                    "input": {
                      "type": "number"
                    },
                    "output": {
                      "type": "number"
                    }
                  }
                },
                "min_price_usd": {
                  "type": "number"
                },
                "thinking": {
                  "type": "boolean"
                },
                "example_price_usd_1k_in_1k_out": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "DropItem": {
        "type": "object",
        "properties": {
          "box": {
            "type": "string",
            "pattern": "^box_[0-9a-f]{32}$"
          },
          "key": {
            "type": "string"
          },
          "value": {
            "type": "string"
          },
          "seq": {
            "type": "integer",
            "description": "Box-wide, increases on every put"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "bytes": {
            "type": "integer"
          }
        }
      },
      "DropMiss": {
        "type": "object",
        "properties": {
          "box": {
            "type": "string"
          },
          "key": {
            "type": "string"
          },
          "value": {
            "type": "null"
          },
          "timed_out": {
            "type": "boolean"
          },
          "found": {
            "type": "boolean"
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/ai/v1/chat/completions": {
      "post": {
        "tags": [
          "inference"
        ],
        "operationId": "chatCompletions",
        "security": [
          {
            "x402": []
          }
        ],
        "summary": "Chat completion on a frontier model, priced per call",
        "description": "Price = (estimated input tokens x input rate + max_tokens x output rate) x 1.4 + $0.0005, minimum $0.002; the 402 states it for your exact body. Settled only if the model call succeeds. No streaming. Input up to 200000 characters.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChatRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The completion. Settled; PAYMENT-RESPONSE carries the transaction hash.",
            "headers": {
              "PAYMENT-RESPONSE": {
                "schema": {
                  "type": "string"
                },
                "description": "base64 JSON x402 settlement: success, transaction hash, network, payer"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatCompletion"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body, unknown model, or unsupported option",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Payment required. The PAYMENT-REQUIRED header carries a base64 JSON x402 PaymentRequired object (accepts[]: scheme, network, asset, amount in atomic USDC, payTo, maxTimeoutSeconds). Sign an EIP-3009 transfer authorisation for exactly that amount and retry the same request with it base64-encoded in the PAYMENT-SIGNATURE header. Any x402 client does this for you.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "schema": {
                  "type": "string"
                },
                "description": "base64 JSON x402 PaymentRequired"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unpaid"
                }
              }
            }
          },
          "413": {
            "description": "Input over the character limit"
          },
          "502": {
            "description": "The model call failed after payment verification; nothing was settled or charged",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Inference not configured on this deployment; nothing charged"
          }
        }
      }
    },
    "/ai/v1/models": {
      "get": {
        "tags": [
          "inference"
        ],
        "operationId": "listModels",
        "summary": "Models, provider rates, pricing formula and limits. Free.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelsList"
                }
              }
            }
          }
        }
      }
    },
    "/box/{box}/{key}": {
      "parameters": [
        {
          "name": "box",
          "in": "path",
          "required": true,
          "description": "A box_ id, a channel name (hashed into a box id), or `me` for the paying wallet's own box",
          "schema": {
            "type": "string",
            "maxLength": 512
          }
        },
        {
          "name": "key",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 256
          }
        }
      ],
      "put": {
        "tags": [
          "drops"
        ],
        "operationId": "putDrop",
        "security": [
          {
            "x402": []
          }
        ],
        "summary": "Store the request body under key in box ($0.002)",
        "description": "Body is the value (text, up to 65536 bytes). Query ttl in seconds, 60 to 604800, default 3600. Stored once the payment settles; a failed payment stores nothing. Overwrites and bumps seq.",
        "parameters": [
          {
            "name": "ttl",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 60,
              "maximum": 604800,
              "default": 3600
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              }
            },
            "*/*": {
              "schema": {
                "type": "string"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored and settled.",
            "headers": {
              "PAYMENT-RESPONSE": {
                "schema": {
                  "type": "string"
                },
                "description": "base64 JSON x402 settlement: success, transaction hash, network, payer"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DropItem"
                }
              }
            }
          },
          "400": {
            "description": "Value too large, box full, or `me` without an identifiable payer"
          },
          "402": {
            "description": "Payment required. The PAYMENT-REQUIRED header carries a base64 JSON x402 PaymentRequired object (accepts[]: scheme, network, asset, amount in atomic USDC, payTo, maxTimeoutSeconds). Sign an EIP-3009 transfer authorisation for exactly that amount and retry the same request with it base64-encoded in the PAYMENT-SIGNATURE header. Any x402 client does this for you.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "schema": {
                  "type": "string"
                },
                "description": "base64 JSON x402 PaymentRequired"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unpaid"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "drops"
        ],
        "operationId": "getDrop",
        "summary": "Read a key. Free, rate limited; needs a box_ id (not a channel name)",
        "responses": {
          "200": {
            "description": "The item",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DropItem"
                }
              }
            }
          },
          "400": {
            "description": "box must be a box_ id"
          },
          "404": {
            "description": "No such key, or expired"
          },
          "429": {
            "description": "Over 60 free reads per minute from this address, or 600 per minute for this box; Retry-After says how long",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            }
          }
        }
      }
    },
    "/box/{box}/{key}/wait": {
      "parameters": [
        {
          "name": "box",
          "in": "path",
          "required": true,
          "description": "A box_ id, a channel name (hashed into a box id), or `me` for the paying wallet's own box",
          "schema": {
            "type": "string",
            "maxLength": 512
          }
        },
        {
          "name": "key",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "maxLength": 256
          }
        }
      ],
      "get": {
        "tags": [
          "drops"
        ],
        "operationId": "waitDrop",
        "security": [
          {
            "x402": []
          }
        ],
        "summary": "Block until key has a seq greater than since, up to timeout seconds ($0.001)",
        "description": "Charged whether or not a value arrives. Returns immediately if a newer value already exists; otherwise waits up to timeout (max 25 s) and returns value null with timed_out true.",
        "parameters": [
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "timeout",
            "in": "query",
            "schema": {
              "type": "number",
              "minimum": 0,
              "maximum": 25,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The item, or {value: null, timed_out: true}. Settled either way.",
            "headers": {
              "PAYMENT-RESPONSE": {
                "schema": {
                  "type": "string"
                },
                "description": "base64 JSON x402 settlement: success, transaction hash, network, payer"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DropItem"
                }
              }
            }
          },
          "402": {
            "description": "Payment required. The PAYMENT-REQUIRED header carries a base64 JSON x402 PaymentRequired object (accepts[]: scheme, network, asset, amount in atomic USDC, payTo, maxTimeoutSeconds). Sign an EIP-3009 transfer authorisation for exactly that amount and retry the same request with it base64-encoded in the PAYMENT-SIGNATURE header. Any x402 client does this for you.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "schema": {
                  "type": "string"
                },
                "description": "base64 JSON x402 PaymentRequired"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unpaid"
                }
              }
            }
          }
        }
      }
    },
    "/": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "describe",
        "summary": "Service description: services, prices, limits, security model (JSON; HTML for browsers)",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "llmsTxt",
        "summary": "Everything an agent needs, as plain text",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {}
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "openapi",
        "summary": "This document",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  }
}