#!/usr/bin/env python3 """Read-only live probes for the OpenAI admin/webhooks/errors domain. Every call is logged to reports/live-requests.jsonl by scripts.live; sanitized bodies go to tmp-live/openai-admin/. No destructive action. Only GET/LIST admin calls + deliberately-invalid requests (rejected before billing) + ONE tiny chat completion (max_tokens=5) to observe rate-limit headers. """ from __future__ import annotations import json, sys, time from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT)) from scripts import live # noqa: E402 OUT = ROOT / "tmp-live" / "openai-admin" OUT.mkdir(parents=True, exist_ok=True) now = int(time.time()) week_ago = now - 7 * 86400 summary = [] def probe(name, method, path, *, json_body=None, data=None, content_type="application/json", extra_headers=None, note="", est=0.0): st, body, hdrs = live.openai_request(method, path, json_body, data=data, content_type=content_type, extra_headers=extra_headers, note=note, est_cost_usd=est) if isinstance(body, bytes): try: body = body.decode() except Exception: # noqa: BLE001 body = f"<{len(body)} bytes>" rec = {"name": name, "method": method, "path": path, "status": st, "headers": live.interesting_headers(hdrs), "body": body} live.save_sanitized(rec, OUT / f"{name}.json") summary.append({k: rec[k] for k in ("name", "method", "path", "status")} | { "error": (body.get("error") if isinstance(body, dict) else None)}) err = body.get("error") if isinstance(body, dict) else None print(f"{st:>3} {method:6} {path[:70]:70} {json.dumps(err)[:160] if err else ''}") return st, body, hdrs # ---- Admin API (expect 403 Missing scopes with a project key) ---- for name, path in [ ("admin_users", "/v1/organization/users?limit=1"), ("admin_invites", "/v1/organization/invites?limit=1"), ("admin_projects", "/v1/organization/projects?limit=1"), ("admin_audit_logs", "/v1/organization/audit_logs?limit=1"), ("admin_admin_api_keys", "/v1/organization/admin_api_keys?limit=1"), ("admin_usage_completions", f"/v1/organization/usage/completions?start_time={week_ago}&limit=1"), ("admin_costs", f"/v1/organization/costs?start_time={week_ago}&limit=1"), ("admin_certificates", "/v1/organization/certificates?limit=1"), ("admin_roles", "/v1/organization/roles?limit=1"), ("admin_groups", "/v1/organization/groups?limit=1"), ("admin_spend_limit", "/v1/organization/spend_limit"), ("admin_data_retention", "/v1/organization/data_retention"), ("admin_external_storage", "/v1/organization/external_storage"), ("admin_spend_alerts", "/v1/organization/spend_alerts"), ]: probe(name, "GET", path, note="admin probe with project key") # ---- Webhooks (project-scoped; may work with a normal key) ---- probe("webhook_endpoints_list", "GET", "/v1/webhook_endpoints?limit=1", note="webhook endpoints list") probe("webhook_event_types", "GET", "/v1/webhook_event_types", note="webhook event types") # ---- Deliberate error shapes (all rejected before generation => free) ---- probe("err_model_not_found", "POST", "/v1/responses", json_body={"model": "does-not-exist-model", "input": "Reply with OK.", "max_output_tokens": 16}, note="404 model_not_found") probe("err_invalid_type", "POST", "/v1/responses", json_body={"model": "gpt-5.4-nano", "input": "Reply with OK.", "max_output_tokens": "sixteen"}, note="400 invalid type") probe("err_missing_param", "POST", "/v1/responses", json_body={"input": "Reply with OK."}, note="400 missing model") probe("err_unknown_param", "POST", "/v1/responses", json_body={"model": "gpt-5.4-nano", "input": "Reply with OK.", "max_output_tokens": 16, "definitely_not_a_param": 1}, note="400 unknown parameter") probe("err_invalid_json", "POST", "/v1/responses", data=b'{"model": "gpt-5.4-nano", "input": ', note="400 invalid JSON") probe("err_invalid_api_key", "GET", "/v1/models", extra_headers={"Authorization": "Bearer sk-invalid"}, note="401 invalid_api_key (bogus literal)") probe("err_no_auth", "GET", "/v1/models", extra_headers={"Authorization": ""}, note="401 missing auth") probe("err_response_not_found", "GET", "/v1/responses/resp_000000000000000000000000000000", note="404 response id") probe("err_invalid_url", "GET", "/v1/this_endpoint_does_not_exist", note="404 invalid URL") probe("err_context_length", "POST", "/v1/chat/completions", json_body={"model": "gpt-4.1-nano", "messages": [{"role": "user", "content": "Reply with OK."}], "max_tokens": 100000000}, note="400 max_tokens too large") probe("err_bad_content_type", "POST", "/v1/responses", data=b"model=gpt-5.4-nano", content_type="text/plain", note="400/415 content type") probe("err_bad_org_header", "GET", "/v1/models", extra_headers={"OpenAI-Organization": "org-doesnotexist0000"}, note="401 bad org header") probe("err_bad_project_header", "GET", "/v1/models", extra_headers={"OpenAI-Project": "proj_doesnotexist0000"}, note="bad project header") probe("err_wrong_method", "PATCH", "/v1/models", note="405? wrong method") probe("err_bad_client_request_id", "GET", "/v1/models", extra_headers={"X-Client-Request-Id": "é" * 10}, note="400 non-ascii X-Client-Request-Id") # ---- One tiny paid call to observe headers (x-ratelimit-*, x-request-id, openai-*) ---- probe("ok_chat_headers", "POST", "/v1/chat/completions", json_body={"model": "gpt-4.1-nano", "messages": [{"role": "user", "content": "Reply with OK."}], "max_tokens": 5}, extra_headers={"X-Client-Request-Id": "atlas-openai-admin-headers-probe-0001"}, note="header observation", est=0.00001) probe("ok_models_headers", "GET", "/v1/models/gpt-4.1-nano", note="header observation GET") live.save_sanitized(summary, OUT / "summary.json") print("saved", OUT)