{
  "openapi": "3.1.0",
  "info": {
    "title": "Treasury Copilot Agent API",
    "version": "1.2.0",
    "description": "HTTP API for policy-gated agent spending. Current automatic execution uses Base Sepolia test USDC. POST /spend is asynchronous and returns 202 after GenLayer submission."
  },
  "servers": [
    {
      "url": "https://treasury-copilot-genjury.vercel.app/api/v1",
      "description": "Base Sepolia testnet"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/spend": {
      "post": {
        "summary": "Submit a spend request",
        "description": "Verifies the API key, policy binding, exact token units, and optional evidence; submits queue_request with the platform signer; then returns 202. Policy V5 starts GenLayer prompt-comparative review inside that same transaction. Legacy V4 requests use bounded automatic review with cron recovery.",
        "operationId": "createSpendRequest",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SpendRequest"
              },
              "examples": {
                "signedInvoice": {
                  "value": {
                    "agent_address": "0x1111111111111111111111111111111111111111",
                    "recipient": "0x2222222222222222222222222222222222222222",
                    "amount": "25.00",
                    "category": "software_subscription",
                    "justification": "Vercel invoice INV-4471",
                    "idempotency_key": "vercel-inv-4471-2026-07",
                    "evidence": [
                      {
                        "type": "signed_invoice",
                        "invoice_id": "INV-4471",
                        "merchant_id": "vercel",
                        "expected_recipient": "0x2222222222222222222222222222222222222222",
                        "expected_amount": "25000000",
                        "issued_at": 1784800000,
                        "expires_at": 1785400000,
                        "content_hash": "0x7777777777777777777777777777777777777777777777777777777777777777",
                        "signer": "0x3333333333333333333333333333333333333333",
                        "signature": "0x..."
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Submitted to GenLayer; poll the Location URL",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "const": 10
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpendAccepted"
                }
              }
            }
          },
          "200": {
            "description": "An identical idempotent request already exists and is finalized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpendAccepted"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/requests": {
      "get": {
        "summary": "Recover a request by idempotency key",
        "operationId": "findRequestByIdempotencyKey",
        "parameters": [
          {
            "name": "idempotency_key",
            "in": "query",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/IdempotencyKey"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Request is visible on GenLayer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequestEnvelope"
                }
              }
            }
          },
          "202": {
            "description": "The deterministic request ID is known, but the request is not visible on GenLayer yet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["request_id", "status", "retryable"],
                  "properties": {
                    "request_id": {
                      "$ref": "#/components/schemas/RequestId"
                    },
                    "status": {
                      "const": "not_found_or_pending"
                    },
                    "message": {
                      "type": "string"
                    },
                    "retryable": {
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          }
        }
      }
    },
    "/requests/{request_id}": {
      "get": {
        "summary": "Get one on-chain request",
        "operationId": "getRequest",
        "parameters": [
          {
            "name": "request_id",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/RequestId"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current request lifecycle state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequestEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Request is not visible for this API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/policy": {
      "get": {
        "summary": "Get safe policy configuration and recipient discovery data",
        "operationId": "getPolicy",
        "responses": {
          "200": {
            "description": "Policy state with delegation secrets removed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/balance": {
      "get": {
        "summary": "Get token balance and policy budget",
        "operationId": "getBalance",
        "responses": {
          "200": {
            "description": "Live EVM token balance and GenLayer policy limits",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamFailure"
          }
        }
      }
    },
    "/history": {
      "get": {
        "summary": "List on-chain request history",
        "operationId": "getHistory",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Newest requests first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["policy", "agent", "requests"],
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/Address"
                    },
                    "agent": {
                      "$ref": "#/components/schemas/Address"
                    },
                    "requests": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Request"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/Unprocessable"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "tcp_ agent API key"
      }
    },
    "schemas": {
      "Address": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{40}$"
      },
      "RequestId": {
        "type": "string",
        "pattern": "^0x[0-9a-fA-F]{64}$"
      },
      "IdempotencyKey": {
        "type": "string",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[A-Za-z0-9._:-]+$"
      },
      "Amount": {
        "type": "string",
        "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]+)?$",
        "description": "Positive display-unit decimal string. Scientific notation, JSON numbers, negatives, zero, and excess token precision are rejected."
      },
      "SpendRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["agent_address", "recipient", "amount", "category", "justification", "idempotency_key"],
        "properties": {
          "agent_address": {
            "$ref": "#/components/schemas/Address"
          },
          "recipient": {
            "$ref": "#/components/schemas/Address"
          },
          "amount": {
            "$ref": "#/components/schemas/Amount"
          },
          "category": {
            "type": "string",
            "minLength": 2,
            "maxLength": 64,
            "description": "Owner-defined label. The API does not maintain a global category enum."
          },
          "justification": {
            "type": "string",
            "minLength": 4,
            "maxLength": 1200
          },
          "idempotency_key": {
            "$ref": "#/components/schemas/IdempotencyKey"
          },
          "evidence": {
            "type": "array",
            "maxItems": 3,
            "items": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/InvoiceUrlEvidence"
                },
                {
                  "$ref": "#/components/schemas/SignedInvoiceEvidence"
                }
              ]
            }
          }
        }
      },
      "InvoiceCommon": {
        "type": "object",
        "required": ["invoice_id", "merchant_id", "expected_recipient", "expected_amount", "issued_at"],
        "properties": {
          "invoice_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "merchant_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "expected_recipient": {
            "$ref": "#/components/schemas/Address"
          },
          "expected_amount": {
            "type": "string",
            "pattern": "^[0-9]+$",
            "description": "Exact token base units, for example 25000000 for 25 USDC with 6 decimals."
          },
          "issued_at": {
            "type": "integer",
            "minimum": 1
          },
          "expires_at": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "InvoiceUrlEvidence": {
        "allOf": [
          {
            "$ref": "#/components/schemas/InvoiceCommon"
          },
          {
            "type": "object",
            "required": ["type", "uri", "merchant_domain", "sha256"],
            "properties": {
              "type": {
                "const": "invoice_url"
              },
              "uri": {
                "type": "string",
                "format": "uri",
                "maxLength": 2048,
                "description": "HTTPS only. Private, localhost, link-local, reserved destinations and nonstandard ports are rejected."
              },
              "merchant_domain": {
                "type": "string",
                "maxLength": 253
              },
              "sha256": {
                "type": "string",
                "pattern": "^0x[0-9a-fA-F]{64}$"
              }
            }
          }
        ]
      },
      "SignedInvoiceEvidence": {
        "allOf": [
          {
            "$ref": "#/components/schemas/InvoiceCommon"
          },
          {
            "type": "object",
            "required": ["type", "signer", "signature", "content_hash"],
            "properties": {
              "type": {
                "const": "signed_invoice"
              },
              "signer": {
                "$ref": "#/components/schemas/Address"
              },
              "signature": {
                "type": "string",
                "pattern": "^0x[0-9a-fA-F]{130}$"
              },
              "content_hash": {
                "type": "string",
                "pattern": "^0x[0-9a-fA-F]{64}$"
              }
            }
          }
        ]
      },
      "Request": {
        "type": "object",
        "required": ["request_id", "recipient", "amount", "amount_units", "category", "justification", "verdict", "status", "execution_status"],
        "properties": {
          "request_id": {
            "$ref": "#/components/schemas/RequestId"
          },
          "recipient": {
            "$ref": "#/components/schemas/Address"
          },
          "amount": {
            "type": "string"
          },
          "amount_units": {
            "type": "string",
            "pattern": "^[0-9]+$"
          },
          "category": {
            "type": "string"
          },
          "justification": {
            "type": "string"
          },
          "evidence": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "evidence_digest": {
            "type": "string"
          },
          "invoice_key": {
            "type": "string"
          },
          "verdict": {
            "enum": ["pending", "approved", "denied"]
          },
          "decision_mode": {
            "enum": ["deterministic", "prompt_comparative"]
          },
          "status": {
            "enum": ["submitted", "reviewing", "review_pending", "pending", "approved", "denied", "ready", "executing", "failed", "executed", "not_applicable"]
          },
          "reasoning": {
            "type": "string"
          },
          "execution_status": {
            "enum": ["submitted", "reviewing", "review_pending", "ready", "executing", "failed", "executed", "not_applicable"]
          },
          "execution_error": {
            "type": "string"
          },
          "tx_hash": {
            "type": "string"
          },
          "explorer_url": {
            "type": ["string", "null"]
          },
          "created_at": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        }
      },
      "SpendAccepted": {
        "type": "object",
        "required": ["request_id", "verdict", "status", "request", "poll_url", "idempotent_replay", "genlayer"],
        "properties": {
          "request_id": {
            "$ref": "#/components/schemas/RequestId"
          },
          "verdict": {
            "enum": ["pending", "approved", "denied"]
          },
          "reasoning": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "request": {
            "$ref": "#/components/schemas/Request"
          },
          "poll_url": {
            "type": "string"
          },
          "idempotent_replay": {
            "type": "boolean"
          },
          "genlayer": {
            "type": "object",
            "properties": {
              "request_tx_hash": {
                "type": ["string", "null"]
              }
            }
          }
        }
      },
      "RequestEnvelope": {
        "type": "object",
        "required": ["policy", "request"],
        "properties": {
          "policy": {
            "$ref": "#/components/schemas/Address"
          },
          "request": {
            "$ref": "#/components/schemas/Request"
          }
        }
      },
      "PolicyResponse": {
        "type": "object",
        "required": ["policy", "state"],
        "properties": {
          "policy": {
            "$ref": "#/components/schemas/Address"
          },
          "state": {
            "type": "object",
            "required": ["contract_version", "whitelist_enabled", "whitelisted_recipients", "security"],
            "properties": {
              "contract_version": {
                "type": "string"
              },
              "authorized_agent": {
                "$ref": "#/components/schemas/Address"
              },
              "delegated_account": {
                "$ref": "#/components/schemas/Address"
              },
              "token_address": {
                "$ref": "#/components/schemas/Address"
              },
              "evm_chain_id": {
                "type": "string"
              },
              "per_tx_cap_atto": {
                "type": "string"
              },
              "weekly_cap_atto": {
                "type": "string"
              },
              "weekly_spent_atto": {
                "type": "string"
              },
              "policy_text": {
                "type": "string"
              },
              "whitelist_enabled": {
                "type": "boolean"
              },
              "whitelisted_recipients": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Address"
                }
              },
              "delegation_registered": {
                "type": "boolean"
              },
              "security": {
                "type": "object",
                "properties": {
                  "contract_version": {
                    "type": "string"
                  },
                  "semantic_review_required_for_all_requests": {
                    "type": "boolean"
                  },
                  "asynchronous_review_supported": {
                    "type": "boolean"
                  },
                  "legacy_fast_approval_active": {
                    "type": "boolean"
                  },
                  "warnings": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error", "message", "fields", "retryable"],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "request_id": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean"
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Malformed JSON or unsupported request shape",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, malformed, expired, tampered, rotated, or revoked API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Agent, policy, owner, chain, token, or funding binding mismatch",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Idempotency conflict or policy migration required",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unprocessable": {
        "description": "Invalid amount, address, evidence, category, justification, or unsupported chain",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Infrastructure rate limit exceeded when distributed edge limiting is configured; honor Retry-After",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UpstreamFailure": {
        "description": "GenLayer, EVM RPC, or 1Shot upstream failure; retry with the same idempotency key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unavailable": {
        "description": "Platform signer, GenLayer consensus, execution-slot capacity, or service configuration unavailable. Honor Retry-After when present.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}
