{
  "openapi": "3.1.0",
  "info": {
    "title": "Till agent API",
    "version": "1.0.0",
    "description": "Till issues scoped keys with a required activation ceiling. Public signup is closed. Unauthenticated mint returns HTTP 402 beta_access_required. Existing tenants mint with a till_admin_ bearer after at least one provider connection exists. Hosted MCP is POST /mcp (Streamable HTTP). OAuth sign-in is not live. GET /api/checkout-result is pollable JSON; only the signed Stripe webhook fulfills checkout."
  },
  "servers": [
    {
      "url": "https://api.till.ac"
    }
  ],
  "paths": {
    "/v1/keys": {
      "post": {
        "operationId": "mintOrRequestAccess",
        "summary": "Mint a scoped key, or return HTTP 402 when unauthenticated",
        "description": "With a valid till_admin_ bearer this is an alias of POST /admin/keys. Without an Authorization header this returns HTTP 402 beta_access_required. A present but invalid Authorization header returns HTTP 401. This endpoint never mints a till_sk_ for a stranger and does not invent a Stripe pay_url.",
        "security": [
          {
            "tenantAdmin": []
          },
          {}
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateScopedKey"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Scoped key minted for an authenticated tenant. The complete till_sk_ token is returned once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedKey"
                }
              }
            }
          },
          "401": {
            "description": "Authorization header was present but is not a valid till_admin_ bearer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnauthorizedError"
                }
              }
            }
          },
          "402": {
            "description": "No Authorization header. Public signup is closed. Request Pro beta access or use an existing tenant's mint/checkout/claim path.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BetaAccessRequired"
                }
              }
            }
          },
          "400": {
            "description": "Invalid mint body, or the tenant has no provider connection (automatic keys require one)."
          }
        }
      }
    },
    "/admin/keys": {
      "post": {
        "operationId": "createScopedKey",
        "summary": "Create a scoped key for an existing tenant",
        "description": "Requires Authorization: Bearer till_admin_.... Only max_activations is required after at least one provider connection exists. Missing or invalid admin auth is HTTP 401, not 402.",
        "security": [
          {
            "tenantAdmin": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateScopedKey"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Scoped key created. The complete till_sk_<lookup>.<payload> token is returned once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatedKey"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid admin bearer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnauthorizedError"
                }
              }
            }
          }
        }
      }
    },
    "/api/checkout": {
      "post": {
        "operationId": "createExistingTenantCheckout",
        "summary": "Start paid checkout for an existing tenant",
        "description": "Authenticated existing-tenant upgrade. Uses configured Stripe Price IDs from the environment. Does not invent price IDs. Cannot create a new tenant without verified-email onboarding.",
        "security": [
          {
            "tenantAdmin": []
          }
        ]
      }
    },
    "/api/checkout-result": {
      "get": {
        "operationId": "pollCheckoutResult",
        "summary": "Poll checkout fulfillment JSON",
        "description": "Pollable JSON for agents. fulfilled is true only after the signed Stripe webhook completes the session. GET never fulfills. success_url is not fulfillment. Existing-tenant checkouts return ready_without_key and no new admin key.",
        "parameters": [
          {
            "name": "session_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pending or fulfilled checkout state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutResult"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "operationId": "mcpJsonRpc",
        "summary": "Hosted Streamable HTTP MCP",
        "description": "Unauthenticated initialize and discover work. mint_scoped_key without a till_admin_ bearer returns beta_access_required and does not mint a till_sk_. OAuth sign-in is not live."
      }
    },
    "/v1/chat/completions": {
      "post": {
        "operationId": "proxyChatCompletions",
        "summary": "Proxy an OpenAI-compatible chat completion",
        "description": "Requires Authorization: Bearer till_sk_<lookup>.<payload>. Till enforces the key ceiling, then forwards to a configured tenant provider connection.",
        "security": [
          {
            "scopedKey": []
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "tenantAdmin": {
        "type": "http",
        "scheme": "bearer",
        "description": "Tenant or super-admin key: till_admin_..."
      },
      "scopedKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Scoped Till token: till_sk_<lookup>.<payload>"
      }
    },
    "schemas": {
      "CreateScopedKey": {
        "type": "object",
        "required": ["max_activations"],
        "properties": {
          "max_activations": {
            "type": "integer",
            "minimum": 1
          },
          "max_tokens": {
            "type": "integer",
            "nullable": true
          },
          "max_spend_cents": {
            "type": "integer",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "CreatedKey": {
        "type": "object",
        "properties": {
          "key": {
            "type": "object",
            "properties": {
              "id": { "type": "string" },
              "scopedKey": { "type": "string" },
              "maxActivations": { "type": "integer" }
            }
          }
        }
      },
      "UnauthorizedError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": { "const": "unauthorized" },
              "message": { "type": "string" }
            }
          }
        }
      },
      "BetaAccessRequired": {
        "type": "object",
        "required": ["error", "beta", "existing_tenant"],
        "properties": {
          "error": { "const": "beta_access_required" },
          "message": { "type": "string" },
          "beta": {
            "type": "object",
            "required": ["request_url", "email"],
            "properties": {
              "request_url": { "type": "string" },
              "email": { "const": "support@till.ac" }
            }
          },
          "existing_tenant": {
            "type": "object",
            "required": ["mint", "checkout", "claim"],
            "properties": {
              "mint": { "type": "string" },
              "checkout": { "type": "string" },
              "claim": { "type": "string" }
            }
          }
        }
      },
      "CheckoutResult": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": ["pending", "ready", "ready_without_key", "already_revealed", "failed"]
          },
          "fulfilled": { "type": "boolean" },
          "pollable": { "const": true },
          "email": { "type": "string" },
          "plan": { "type": "string" },
          "tenantId": { "type": "string", "nullable": true },
          "adminKey": { "type": "string" },
          "message": { "type": "string" }
        }
      }
    }
  }
}
