{
  "openapi": "3.1.0",
  "info": {
    "title": "Grisoco Agent Transaction Check",
    "version": "1.0.0",
    "summary": "Check an AI agent's on-chain payment before it signs.",
    "description": "Call this immediately before an AI agent signs an on-chain PAYMENT: sending a supported token (USDC, USDT, DAI, WETH, or the chain's native coin) to one recipient on Base, Ethereum or Polygon, either as a transaction or as a gasless EIP-3009 transfer authorization (x402). State the intent (token, amount, recipient) and send the transaction or typed data exactly as it will be signed. The answer is allow, warn or block with a reason per check. Rule-based, no AI: the same input always gets the same verdict.\n\nNOT covered: swaps or trades, bridging, staking, lending or deposits, NFT purchases or mints, contract deployments, approvals as the goal, governance votes, tokens outside the supported list, other chains, and any off-chain or card payment. Only intent type 'payment' is accepted; anything else is a 400 and is not charged.\n\nA verdict means 'no risks detected at block N', not a guarantee. Price: 0.03 USDC per verdict from the account's prepaid balance; requests that return no verdict are not charged. Terms: https://grisoco.com/terms#tx-check\n\nTwo rules for a safe integration. 1) Take the intent (token, amount, recipient) from the task your user gave you, never from the payment request, checkout page or merchant: if the same party supplies the transaction and the intent, the check only confirms they match each other. 2) Fail closed: sign only on allow. No answer, an error, a timeout or a 400 means do not sign. Retry a 503 after Retry-After and back off on a 429; allow about 10 seconds per call (the first call after a quiet spell starts the service).",
    "contact": { "email": "contact@grisoco.com", "url": "https://grisoco.com/transaction-check" },
    "termsOfService": "https://grisoco.com/terms#tx-check"
  },
  "externalDocs": { "description": "Human-readable guide", "url": "https://grisoco.com/transaction-check" },
  "servers": [{ "url": "https://check.grisoco.com" }],
  "security": [{ "apiKey": [] }],
  "paths": {
    "/v1/check": {
      "post": {
        "operationId": "checkTransaction",
        "summary": "Check a payment transaction or signature before the agent signs it",
        "description": "Send exactly one of `tx` (a transaction) or `signature` (an EIP-712 message to sign). Charged 0.03 USDC only when a verdict is returned (200).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CheckRequest" },
              "examples": {
                "transaction": {
                  "summary": "A USDC payment on Base",
                  "value": {
                    "chain_id": 8453,
                    "from": "0x1111111111111111111111111111111111111111",
                    "intent": { "type": "payment", "token": "USDC", "amount": "5.00", "recipient": "0x2222222222222222222222222222222222222222" },
                    "tx": { "to": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913", "data": "0xa9059cbb…", "value": "0x0" },
                    "policy": { "max_amount": "50", "allowed_recipients": ["0x2222222222222222222222222222222222222222"] }
                  }
                },
                "signature": {
                  "summary": "A gasless x402 payment (EIP-3009 TransferWithAuthorization)",
                  "value": {
                    "chain_id": 8453,
                    "from": "0x1111111111111111111111111111111111111111",
                    "intent": { "type": "payment", "token": "USDC", "amount": "0.10", "recipient": "0x2222222222222222222222222222222222222222" },
                    "signature": { "typed_data": { "types": {}, "primaryType": "TransferWithAuthorization", "domain": {}, "message": {} } }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "A verdict. Charged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckResponse" } } } },
          "400": { "description": "Malformed request, unsupported chain, token or intent type. Not charged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "description": "Missing or unknown API key. Not charged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "402": { "description": "Balance or the key's daily spend cap does not cover a check. Not charged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "The key lacks the checks:write scope. Not charged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "413": { "description": "Body over 256 KB. Not charged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Over the key's limit of 60 checks a minute. Not charged; retry after the Retry-After header.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "503": { "description": "The chain could not be read. Not charged; retry.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": { "type": "http", "scheme": "bearer", "description": "A Grisoco API key (grisoco_sk_…), created at https://grisoco.com/app under Create API key. The same key used for audits." }
    },
    "schemas": {
      "CheckRequest": {
        "type": "object",
        "required": ["chain_id", "from", "intent"],
        "properties": {
          "chain_id": { "type": "integer", "enum": [1, 137, 8453], "description": "1 Ethereum, 137 Polygon, 8453 Base." },
          "from": { "type": "string", "description": "The agent's wallet address that will sign." },
          "intent": { "$ref": "#/components/schemas/PaymentIntent" },
          "tx": {
            "type": "object",
            "description": "The transaction exactly as it will be signed.",
            "properties": {
              "to": { "type": "string", "description": "The address the transaction calls." },
              "data": { "type": "string", "description": "Hex calldata, 0x for none." },
              "value": { "type": "string", "description": "Hex wei of the native coin sent." },
              "type": { "type": "string", "description": "Transaction type, e.g. 0x2. A type-4 (EIP-7702) transaction is always blocked." },
              "authorization_list": { "type": "array", "items": { "type": "object" }, "description": "Any EIP-7702 authorization list. Non-empty is always blocked." }
            }
          },
          "signature": {
            "type": "object",
            "description": "A message the agent is asked to sign, instead of tx.",
            "properties": {
              "typed_data": { "type": "object", "description": "The eth_signTypedData_v4 payload. Only an EIP-3009 transfer authorization for the official token can be allowed; any permit or Permit2 message is blocked." },
              "eip7702_authorization": { "type": "object", "description": "An EIP-7702 authorization to sign. Always blocked." }
            }
          },
          "policy": {
            "type": "object",
            "description": "Optional limits of your own. Anything outside them is blocked.",
            "properties": {
              "max_amount": { "type": "string" },
              "allowed_tokens": { "type": "array", "items": { "type": "string" } },
              "allowed_recipients": { "type": "array", "items": { "type": "string" } }
            }
          }
        }
      },
      "PaymentIntent": {
        "type": "object",
        "required": ["token", "amount", "recipient"],
        "properties": {
          "type": { "type": "string", "enum": ["payment"], "default": "payment", "description": "Only payments are checked." },
          "token": { "type": "string", "description": "Symbol or official address. Ethereum: ETH, USDC, USDT, DAI, WETH. Polygon: POL, USDC, USDT, DAI, WETH. Base: ETH, USDC, DAI, WETH." },
          "amount": { "type": "string", "description": "Decimal string in token units, e.g. \"5.00\"." },
          "recipient": { "type": "string", "description": "The address that must receive the payment." }
        }
      },
      "CheckResponse": {
        "type": "object",
        "properties": {
          "check_id": { "type": "string" },
          "verdict": { "type": "string", "enum": ["allow", "warn", "block"], "description": "Sign only on allow. warn needs a human or policy decision. block: do not sign." },
          "summary": { "type": "string" },
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string", "description": "I1 official token, I2 exact amount out, I3 right recipient, I4 nothing else leaves, I5 no spending rights, I6 does not revert, I7 no change of control or EIP-7702, I8 measured balances agree, P1-P3 your policy, S0-S3 signature checks." },
                "status": { "type": "string", "enum": ["pass", "warn", "fail"] },
                "message": { "type": "string" }
              }
            }
          },
          "sender_changes": {
            "type": "array",
            "description": "Every balance change of the sender, per token. Negative leaves the wallet.",
            "items": {
              "type": "object",
              "properties": {
                "token": { "type": "string", "description": "Token address, or \"native\" for the chain's own coin." },
                "token_id": { "type": ["integer", "null"], "description": "Set for an NFT." },
                "symbol": { "type": ["string", "null"] },
                "amount": { "type": "string", "description": "Signed raw base units, e.g. \"-5000000\" for 5 USDC (6 decimals)." },
                "display": { "type": "string", "description": "The same change in token units, e.g. \"-5 USDC\". Read this one." }
              }
            }
          },
          "chain_id": { "type": "integer" },
          "block": { "type": ["integer", "null"], "description": "The block simulated against; null for a signature." },
          "latency_ms": { "type": "integer" },
          "charged_cents": { "type": "integer" }
        }
      },
      "Error": {
        "type": "object",
        "properties": { "detail": { "type": "string" }, "charged_cents": { "type": "integer", "const": 0 } }
      }
    }
  }
}
