{
  "openapi": "3.0.3",
  "info": {
    "title": "CircaOS API",
    "description": "Schema-locked, tier-routed inference substrate with cryptographic response signing. The `/v1/chat/completions` request and response shape is API-compatible with the widely-adopted format so existing client SDKs work without modification. Byte-identical outputs at temperature 0. Hash-chained per-(tenant, app) audit. Substrate-resolved inference — no third-party hosted LLM is in the prod request path.",
    "version": "0.1.0",
    "contact": { "email": "support@5ceos.com", "url": "https://cogos.5ceos.com" },
    "license": { "name": "Proprietary (API)", "url": "https://cogos.5ceos.com/terms" }
  },
  "servers": [
    { "url": "https://cogos.5ceos.com", "description": "Production" }
  ],
  "tags": [
    { "name": "completions", "description": "Schema-locked chat completions in the widely-adopted /v1/chat/completions format" },
    { "name": "models", "description": "Available models" },
    { "name": "audit", "description": "Hash-chained audit receipts per (tenant, app)" },
    { "name": "signup", "description": "Free-tier key issuance" },
    { "name": "identity", "description": "Caller identity + package introspection" },
    { "name": "grounded", "description": "Search-augmented chat with unified receipt — answer + citations bound together" },
    { "name": "processes", "description": "Deterministic processes (iolta-reconcile, 5law-conflict-check, etc.) — same input → same bytes out" },
    { "name": "search", "description": "Receipt-bearing live-web search" },
    { "name": "compose", "description": "Multi-step deterministic workflow composition" },
    { "name": "state", "description": "Per-key stateful substrate (matters, parties); writes metered, reads free" }
  ],
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sk-cogos-...",
        "description": "API key issued at /signup/free. Format: `sk-cogos-` followed by 32 hex chars."
      }
    },
    "schemas": {
      "ChatCompletionRequest": {
        "type": "object",
        "required": ["model", "messages"],
        "properties": {
          "model": {
            "type": "string",
            "enum": ["cogos-tier-a", "cogos-tier-b"],
            "description": "Tier A = narrative workloads; Tier B = classification, scoring, extraction. The router prefers B for schema-constrained or short-decode tasks."
          },
          "messages": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ChatMessage" }
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "default": 0,
            "description": "Recommend 0 for byte-identical reproducibility. Higher values defeat the determinism guarantee."
          },
          "max_tokens": { "type": "integer", "minimum": 1 },
          "response_format": {
            "type": "object",
            "description": "Schema-locked decoding. With `strict: true`, the decoder physically cannot emit a token that violates the grammar.",
            "properties": {
              "type": { "type": "string", "enum": ["text", "json_object", "json_schema"] },
              "strict": { "type": "boolean", "default": false },
              "schema": { "type": "object", "description": "JSON Schema object when type=json_schema" }
            }
          },
          "stream": { "type": "boolean", "default": false },
          "tools": {
            "type": "array",
            "description": "OpenAI-compatible tool/function definitions. Forwarded verbatim to the upstream model.",
            "items": {
              "type": "object",
              "properties": {
                "type": { "type": "string", "enum": ["function"] },
                "function": {
                  "type": "object",
                  "properties": {
                    "name": { "type": "string" },
                    "description": { "type": "string" },
                    "parameters": { "type": "object", "description": "JSON Schema object describing function parameters" }
                  }
                }
              }
            }
          },
          "tool_choice": {
            "description": "Same semantics as OpenAI: 'none' | 'auto' | 'required' | { type:'function', function:{name} }",
            "oneOf": [
              { "type": "string", "enum": ["none", "auto", "required"] },
              { "type": "object" }
            ]
          },
          "seed": { "type": "integer", "description": "Forwarded to upstream for deterministic sampling when supported." }
        }
      },
      "ChatMessage": {
        "type": "object",
        "required": ["role"],
        "properties": {
          "role": { "type": "string", "enum": ["system", "user", "assistant", "tool"] },
          "content": { "type": "string" },
          "tool_calls": {
            "type": "array",
            "description": "Present on assistant messages when the model decided to call a tool. Pass these back in a follow-up message with role:'tool' and matching tool_call_id to complete the loop.",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "type": { "type": "string", "enum": ["function"] },
                "function": {
                  "type": "object",
                  "properties": {
                    "name": { "type": "string" },
                    "arguments": { "type": "string", "description": "JSON-encoded arguments string" }
                  }
                }
              }
            }
          },
          "tool_call_id": { "type": "string", "description": "Required on role:'tool' messages — the id from the assistant's tool_calls[]." },
          "name": { "type": "string", "description": "Optional sender name (function name for role:'tool')" }
        }
      },
      "ChatCompletionResponse": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "object": { "type": "string", "enum": ["chat.completion"] },
          "created": { "type": "integer", "description": "Unix epoch seconds" },
          "model": { "type": "string", "description": "Tier alias the request resolved to (e.g. `cogos-tier-b`)." },
          "choices": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": { "type": "integer" },
                "message": { "$ref": "#/components/schemas/ChatMessage" },
                "finish_reason": { "type": "string", "enum": ["stop", "length", "content_filter"] }
              }
            }
          },
          "usage": {
            "type": "object",
            "properties": {
              "prompt_tokens": { "type": "integer" },
              "completion_tokens": { "type": "integer" },
              "total_tokens": { "type": "integer" }
            }
          },
          "cogos": {
            "type": "object",
            "description": "CircaOS-specific metadata block (extension to the standard chat-completion response). Includes schema enforcement flag, server-side latency, and the request_id you can use to look up the audit receipt.",
            "properties": {
              "schema_enforced": { "type": "boolean" },
              "latency_ms": { "type": "integer" },
              "request_id": { "type": "string" }
            }
          }
        }
      },
      "Model": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "object": { "type": "string", "enum": ["model"] },
          "created": { "type": "integer" },
          "owned_by": { "type": "string", "example": "cogos" }
        }
      },
      "AuditChainHead": {
        "type": "object",
        "description": "Current head of the hash-chained audit log for the requesting (tenant, app). Sign this and archive it to prove later that the chain was not rewritten.",
        "properties": {
          "tenant_id": { "type": "string" },
          "head_hash": { "type": "string", "description": "Hex-encoded SHA-256 of the latest receipt + its predecessor hash" },
          "height": { "type": "integer", "description": "Number of receipts in this chain" },
          "as_of": { "type": "string", "format": "date-time" },
          "signature": { "type": "string", "description": "Ed25519 signature over (tenant_id || head_hash || height || as_of)" },
          "attestation_kid": { "type": "string", "description": "Key ID for the attestation public key; fetch the PEM at /attestation.pub" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": { "type": "string" },
              "message": { "type": "string" },
              "code": { "type": "string" }
            }
          }
        }
      }
    }
  },
  "security": [{ "ApiKey": [] }],
  "paths": {
    "/v1/chat/completions": {
      "post": {
        "tags": ["completions"],
        "summary": "Create a chat completion (schema-locked, signed response)",
        "description": "Request/response shape is the widely-adopted `/v1/chat/completions` format, so existing client SDKs work unmodified. Every response is HMAC-signed; verify the `X-Cogos-Signature` header against your HMAC secret. At `temperature: 0` with `response_format.strict: true`, outputs are byte-identical across N identical-input calls.",
        "operationId": "createChatCompletion",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatCompletionRequest" } } }
        },
        "responses": {
          "200": {
            "description": "Completion returned. Verify `X-Cogos-Signature` before trusting `choices[].message.content`.",
            "headers": {
              "X-Cogos-Signature": { "schema": { "type": "string" }, "description": "HMAC-SHA256 over the response body, using your HMAC secret." },
              "X-Cogos-Attestation": { "schema": { "type": "string" }, "description": "Ed25519 signature over the response body, verifiable with /attestation.pub" },
              "X-Request-Id": { "schema": { "type": "string" }, "description": "Maps to the audit-receipt id; cite this when contacting support." }
            },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatCompletionResponse" } } }
          },
          "401": { "description": "Missing or invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "429": { "description": "Daily cap exceeded (free tier: 100 req/day)", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
        }
      }
    },
    "/v1/models": {
      "get": {
        "tags": ["models"],
        "summary": "List available models",
        "operationId": "listModels",
        "responses": {
          "200": {
            "description": "Models list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": { "type": "string", "enum": ["list"] },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Model" } }
                  }
                }
              }
            }
          },
          "401": { "description": "Missing or invalid API key" }
        }
      }
    },
    "/signup/free": {
      "post": {
        "tags": ["signup"],
        "summary": "Issue a free-tier API key (no email required)",
        "description": "Returns an HTML page with the new `sk-cogos-...` key and the matching HMAC secret. The key is shown once. Idempotent on email: if the supplied email already has a key, no second key is minted.",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": { "email": { "type": "string", "format": "email", "description": "Optional. Used for support contact and dedup." } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Either the new-key page or an already-exists notice.",
            "content": { "text/html": { "schema": { "type": "string" } } }
          },
          "429": { "description": "Per-IP signup rate limited" }
        }
      }
    },
    "/health": {
      "get": {
        "tags": ["audit"],
        "summary": "Liveness check",
        "security": [],
        "responses": {
          "200": {
            "description": "Service alive",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string", "enum": ["ok"] },
                    "service": { "type": "string" },
                    "version": { "type": "string" },
                    "uptime_s": { "type": "integer" },
                    "timestamp": { "type": "string", "format": "date-time" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": ["identity"],
        "summary": "Identity probe — caller's tenant, key, package, quota",
        "description": "Returns the requesting key's identity + the resolved package's monthly_request_quota, allowed_model_tiers, and current billing-cycle start. Used by CLI / SDK quota commands to render usage-vs-cap without a second call.",
        "operationId": "getIdentity",
        "responses": {
          "200": {
            "description": "Identity payload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "tenant_id": { "type": "string" },
                    "tenant_type": { "type": "string", "enum": ["subscriber", "operator"] },
                    "key_id": { "type": "string" },
                    "app_id": { "type": "string" },
                    "tier": { "type": "string" },
                    "scheme": { "type": "string", "enum": ["bearer", "ed25519"] },
                    "active": { "type": "boolean" },
                    "monthly_request_quota": { "type": "integer", "nullable": true },
                    "allowed_model_tiers": { "type": "array", "items": { "type": "string" } },
                    "package_id": { "type": "string", "nullable": true },
                    "cycle_start_ms": { "type": "integer", "nullable": true }
                  }
                }
              }
            }
          },
          "401": { "description": "Missing or invalid API key" }
        }
      }
    },
    "/v1/audit": {
      "get": {
        "tags": ["audit"],
        "summary": "Query the hash-chained audit log slice for this tenant",
        "description": "Returns the requesting tenant's chained usage rows since a unix-ms timestamp. Strictly tenant-scoped — customer A can never see customer B's rows. chain_ok reflects server-side verifyChain() on the returned slice; customers re-run verification locally for independent assurance.",
        "operationId": "queryAudit",
        "parameters": [
          { "name": "since", "in": "query", "schema": { "type": "integer", "default": 0 }, "description": "Unix-ms; rows with ts >= since are returned." },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 100, "maximum": 1000 } },
          { "name": "app_id", "in": "query", "schema": { "type": "string" }, "description": "Optional — scope the slice to a single app's chain." }
        ],
        "responses": {
          "200": {
            "description": "Audit slice + chain integrity flag",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rows": { "type": "array", "items": { "type": "object" } },
                    "chain_ok": { "type": "boolean" },
                    "chain_break": { "type": "object", "nullable": true }
                  }
                }
              }
            }
          },
          "400": { "description": "Invalid `since` or `limit`" },
          "401": { "description": "Missing or invalid API key" }
        }
      }
    },
    "/v1/chat-grounded": {
      "post": {
        "tags": ["grounded"],
        "summary": "Search-augmented chat — answer + citations bound by one receipt",
        "description": "Composes /v1/search + sovereign LLM on the server side so the customer makes ONE call and the output_hash binds answer + citations together as one indivisible artifact. Search trigger is heuristic in mode='auto' (keywords: today, current, latest, news, price of, who won, etc.); override with mode='always-search' or 'never-search'. ONE billable usage row per call regardless of internal sub-operations.",
        "operationId": "chatGrounded",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["query"],
                "properties": {
                  "query": { "type": "string" },
                  "mode": { "type": "string", "enum": ["auto", "always-search", "never-search"], "default": "auto" },
                  "model": { "type": "string", "default": "cogos-tier-b" },
                  "temperature": { "type": "number", "default": 0 },
                  "max_tokens": { "type": "integer", "default": 500 },
                  "max_results": { "type": "integer", "default": 5, "maximum": 10 }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Answer + citations + receipt with evidence_chain",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "answer": { "type": "string" },
                    "citations": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "url": { "type": "string" }, "snippet": { "type": "string" } } } },
                    "search_used": { "type": "boolean" },
                    "search_skip_reason": { "type": "string" },
                    "model": { "type": "string" },
                    "usage": { "type": "object", "properties": { "prompt_tokens": { "type": "integer" }, "completion_tokens": { "type": "integer" } } },
                    "receipt": {
                      "type": "object",
                      "properties": {
                        "request_id": { "type": "string" },
                        "ms": { "type": "integer" },
                        "deterministic_hash": { "type": "string" },
                        "output_hash": { "type": "string" },
                        "evidence_chain": {
                          "type": "object",
                          "properties": {
                            "search_request_id": { "type": "string" },
                            "search_provider": { "type": "string" },
                            "search_output_hash": { "type": "string" },
                            "search_ms": { "type": "integer" },
                            "chat_ms": { "type": "integer" }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": { "description": "Missing or invalid query" },
          "401": { "description": "Missing or invalid API key" },
          "429": { "description": "Daily cap or monthly quota exceeded" },
          "502": { "description": "Upstream LLM call failed" }
        }
      }
    },
    "/v1/process": {
      "get": {
        "tags": ["processes"],
        "summary": "List available deterministic processes",
        "description": "Catalog endpoint — machine-readable list of processes this gateway exposes. No auth required for discovery; invocations are auth-gated.",
        "operationId": "listProcesses",
        "security": [],
        "responses": {
          "200": {
            "description": "Process catalog",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "processes": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "model_id": { "type": "string" }, "description": { "type": "string" } } } }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/process/{slug}": {
      "post": {
        "tags": ["processes"],
        "summary": "Invoke a deterministic process — same input → same bytes out",
        "description": "Each process is a pure function over its input; output_hash in the receipt proves bit-stable output for this (input, engine_version). v0.1 ships iolta-reconcile and 5law-conflict-check.",
        "operationId": "invokeProcess",
        "parameters": [
          { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Process id from /v1/process catalog (e.g. `iolta-reconcile`)" }
        ],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "description": "Process-specific input shape — see catalog for each process's schema." } } } },
        "responses": {
          "200": {
            "description": "Process output + receipt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "receipt": {
                      "type": "object",
                      "properties": {
                        "request_id": { "type": "string" },
                        "ms": { "type": "integer" },
                        "deterministic_hash": { "type": "string" },
                        "output_hash": { "type": "string" }
                      }
                    }
                  },
                  "additionalProperties": true
                }
              }
            }
          },
          "400": { "description": "Invalid input shape" },
          "401": { "description": "Missing or invalid API key" },
          "429": { "description": "Daily cap or monthly quota exceeded" },
          "500": { "description": "Engine failure" }
        }
      }
    },
    "/v1/search": {
      "post": {
        "tags": ["search"],
        "summary": "Receipt-bearing live-web search",
        "description": "Live web search via Brave (or provider:'none' when BRAVE_SEARCH_API_KEY is unset — receipt still emitted). Same receipt-chain semantics as /v1/process; the output_hash proves what was returned at this moment (not determinism — search is non-deterministic by nature).",
        "operationId": "search",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["query"],
                "properties": {
                  "query": { "type": "string" },
                  "max_results": { "type": "integer", "default": 5, "maximum": 10 }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search results + receipt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": { "type": "string" },
                    "provider": { "type": "string", "description": "'brave-v1' or 'none' when no API key configured" },
                    "results": { "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string" }, "url": { "type": "string" }, "snippet": { "type": "string" } } } },
                    "receipt": { "type": "object" }
                  }
                }
              }
            }
          },
          "401": { "description": "Missing or invalid API key" },
          "429": { "description": "Daily cap or monthly quota exceeded" }
        }
      }
    },
    "/v1/compose": {
      "post": {
        "tags": ["compose"],
        "summary": "Multi-step deterministic workflow composition",
        "description": "Linear-sequence composition of registered processes with end-to-end chain hashing. One billable usage row per composition (not per step).",
        "operationId": "compose",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "steps": { "type": "array", "items": { "type": "object" } } } } } } },
        "responses": {
          "200": { "description": "Composed output + receipt" },
          "400": { "description": "Invalid step shape" },
          "401": { "description": "Missing or invalid API key" },
          "429": { "description": "Daily cap or monthly quota exceeded" }
        }
      }
    },
    "/v1/state/matters": {
      "post": {
        "tags": ["state"],
        "summary": "Upsert one or many matter records (per-key stateful substrate)",
        "description": "Writes one chain-row per call. Reads on /v1/state/* GET endpoints are free (not metered).",
        "operationId": "stateMatterUpsert",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object" } } } },
        "responses": {
          "200": { "description": "Write succeeded + receipt" },
          "401": { "description": "Missing or invalid API key" },
          "429": { "description": "Daily cap or monthly quota exceeded" }
        }
      }
    }
  }
}
