{
  "openapi": "3.0.3",
  "info": {
    "title": "AgentPay API",
    "version": "0.1.0",
    "description": "Payment infrastructure for AI agents. Microtransactions without credit cards.",
    "contact": {
      "url": "https://github.com/7399795590/agentpay"
    }
  },
  "servers": [
    {
      "url": "https://web-production-32de5.up.railway.app",
      "description": "Production"
    },
    {
      "url": "http://localhost:8080",
      "description": "Local development"
    }
  ],
  "tags": [
    {
      "name": "health",
      "description": "Health check"
    },
    {
      "name": "agents",
      "description": "Agent management"
    },
    {
      "name": "transactions",
      "description": "Top-up and charge"
    },
    {
      "name": "stripe",
      "description": "Stripe payment integration"
    },
    {
      "name": "admin",
      "description": "Administrative operations (require ADMIN_API_KEY)"
    },
    {
      "name": "ledger",
      "description": "Immutable ledger & reconciliation"
    },
    {
      "name": "subscriptions",
      "description": "Recurring billing"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Service health check",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/agents/create": {
      "post": {
        "tags": [
          "agents"
        ],
        "summary": "Create a new agent",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "my-bot"
                  },
                  "webhook_url": {
                    "type": "string",
                    "example": "https://webhook.site/..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Agent created"
          },
          "400": {
            "description": "Invalid input"
          },
          "429": {
            "description": "Rate limit (5/min per IP)"
          }
        }
      }
    },
    "/agents/{agent_id}": {
      "get": {
        "tags": [
          "agents"
        ],
        "summary": "Get agent details",
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Agent found"
          },
          "404": {
            "description": "Not found"
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "description": "Retrieve an agent's current balance and metadata. Requires either a valid session (user is owner or admin) OR a Bearer agent API key."
      }
    },
    "/agents/{agent_id}/topup": {
      "post": {
        "tags": [
          "Agents"
        ],
        "summary": "Direct topup — DISABLED",
        "description": "**⚠️ DEPRECATED — This endpoint is disabled for security.**\n\nAgents cannot increase their own balance. To add funds:\n\n1. **Real payments:** `POST /agents/{agent_id}/stripe/checkout` (creates a Stripe Checkout session)\n2. **Admin topup:** `POST /admin/agents/{agent_id}/topup` (requires admin auth + CSRF + Idempotency-Key + reason)\n\nThis endpoint returns `403 Forbidden` and logs the attempt.",
        "deprecated": true,
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "403": {
            "description": "Direct topup is disabled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "example": false
                    },
                    "error": {
                      "type": "string",
                      "example": "Direct topup is disabled. Use POST /agents/<id>/stripe/checkout for real payments, or contact an admin."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/agents/{agent_id}/charge": {
      "post": {
        "tags": [
          "transactions"
        ],
        "summary": "Deduct from agent balance",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "example": 30,
                    "description": "USD cents"
                  },
                  "description": {
                    "type": "string",
                    "example": "API call fee"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Charge successful"
          },
          "402": {
            "description": "Insufficient balance"
          },
          "429": {
            "description": "Rate limit (60/min per agent)"
          }
        }
      }
    },
    "/agents/{agent_id}/transactions": {
      "get": {
        "tags": [
          "transactions"
        ],
        "summary": "List recent transactions",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "max": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Transaction list"
          }
        }
      }
    },
    "/agents/{agent_id}/stripe/checkout": {
      "post": {
        "tags": [
          "stripe"
        ],
        "summary": "Create Stripe Checkout session for top-up",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_cents"
                ],
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "example": 1000,
                    "description": "Minimum 100 (=$1)"
                  },
                  "success_url": {
                    "type": "string"
                  },
                  "cancel_url": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout URL created"
          }
        }
      }
    },
    "/stripe/config": {
      "get": {
        "tags": [
          "stripe"
        ],
        "summary": "Get Stripe publishable key (for frontend)",
        "responses": {
          "200": {
            "description": "Stripe config"
          }
        }
      }
    },
    "/agents/{agent_id}/refund": {
      "post": {
        "tags": [
          "transactions"
        ],
        "summary": "Refund a previous charge",
        "description": "Refund a previous charge transaction. Protected against double-refunds (returns 409 if already refunded). The refund amount equals the original charge amount.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "transaction_id"
                ],
                "properties": {
                  "transaction_id": {
                    "type": "integer",
                    "description": "ID of the original charge transaction"
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Optional reason"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Refund successful"
          },
          "404": {
            "description": "Transaction not found for this agent"
          },
          "409": {
            "description": "Already refunded"
          },
          "402": {
            "description": "Only charge transactions can be refunded"
          }
        }
      }
    },
    "/agents/{agent_id}/subscriptions/create": {
      "post": {
        "tags": [
          "subscriptions"
        ],
        "summary": "Create a recurring subscription",
        "description": "Creates a recurring subscription. Intervals: daily, weekly, monthly, yearly.",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "amount",
                  "interval"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "amount": {
                    "type": "integer",
                    "description": "Amount in cents"
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "daily",
                      "weekly",
                      "monthly",
                      "yearly"
                    ]
                  },
                  "description": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created"
          }
        }
      }
    },
    "/agents/{agent_id}/subscriptions": {
      "get": {
        "tags": [
          "subscriptions"
        ],
        "summary": "List agent subscriptions",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "List of subscriptions"
          }
        }
      }
    },
    "/agents/{agent_id}/subscriptions/{sub_id}/cancel": {
      "post": {
        "tags": [
          "subscriptions"
        ],
        "summary": "Cancel a subscription",
        "description": "Cancel an active subscription (soft delete).",
        "security": [
          {
            "BearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sub_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription cancelled"
          }
        }
      }
    },
    "/admin/stats": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Platform statistics",
        "description": "Returns aggregate platform statistics (agents, balances, transactions).",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Stats"
          },
          "401": {
            "description": "Missing admin key"
          },
          "403": {
            "description": "Wrong admin key"
          }
        }
      }
    },
    "/admin/agents": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "List all agents",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of agents"
          }
        }
      }
    },
    "/admin/agents/{agent_id}": {
      "delete": {
        "tags": [
          "admin"
        ],
        "summary": "Delete an agent",
        "description": "Delete an agent and all associated data.",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          }
        }
      }
    },
    "/admin/agents/{agent_id}/balance": {
      "post": {
        "tags": [
          "admin"
        ],
        "summary": "Set an agent's balance directly",
        "description": "Set an agent's balance to a specific value. This is a privileged operation and logs a signed LedgerEntry. Balance must be a non-negative integer (cents).",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "balance"
                ],
                "properties": {
                  "balance": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Balance updated"
          }
        }
      }
    },
    "/admin/agents/{agent_id}/topup": {
      "post": {
        "tags": [
          "admin",
          "transactions"
        ],
        "summary": "Admin topup (privileged)",
        "description": "Admin-only topup with strict controls:\n- Requires admin auth (Bearer <ADMIN_API_KEY>)\n- Requires X-CSRF-Token if using session auth\n- Requires Idempotency-Key header\n- Requires a reason (5-500 chars)\n- Subject to per-transaction limit ($500) and daily limit ($500)\n- Records a LedgerEntry and an audit log",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_cents",
                  "reason"
                ],
                "properties": {
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50000
                  },
                  "currency": {
                    "type": "string",
                    "default": "USD"
                  },
                  "reason": {
                    "type": "string",
                    "minLength": 5,
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Topup successful"
          },
          "400": {
            "description": "Validation failed"
          },
          "429": {
            "description": "Daily limit exceeded"
          }
        }
      }
    },
    "/admin/audit": {
      "get": {
        "tags": [
          "admin"
        ],
        "summary": "Retrieve audit log entries",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "agent_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "action",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audit entries"
          }
        }
      }
    },
    "/admin/audit/verify": {
      "get": {
        "tags": [
          "admin",
          "ledger"
        ],
        "summary": "Verify audit hash chain",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Verification result"
          }
        }
      }
    },
    "/admin/audit/checkpoint": {
      "get": {
        "tags": [
          "admin",
          "ledger"
        ],
        "summary": "Get audit checkpoint",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Checkpoint hash"
          }
        }
      }
    },
    "/admin/run-billing": {
      "post": {
        "tags": [
          "admin",
          "subscriptions"
        ],
        "summary": "Manually trigger billing",
        "description": "Run billing for all due subscriptions (admin).",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Billing result"
          }
        }
      }
    },
    "/admin/ledger/{agent_id}": {
      "get": {
        "tags": [
          "admin",
          "ledger"
        ],
        "summary": "List ledger entries for an agent",
        "description": "Returns all LedgerEntry rows for a specific agent (paginated).",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger entries"
          }
        }
      }
    },
    "/admin/audit/reconcile/{agent_id}": {
      "post": {
        "tags": [
          "admin",
          "ledger"
        ],
        "summary": "Reconcile one agent",
        "description": "Verify balance == sum(ledger entries) for a specific agent.",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "parameters": [
          {
            "name": "agent_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation result"
          }
        }
      }
    },
    "/admin/audit/reconcile-all": {
      "post": {
        "tags": [
          "admin",
          "ledger"
        ],
        "summary": "Reconcile all agents",
        "description": "Verify balance == sum(ledger entries) for every agent. Returns list of mismatches.",
        "security": [
          {
            "AdminAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation summary + mismatches"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Use: Bearer ap_xxx... (your agent API key)"
      },
      "AdminAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Admin API key. Use: Bearer <ADMIN_API_KEY>"
      }
    }
  }
}