{
  "openapi": "3.1.0",
  "info": {
    "title": "x402 Payment Firewall API",
    "version": "0.1.0",
    "description": "Evaluate x402 payment challenges against caller-defined security policies before an AI agent signs or pays.",
    "contact": {
      "email": "yaumglyy@gmail.com",
      "url": "https://github.com/vity-agent/x402-payment-firewall"
    },
    "x-guidance": "Use POST /api/evaluate before an agent signs an x402 payment. Submit the merchant PaymentRequired object, the selected payment requirements, request context, and the policy to enforce. A decision of allow means the supplied payment satisfies the stateless hosted checks; signing still happens locally."
  },
  "servers": [
    {
      "url": "https://x402-payment-firewall.vercel.app",
      "description": "Production API"
    }
  ],
  "tags": [
    {
      "name": "Firewall",
      "description": "Pre-signing x402 payment policy evaluation"
    }
  ],
  "paths": {
    "/api/evaluate": {
      "post": {
        "operationId": "evaluatePayment",
        "summary": "Evaluate an x402 payment before signing",
        "description": "Checks an x402 v2 payment challenge against caller-defined domain, network, asset, scheme, recipient, recipient-pinning, exact resource URL, and per-request amount policies. At least one stateless policy rule is required. This is a policy evaluator, not an independent merchant reputation or wallet-security oracle. The hosted endpoint is stateless; daily budgets and duplicate prevention remain local SDK features.",
        "tags": ["Firewall"],
        "security": [
          {
            "x402": []
          }
        ],
        "x-payment-info": {
          "price": {
            "mode": "fixed",
            "currency": "USD",
            "amount": "0.100000"
          },
          "protocols": [
            {
              "x402": {}
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EvaluateRequest"
              },
              "example": {
                "policy": {
                  "allowedDomains": ["api.example.com"],
                  "allowedNetworks": ["eip155:8453"],
                  "allowedAssets": ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"],
                  "allowedSchemes": ["exact"],
                  "maxPerRequest": [
                    {
                      "network": "eip155:8453",
                      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                      "amount": "20000"
                    }
                  ]
                },
                "input": {
                  "paymentRequired": {
                    "x402Version": 2,
                    "resource": {
                      "url": "https://api.example.com/weather"
                    },
                    "accepts": [
                      {
                        "scheme": "exact",
                        "network": "eip155:8453",
                        "amount": "10000",
                        "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                        "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
                      }
                    ]
                  },
                  "selectedRequirements": {
                    "scheme": "exact",
                    "network": "eip155:8453",
                    "amount": "10000",
                    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
                    "payTo": "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
                  },
                  "request": {
                    "agentId": "research-agent",
                    "method": "GET",
                    "url": "https://api.example.com/weather"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment policy decision",
            "headers": {
              "PAYMENT-RESPONSE": {
                "description": "Base64-encoded x402 v2 settlement response",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid evaluation request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Payment Required",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "Base64-encoded x402 v2 PaymentRequired challenge",
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequiredError"
                }
              }
            }
          },
          "405": {
            "description": "Method not allowed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "x402": {
        "type": "apiKey",
        "in": "header",
        "name": "PAYMENT-SIGNATURE",
        "description": "Base64-encoded x402 v2 payment payload. Obtain payment requirements from the PAYMENT-REQUIRED header returned by an unpaid request."
      }
    },
    "schemas": {
      "AtomicAmount": {
        "type": "string",
        "pattern": "^(0|[1-9][0-9]*)$",
        "description": "Non-negative integer amount in the asset's smallest unit",
        "examples": ["1000"]
      },
      "AmountLimit": {
        "type": "object",
        "additionalProperties": false,
        "required": ["network", "asset", "amount"],
        "properties": {
          "network": {
            "type": "string",
            "description": "CAIP-2 network identifier"
          },
          "asset": {
            "type": "string"
          },
          "amount": {
            "$ref": "#/components/schemas/AtomicAmount"
          }
        }
      },
      "FirewallPolicy": {
        "type": "object",
        "minProperties": 1,
        "description": "Caller-defined policy. The API requires at least one non-empty stateless rule: an allowlist, recipient pin, or per-request limit.",
        "additionalProperties": false,
        "properties": {
          "allowedDomains": {
            "type": "array",
            "maxItems": 100,
            "items": { "type": "string" }
          },
          "allowedNetworks": {
            "type": "array",
            "maxItems": 100,
            "items": { "type": "string" }
          },
          "allowedAssets": {
            "type": "array",
            "maxItems": 100,
            "items": { "type": "string" }
          },
          "allowedSchemes": {
            "type": "array",
            "maxItems": 100,
            "items": { "type": "string" }
          },
          "allowedRecipients": {
            "type": "array",
            "maxItems": 100,
            "items": { "type": "string" }
          },
          "recipientPins": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "maxItems": 100,
              "items": { "type": "string" }
            }
          },
          "maxPerRequest": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/AmountLimit"
            }
          },
          "dailyBudgets": {
            "type": "array",
            "maxItems": 100,
            "description": "Accepted for SDK compatibility but not enforced by the stateless hosted endpoint",
            "items": {
              "$ref": "#/components/schemas/AmountLimit"
            }
          },
          "bindRequestDomain": {
            "type": "boolean",
            "default": true
          },
          "duplicateTtlMs": {
            "type": "integer",
            "minimum": 0,
            "description": "Accepted for SDK compatibility but not enforced by the stateless hosted endpoint"
          }
        }
      },
      "PaymentRequirements": {
        "type": "object",
        "additionalProperties": false,
        "required": ["scheme", "network", "amount", "asset", "payTo"],
        "properties": {
          "scheme": { "type": "string" },
          "network": { "type": "string" },
          "amount": { "$ref": "#/components/schemas/AtomicAmount" },
          "asset": { "type": "string" },
          "payTo": { "type": "string" },
          "maxTimeoutSeconds": { "type": "integer", "minimum": 1 },
          "extra": { "type": "object", "additionalProperties": true }
        }
      },
      "PaymentRequired": {
        "type": "object",
        "additionalProperties": false,
        "required": ["x402Version", "resource", "accepts"],
        "properties": {
          "x402Version": { "type": "integer", "const": 2 },
          "resource": {
            "type": "object",
            "additionalProperties": false,
            "required": ["url"],
            "properties": {
              "url": { "type": "string", "format": "uri" },
              "description": { "type": "string" },
              "mimeType": { "type": "string" }
            }
          },
          "accepts": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": { "$ref": "#/components/schemas/PaymentRequirements" }
          },
          "extensions": { "type": "object", "additionalProperties": true }
        }
      },
      "RequestContext": {
        "type": "object",
        "additionalProperties": false,
        "required": ["agentId", "url"],
        "properties": {
          "agentId": { "type": "string", "minLength": 1 },
          "sessionId": { "type": "string" },
          "tool": { "type": "string" },
          "intent": { "type": "string" },
          "method": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "bodyHash": { "type": "string" }
        }
      },
      "EvaluateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["policy", "input"],
        "properties": {
          "policy": { "$ref": "#/components/schemas/FirewallPolicy" },
          "input": {
            "type": "object",
            "additionalProperties": false,
            "required": ["paymentRequired", "selectedRequirements", "request"],
            "properties": {
              "paymentRequired": { "$ref": "#/components/schemas/PaymentRequired" },
              "selectedRequirements": { "$ref": "#/components/schemas/PaymentRequirements" },
              "request": { "$ref": "#/components/schemas/RequestContext" }
            }
          }
        }
      },
      "FirewallDecision": {
        "type": "object",
        "additionalProperties": false,
        "required": ["decision", "decisionId", "riskScore", "reasons", "fingerprint", "expiresAt"],
        "properties": {
          "decision": { "type": "string", "enum": ["allow", "deny", "review"] },
          "decisionId": { "type": "string", "format": "uuid" },
          "riskScore": { "type": "integer", "minimum": 0, "maximum": 100 },
          "reasons": { "type": "array", "items": { "type": "string" } },
          "fingerprint": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
          "expiresAt": { "type": "string", "format": "date-time" }
        }
      },
      "EvaluateResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["mode", "decision", "limitations"],
        "properties": {
          "mode": { "type": "string", "enum": ["paid"] },
          "decision": { "$ref": "#/components/schemas/FirewallDecision" },
          "limitations": { "type": "array", "items": { "type": "string" } }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": "string" },
          "message": { "type": "string" }
        }
      },
      "PaymentRequiredError": {
        "type": "object",
        "required": ["error", "service", "documentation"],
        "properties": {
          "error": { "type": "string", "const": "payment_required" },
          "service": { "type": "string", "const": "x402-payment-firewall" },
          "documentation": { "type": "string", "format": "uri" }
        }
      }
    }
  }
}
