{"components":{"securitySchemes":{"projectToken":{"description":"A project-scoped token (cspt_…). Returned once by POST /v1/register; rotate via the token endpoints.","in":"header","name":"x-awacs-token","type":"apiKey"},"serviceAccount":{"description":"A scoped service-account key (cssa_…) for machine/reporting clients, provisioned by an operator via AWACS_SERVICE_ACCOUNTS and bound to a role (e.g. `reporting` = projects.view). Not a developer/project token. See docs/guides/reporting-service-accounts.md.","scheme":"bearer","type":"http"}}},"info":{"description":"Add your app to the A.W.A.C.S AI firewall with the API alone. Register to claim a project + token, then point your LLM client's base URL at `/v1/gateway/{project}/{provider}` and route traffic through the firewall.\n\n**Two independent credentials, both sent on every gateway call:**\n- `x-awacs-token` (required) — your project token (`cspt_…`). Authenticates the A.W.A.C.S **project**. There is no `Authorization` fallback on the gateway route: without this header you get 401.\n- Your normal **upstream provider key** in its usual header (`api-key` for Azure OpenAI; `Authorization: Bearer …` for OpenAI/Anthropic; an Azure AD bearer also works for Azure). Relayed to the provider untouched and **never stored**.\n\nBuild the gateway path with **real `/` and a real `?query`** — do not URL-encode it. **Azure OpenAI:** the first path segment is the **bare resource name** (e.g. `cog-myres`), not the full `*.openai.azure.com` host — A.W.A.C.S adds the domain itself, and the full host returns 400.","title":"A.W.A.C.S Developer API","version":"1"},"openapi":"3.1.0","paths":{"/v1/gateway/{project}/{provider}/{path}":{"post":{"description":"Point your LLM client's base URL at `/v1/gateway/{project}/{provider}`. A.W.A.C.S inspects the prompt (allow / flag / block), relays to the real provider, inspects the response (streaming-aware, with a mid-stream kill), meters cost, and records a verdict. Works for OpenAI/Azure **chat completions**, the **Responses API** (`input`/`output`/streaming, inspected + metered), **embeddings**, and **Anthropic messages** — send the provider's normal request body, unchanged.\n\n**Auth (two headers):** `x-awacs-token` authenticates your A.W.A.C.S project (required; no `Authorization` fallback → 401 without it). Your upstream provider key rides in its usual header (`api-key` for Azure; `Authorization: Bearer` for OpenAI/Anthropic) and is relayed untouched, never stored.\n\n**Path rules:** use real `/` and a real `?query`; do not URL-encode. For **azure-openai** the first segment is the **bare resource name** (e.g. `cog-myres`) — passing the full `cog-myres.openai.azure.com` host returns **400 unroutable**.","parameters":[{"description":"Your project id (from POST /v1/register).","in":"path","name":"project","required":true,"schema":{"type":"string"}},{"description":"Upstream provider. `compatible` is an operator-configured self-hosted / OpenAI-compatible endpoint.","in":"path","name":"provider","required":true,"schema":{"enum":["openai","azure-openai","anthropic","compatible"],"type":"string"}},{"description":"The provider's own path, with real slashes (not URL-encoded). Examples — openai: `v1/chat/completions` · azure-openai chat: `{bare-resource}/openai/deployments/{deployment}/chat/completions?api-version=2024-10-21` · azure-openai Responses: `{bare-resource}/openai/responses?api-version=2025-03-01-preview` · anthropic: `v1/messages`. For azure-openai `{bare-resource}` is the resource name only (e.g. `cog-myres`), never `cog-myres.openai.azure.com`.","in":"path","name":"path","required":true,"schema":{"type":"string"}},{"description":"Azure OpenAI: your resource key — the upstream provider key, relayed untouched and never stored. (OpenAI/Anthropic send the key in `Authorization: Bearer` instead; Azure also accepts an Azure AD bearer there.)","in":"header","name":"api-key","required":false,"schema":{"type":"string"}},{"description":"Optional label naming which app/component made the call (e.g. `sow-app`). Tags the traffic in the live feed, firewall metrics, and audit log; default `gateway`. Stripped before relay (never reaches the provider); does not affect auth, routing, or cost.","in":"header","name":"x-awacs-agent","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"The provider's own request body, unchanged (OpenAI chat `{\"messages\":[…]}`, Responses `{\"input\":[…]}`, Anthropic `{\"messages\":[…],\"system\":…}`, embeddings `{\"input\":…}`). Relayed as-is."},"responses":{"200":{"description":"Relayed provider response (verdict allow or flag). Streaming requests come back as pass-through SSE."},"400":{"description":"Unroutable gateway path (e.g. azure-openai resource given as a full host instead of the bare name, or a URL-encoded path)."},"401":{"description":"Missing or invalid x-awacs-token (the project is not authenticated)."},"403":{"description":"Blocked: firewall verdict (prompt or response), the deny-by-default sensitive-data gate, the cost circuit-breaker, or a non-approved model. Provider-shaped error body."}},"security":[{"projectToken":[]}],"summary":"Route an LLM call through the firewall","tags":["gateway"]}},"/v1/projects/{id}/posture":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Posture report (open findings, authorization, last scan)"}},"security":[{"projectToken":[]}],"summary":"Read your project's posture / gap report","tags":["project"]}},"/v1/projects/{id}/self":{"get":{"description":"The app's registration metadata: name, description, purpose, status, capabilities, gateway base, and a reduced budget state. Never exposes admin-owned config (controls, credentials, sign-offs, budget limits).","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Your app's record"}},"security":[{"projectToken":[]}],"summary":"Read your app's own record (filtered)","tags":["project"]},"patch":{"description":"Self-service edit of name, description, and purpose, nothing else. Unknown fields are rejected (422). Changes are audited with before/after values, attributed to the acting token.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"purpose":{"description":"Empty string clears it","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"description":"Updated record (name/description/purpose)"},"422":{"description":"Unknown / non-self-editable field in body"}},"security":[{"projectToken":[]}],"summary":"Edit your app's metadata (allowlisted)","tags":["project"]}},"/v1/projects/{id}/stats":{"get":{"description":"Live firewall rollup (15-minute window: requests, violations, verdict mix, p95 latency, per-model/category breakdowns, recent decisions) plus cost: rolling-30-day spend, per-model spend, calendar-month history, and a reduced budget state (ok/warn/over + blocking). Poll-friendly: does not stamp token last_used_at.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Combined stats"}},"security":[{"projectToken":[]}],"summary":"Read your app's stats (firewall + cost) in one call","tags":["project"]}},"/v1/projects/{id}/tokens":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Token metadata (never the secret)"}},"security":[{"projectToken":[]}],"summary":"List your project's tokens (metadata only)","tags":["tokens"]},"post":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"expires_at":{"description":"RFC3339; empty = no expiry","type":"string"},"name":{"type":"string"}},"type":"object"}}}},"responses":{"201":{"description":"New token; plaintext shown once"}},"security":[{"projectToken":[]}],"summary":"Mint a new token (self-service rotation)","tags":["tokens"]}},"/v1/projects/{id}/tokens/{token_id}":{"delete":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"path","name":"token_id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Revoked"}},"security":[{"projectToken":[]}],"summary":"Revoke a token","tags":["tokens"]}},"/v1/register":{"post":{"description":"First call. Claim a unique key (your app URL, preferred, or a name); A.W.A.C.S derives a project id, creates the project, and returns ONE token (shown once). Re-registering a claimed key returns 409; recover via the token endpoints or admin.","requestBody":{"content":{"application/json":{"schema":{"properties":{"contact_email":{"description":"Owner contact (optional)","type":"string"},"description":{"description":"Free-form description (optional)","type":"string"},"key":{"description":"Your app URL (preferred, enables future domain-verified recovery) or a project name","example":"https://myapp.example.com","type":"string"},"name":{"description":"Display name (optional)","type":"string"},"purpose":{"description":"What the app is for: its AI use case (optional; editable later via PATCH /v1/projects/{id}/self)","type":"string"}},"required":["key"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"docs":{"type":"string"},"gateway_base":{"type":"string"},"portal":{"type":"string"},"project_id":{"type":"string"},"token":{"type":"string"}},"type":"object"}}},"description":"Registered; token shown once"},"409":{"description":"Key already registered (anti-squat)"}},"summary":"Register your app: claim a project + get a token","tags":["onboarding"]}},"/v1/reports/ai-apps":{"get":{"description":"Aggregate report for a reporting client: every project with purpose, environment, app_group, status, capabilities, 30-day + calendar-month spend and tokens, top models, the 15-minute firewall verdict mix, and reduced budget state. Owner emails (?include=contacts, PII) and scan posture (?include=posture) are opt-in. This is the machine/reporting surface: it uses a scoped service-account key (cssa_…) with projects.view, NOT a developer project token. Provision one via AWACS_SERVICE_ACCOUNTS; see docs/guides/reporting-service-accounts.md.","parameters":[{"description":"Comma-separated extras: `contacts` (owner_email, PII), `posture` (scan findings, file-I/O heavy).","in":"query","name":"include","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"All apps with stats, grouped by app_group"},"403":{"description":"Credential lacks projects.view"}},"security":[{"serviceAccount":[]}],"summary":"All registered AI apps + their stats in one call (machine reporting)","tags":["reporting"]}}},"servers":[{"url":"https://dev-awacs.longviewsystems.com"}]}