Python 88.3%
TypeScript 7.6%
Shell 4.1%
1#!/usr/bin/env python32"""Render docs/endpoints/index.md and docs/endpoints/by-status.md from generated/endpoints.json3for the four providers (openai, anthropic, xai, gemini). Re-runnable; no manual edits in the outputs."""4import json, collections5ROOT = "/Users/simon-pierreboucher/Desktop/doc-api"6TODAY = "2026-09-18"7E = json.load(open(f"{ROOT}/generated/endpoints.json"))8PROVIDERS = ["openai", "anthropic", "xai", "gemini"]9PROV_LABEL = {"openai": "OpenAI", "anthropic": "Anthropic", "xai": "xAI", "gemini": "Gemini"}1011def esc(s):12 return str(s).replace("|", "\\|").replace("\n", " ")1314def auth_short(a):15 if a is None: return "—"16 if isinstance(a, dict):17 scheme = a.get("scheme") or ""18 kt = a.get("key_type") or ""19 hdr = a.get("header") or ""20 bits = [b for b in [scheme, kt] if b]21 s = " / ".join(bits) or hdr or json.dumps(a)[:60]22 if a.get("scopes"): s += f" (scopes: {', '.join(a['scopes'][:2])}{'…' if len(a['scopes'])>2 else ''})"23 return s[:110]24 a = str(a)25 return a if len(a) <= 110 else a[:107] + "…"2627def beta_short(b):28 if not b: return "—"29 b = str(b)30 return b if len(b) <= 60 else b[:57] + "…"3132def verif(r):33 v = r.get("verification") or {}34 m = v.get("method") or "—"35 res = v.get("result")36 hs = v.get("http_status")37 if m == "docs_only" and not res:38 return "docs only"39 if m == "—" and not res and hs in (None, 0):40 return "—"41 s = m42 if res: s += f" · {res}"43 if hs not in (None, 0): s += f" · HTTP {hs}"44 return s4546def bucket(r):47 st = set(r["status"])48 if "RETIRED" in st: return "RETIRED"49 if "DEPRECATED" in st: return "DEPRECATED"50 if "LEGACY" in st: return "LEGACY"51 if "FAILED_VERIFICATION" in st: return "FAILED_VERIFICATION"52 if "ACCOUNT_RESTRICTED" in st: return "ACCOUNT_RESTRICTED"53 if "LIVE_VERIFIED" in st: return "LIVE_VERIFIED"54 if "LIVE_DISCOVERED" in st: return "LIVE_DISCOVERED"55 if "BETA" in st or "PREVIEW" in st: return "BETA_OR_PREVIEW_DOCUMENTED_ONLY"56 if "UNVERIFIED" in st: return "UNVERIFIED"57 return "DOCUMENTED_ONLY"5859fam_order = collections.OrderedDict()60for prov in PROVIDERS:61 fam_order[prov] = sorted({r["api_family"] for r in E if r["provider"] == prov})6263status_counter = collections.Counter(s for r in E for s in r["status"])64per_prov = {p: sum(1 for r in E if r["provider"] == p) for p in PROVIDERS}6566L = []67L.append("# Endpoint catalogue — OpenAI + Anthropic + xAI + Gemini (rendered from `generated/endpoints.json`)\n")68L.append(f"**Status:** generated from `generated/endpoints.json` ({len(E)} endpoint records: " + ", ".join(f"{PROV_LABEL[p]} {per_prov[p]}" for p in PROVIDERS) + "). Each row carries the statuses recorded by the domain agents; “Verification” is the record's `verification` block (method · result · HTTP). This page is the human twin of `generated/endpoints.csv`; a status-grouped view is in [by-status.md](by-status.md).")69L.append("**Sources:** per record `sources[]` in `generated/endpoints.json` (OpenAI reference https://developers.openai.com/api/reference/… and OpenAPI `sources/openai/openapi/openapi-master.yaml`; Anthropic reference https://platform.claude.com/docs/en/api/…; xAI reference https://docs.x.ai/developers/rest-api-reference/… and OpenAPI `sources/xai/openapi/openapi.json`; Gemini reference https://ai.google.dev/api/… and discovery documents `sources/gemini/openapi/discovery-v1beta.json` / `discovery-v1.json`).")70L.append(f"**Last verified:** {TODAY}\n")71L.append("## Legend\n")72L.append("| Column | Meaning |\n|---|---|")73L.append("| Status | atlas vocabulary: `DOCUMENTED` in current docs · `LIVE_VERIFIED` called successfully with our key · `LIVE_DISCOVERED` seen live, weak docs · `BETA`/`PREVIEW` gated or pre-GA · `GA` (Gemini fragments only: explicitly marked generally available) · `LEGACY` maintained but superseded · `DEPRECATED` retirement announced · `RETIRED` gone · `ACCOUNT_RESTRICTED` documented, our key got 401/403/404-for-access (xAI: Management API key we do not have, ACL-gated models; Gemini: paid-tier-only models on a free-tier key) · `UNVERIFIED` not tried · `FAILED_VERIFICATION` tried, unexpected failure · `DOCUMENTATION_INCOMPLETE` reference lacks a shape |")74L.append("| Auth | credential accepted: OpenAI Bearer project/Admin key; Anthropic `x-api-key` or Bearer, Admin/Compliance keys, cloud IAM; xAI `Authorization: Bearer xai-…` (inference key) or a **Management key** for `management-api.x.ai`; Gemini `x-goog-api-key` (never in the URL) or OAuth Bearer, `Authorization: Bearer <key>` on `/v1beta/openai/*` |")75L.append("| Beta header | header value required to reach the surface (`OpenAI-Beta: …` or `anthropic-beta: …`); `—` = none. xAI has **no** version or beta headers; Gemini gates features by **URL version** (`/v1beta` vs `/v1`) and `-preview` model ids, not by header |")76L.append("| Verification | `live_api · success · HTTP 200` = exercised; `docs_only` = not called in this run; `restricted` = 401/403 (or access-404) with our key; `failure` = call made, unexpected status (see record `request_note`); `not_tested` = Gemini/xAI records deliberately skipped (cost, destructive, needs a peer) |")77L.append("\n### Status totals (a record may carry several statuses)\n")78L.append("| Status | Total | " + " | ".join(PROV_LABEL[p] for p in PROVIDERS) + " |\n|---|---|" + "---|" * len(PROVIDERS))79for s, n in status_counter.most_common():80 L.append(f"| `{s}` | {n} | " + " | ".join(str(sum(1 for r in E if r['provider']==p and s in r['status'])) for p in PROVIDERS) + " |")8182L.append("\n## Counts per provider → api_family\n")83L.append("| Provider | api_family | Endpoints | LIVE_VERIFIED | ACCOUNT_RESTRICTED | BETA / PREVIEW | DEPRECATED/RETIRED |\n|---|---|---|---|---|---|---|")84for prov, fams in fam_order.items():85 for f in fams:86 rs = [r for r in E if r["provider"] == prov and r["api_family"] == f]87 L.append(f"| {prov} | `{f}` | {len(rs)} | {sum('LIVE_VERIFIED' in r['status'] for r in rs)} | {sum('ACCOUNT_RESTRICTED' in r['status'] for r in rs)} | {sum(('BETA' in r['status']) or ('PREVIEW' in r['status']) for r in rs)} | {sum(('DEPRECATED' in r['status']) or ('RETIRED' in r['status']) for r in rs)} |")88 L.append(f"| **{prov} total** | {len(fams)} families | **{per_prov[prov]}** | {sum(1 for r in E if r['provider']==prov and 'LIVE_VERIFIED' in r['status'])} | {sum(1 for r in E if r['provider']==prov and 'ACCOUNT_RESTRICTED' in r['status'])} | {sum(1 for r in E if r['provider']==prov and (('BETA' in r['status']) or ('PREVIEW' in r['status'])))} | {sum(1 for r in E if r['provider']==prov and (('DEPRECATED' in r['status']) or ('RETIRED' in r['status'])))} |")89L.append(f"| **all providers** | {sum(len(f) for f in fam_order.values())} families | **{len(E)}** | {sum('LIVE_VERIFIED' in r['status'] for r in E)} | {sum('ACCOUNT_RESTRICTED' in r['status'] for r in E)} | {sum(('BETA' in r['status']) or ('PREVIEW' in r['status']) for r in E)} | {sum(('DEPRECATED' in r['status']) or ('RETIRED' in r['status']) for r in E)} |")9091L.append("\n### Reading the four surfaces\n")92L.append("| Provider | Base URL(s) | Path style | Families worth knowing |\n|---|---|---|---|")93L.append("| openai | `https://api.openai.com/v1` (+ regional `us.|eu.|au.|jp.|in.api.openai.com`, `api.chatgpt.com` for workspace agents) | REST nouns (`/v1/responses`, `/v1/agents/sessions`), WebSocket `wss://api.openai.com/v1/{responses,realtime}` | `responses`, `chat_completions`, `agents-platform/*`, `realtime`, `live`, `admin` (124) |")94L.append("| anthropic | `https://api.anthropic.com/v1` (+ Bedrock, Vertex, Foundry, Claude Platform on AWS hosts under `cloud:*`) | REST nouns (`/v1/messages`, `/v1/sessions`), beta surfaces gated by `anthropic-beta` | `messages`, `messages-batches`, `managed-agents` (96), `admin` (100), `compliance` (36) |")95L.append("| xai | `https://api.x.ai/v1` (global), `https://us.api.x.ai/v1` (US-pinned, grok-4.6, 1.1× price), `https://eu-west-1.api.x.ai/v1` (LIVE_DISCOVERED), `https://management-api.x.ai` (separate key), gRPC `api.x.ai:443` | OpenAI-shaped REST (`/v1/responses`, `/v1/chat/completions`, `/v1/batches`) + Anthropic-shaped `/v1/messages`; WebSocket `wss://api.x.ai/v1/{realtime,stt,tts}` | `responses`, `chat_completions` (LEGACY), `collections`, `files`, `voice` (17), `management` (21, ACCOUNT_RESTRICTED) |")96L.append("| gemini | `https://generativelanguage.googleapis.com/v1beta` (also `/v1` stable subset, `/upload/v1beta/*` resumable uploads, `/v1beta/openai/*` compatibility layer), WebSocket `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent` | Google RPC-style verbs on resources (`/v1beta/models/{model}:generateContent`, `:streamGenerateContent`, `:countTokens`, `:embedContent`, `:predictLongRunning`), resource collections (`/v1beta/cachedContents`, `/v1beta/fileSearchStores`, `/v1beta/interactions`, `/v1beta/batches`) | `generate-content`, `interactions`, `live`, `file-search` (12), `tuning` (12, RETIRED/LEGACY), `legacy-palm` (7), `openai-compat` (7) |")9798def method_rank(m):99 return {"GET":0,"POST":1,"PATCH":2,"PUT":3,"DELETE":4,"WS":5,"WSS":6,"SIP":7}.get(m,8)100101for prov, fams in fam_order.items():102 L.append(f"\n# {PROV_LABEL[prov]} endpoints ({per_prov[prov]})\n")103 for f in fams:104 rs = [r for r in E if r["provider"] == prov and r["api_family"] == f]105 rs.sort(key=lambda r: (r["path"].split("?")[0], method_rank(r["method"])))106 L.append(f"\n## {prov} · `{f}` ({len(rs)})\n")107 L.append("| Method | Path | Name | Status | Auth | Beta header | Verification |")108 L.append("|---|---|---|---|---|---|---|")109 for r in rs:110 L.append(f"| `{esc(r['method'])}` | `{esc(r['path'])}` | {esc(r['name'])} | {' · '.join('`'+s+'`' for s in r['status'])} | {esc(auth_short(r.get('auth')))} | {esc(beta_short(r.get('beta_header')))} | {esc(verif(r))} |")111112L.append("\n---\nRelated: [by-status view](by-status.md) · [feature matrix](../comparisons/features.md) · `generated/endpoints.csv` · `generated/parameters.json` (per-endpoint parameters, key = `endpoint` field `METHOD /path`).\n")113open(f"{ROOT}/docs/endpoints/index.md", "w").write("\n".join(L))114115# ---------------- by-status116order = ["LIVE_VERIFIED","LIVE_DISCOVERED","ACCOUNT_RESTRICTED","BETA_OR_PREVIEW_DOCUMENTED_ONLY","DOCUMENTED_ONLY","UNVERIFIED","LEGACY","DEPRECATED","RETIRED","FAILED_VERIFICATION"]117titles = {118 "LIVE_VERIFIED": "LIVE_VERIFIED — called successfully with this atlas's keys",119 "LIVE_DISCOVERED": "LIVE_DISCOVERED — observed live (e.g. 404/405 probe proves the route) with weak/absent docs",120 "ACCOUNT_RESTRICTED": "ACCOUNT_RESTRICTED — documented; our key received 401/403 or an access-denial (needs Admin/Compliance key, WIF token, xAI Management key or ACL, Gemini paid tier, or gated access)",121 "BETA_OR_PREVIEW_DOCUMENTED_ONLY": "BETA / PREVIEW — documented behind a beta header, a `/v1beta` path or a preview program, not called in this run",122 "DOCUMENTED_ONLY": "DOCUMENTED only — in current docs, not called (mostly mutations or sub-resources)",123 "UNVERIFIED": "UNVERIFIED — documented, deliberately not tried (cost, needs browser/SIP peer, destructive)",124 "LEGACY": "LEGACY — still served, superseded",125 "DEPRECATED": "DEPRECATED — retirement announced (see docs/openai/deprecations.md, docs/anthropic/deprecations.md, docs/xai/deprecations-and-release-notes.md, docs/gemini/deprecations-and-changelog.md)",126 "RETIRED": "RETIRED — gone (404/400/501) or redirected to a replacement",127 "FAILED_VERIFICATION": "FAILED_VERIFICATION — tried; unexpected failure (reason in `verification.request_note`)",128}129B = []130B.append("# Endpoints grouped by status — four providers\n")131B.append(f"**Status:** generated from `generated/endpoints.json` ({len(E)} records: " + ", ".join(f"{PROV_LABEL[p]} {per_prov[p]}" for p in PROVIDERS) + "). Each endpoint is placed in exactly one bucket by precedence RETIRED > DEPRECATED > LEGACY > FAILED_VERIFICATION > ACCOUNT_RESTRICTED > LIVE_VERIFIED > LIVE_DISCOVERED > BETA/PREVIEW > UNVERIFIED > DOCUMENTED-only; the full status list is shown in the row. Full catalogue with auth/beta columns: [index.md](index.md).")132B.append(f"**Sources:** as `generated/endpoints.json` · **Last verified:** {TODAY}\n")133buckets = collections.defaultdict(list)134for r in E: buckets[bucket(r)].append(r)135B.append("| Bucket | " + " | ".join(PROV_LABEL[p] for p in PROVIDERS) + " | Total |\n|---|" + "---|" * len(PROVIDERS) + "---|")136for b in order:137 rs = buckets.get(b, [])138 B.append(f"| {b} | " + " | ".join(str(sum(r['provider']==p for r in rs)) for p in PROVIDERS) + f" | {len(rs)} |")139B.append(f"| **Total** | " + " | ".join(str(per_prov[p]) for p in PROVIDERS) + f" | **{len(E)}** |")140B.append("\nHow to read the provider columns: the OpenAI and Anthropic catalogues include large Admin/Compliance families that a project key cannot call (hence many `ACCOUNT_RESTRICTED`); xAI's `management` family (21) needs a Management key this atlas does not hold; Gemini's `agents`, `credentials`, `environments`, `triggers`, `webhooks` families are the managed-agents preview (documented, mostly `not_tested`), and its `tuning` / `legacy-palm` families are retired or legacy surfaces still present in the discovery document.\n")141for b in order:142 rs = buckets.get(b, [])143 if not rs: continue144 B.append(f"\n## {titles[b]} ({len(rs)})\n")145 B.append("| Provider | api_family | Method | Path | Status | Verification / note |")146 B.append("|---|---|---|---|---|---|")147 rs.sort(key=lambda r: (PROVIDERS.index(r["provider"]), r["api_family"], r["path"], method_rank(r["method"])))148 for r in rs:149 v = r.get("verification") or {}150 note = v.get("request_note") or ""151 vs = verif(r)152 if b in ("FAILED_VERIFICATION","LIVE_DISCOVERED","ACCOUNT_RESTRICTED","RETIRED") and note:153 vs += " — " + (note if len(note) <= 140 else note[:137] + "…")154 B.append(f"| {r['provider']} | `{esc(r['api_family'])}` | `{esc(r['method'])}` | `{esc(r['path'])}` | {' · '.join('`'+s+'`' for s in r['status'])} | {esc(vs)} |")155B.append("\n---\nRelated: [full catalogue](index.md) · [feature matrix](../comparisons/features.md) · [FAQ](../faq.md).\n")156open(f"{ROOT}/docs/endpoints/by-status.md", "w").write("\n".join(B))157print("index rows", len(E), {p: per_prov[p] for p in PROVIDERS}, {b: len(v) for b, v in buckets.items()})158