API Tiers & Documentation Visibility

Axiom Exchange publishes progressively more documentation the deeper you go, rather than putting the entire reference behind a login. The moat is the service, the data, the models, and the contractual access — not whether a developer can read what GET /plans returns.

The four things kept separate

Authentication alone never determines what a request can do. Four concerns, evaluated in this order for every request:

Identity  →  Role  →  OAuth Scope  →  API Gateway  →  Usage Metering  →  API

A caller can be fully authenticated and still lack the scope to reach a given endpoint — see AUTH_ARCHITECTURE.md.

Classification matrix

API family Documentation API access Real endpoints today
Product/catalog Public Free/key GET /utilities, GET /plans
Tariffs/pricing Public Key/metered GET /plans, GET /rates/{utility_id}/history
Address/serviceability Public Metered GET /utilities/lookup, POST /geo/utilities/status
Forecasting Public description Metered GET /usage/{id}/forecast
Customer/account Authenticated docs OAuth /resellers, /customers, /cost-keys, /assets, /geo/*
Customer enrollment Authenticated docs OAuth /enrollments
Meter/usage Authenticated docs OAuth GET /usage/{id}
Trading/order execution Private docs Contract + OAuth /exchange/transactions
Settlement Private docs Contract (scope reserved: settlement:* — no dedicated endpoint yet)
DER/device control Private docs Partner OAuth (scope reserved: der:* — no dedicated endpoint yet; der:read currently gates nothing)
Grid operations Private docs Contract (scope reserved: grid:* — not yet implemented)
Market data Public summary Metered (scope reserved: market:* — belongs to the Axiom Market/ETRM module, not Exchange)
Internal orchestration Never public Internal only database schema, algorithm internals, infrastructure config

Rows marked "reserved" are real scopes in the code today (see app/core/scopes.py) with no route wired to them yet — declared now, deliberately, so that when DER, grid, settlement, and market endpoints ship, they slot into an existing authorization model instead of triggering a second wave of retrofitting.

OpenAPI document split (target)

/openapi/public.json      → catalog, tariffs, serviceability, forecast description
/openapi/developer.json   → public + sandbox-only operations
/openapi/customer.json    → developer + accounts, enrollment, meter/usage
/openapi/partner.json     → customer + DER read, advanced market data
/openapi/trading.json     → partner + trading, settlement
/internal/openapi.json    → administration, orchestration, infrastructure (never public)

Current state: /openapi.json (and Swagger UI at /docs) is now gated behind a real portal login (standard username/password, Twilio- Verify-ready 2FA — see the Developer Portal) rather than served openly; every operation's security block is correctly populated with its real required scope (see main.py's custom_openapi()). That single gate is coarser than the tiered split above — it's "logged in or not," not "which tier does this operation belong to" — so splitting into the actual /openapi/<tier>.json documents, each independently gated by role, remains the next concrete step, tracked in TESTING.md.

Never document publicly

Regardless of tier: internal orchestration logic, pricing/forecasting model internals, database schemas, infrastructure/deployment configuration, and algorithm implementations. docs/DEPLOYMENT.md has already moved off the public static docs site and is served only through the login-gated GET /portal/docs/deployment endpoint — the same treatment docs/AUTH_ARCHITECTURE.md received. docs/DATA_MODEL.md remains public for now; it describes schema shape and rationale for engineering onboarding, not deployment secrets or infrastructure topology, which is the distinction worth re-checking if it grows to include either.