The gateway is the single HTTP API for everything: auth, wallet & billing, the agent, chat, teams/RBAC, retrieval,
subscriptions, enterprise policy, and payment webhooks. The CLI, VS Code extension, and @koda-rising/sdk are all clients of it.
- Browsable docs (live):
GET /docs — an interactive Redoc reference, self-hosted (no CDN, CSP-safe). Open http://localhost:8080/docs.
- Machine-readable spec (live):
GET /openapi.json — OpenAPI 3.1 (covers the developer and operator/admin surface). Load it into anything:
- Swagger UI / Postman / Insomnia: point at the URL or import.
- Codegen:
npx @openapitools/openapi-generator-cli generate -i http://localhost:8080/openapi.json -g <lang>.
- This doc is the full human catalog (every route, incl. operator/admin/webhooks the served spec summarizes).
- Base URL: same-origin (
https://<host>). Dev: http://localhost:8080.
| Method |
How |
Use |
| Session cookie |
Sealed koda_session cookie from the /auth/* flows; send credentials: "include". |
Browsers / the dashboards. Never store a token in JS. |
| API key |
Authorization: Bearer <key> (create via POST /auth/keys). Keys carry scopes. |
CLI / CI / SDK / server-to-server. |
| Dev bearer |
Authorization: Bearer <userId> — only when KODA_DEV_AUTH=on (force-disabled in production). |
Local dev. |
Scopes (API keys): agent:run, wallet:read, keys:manage, metrics:read. A browser session carries * (a
signed-in human) — but * is not operator. Platform staff are users.is_platform_admin (grant:
node scripts/grant-operator.mjs <email>); cross-org reads and money-minting require it.
- Money is integer minor units of the org currency (NGN kobo, KES cent, …) — fields are named
*Minor (amountMinor, balanceMinor). A few convenience inputs take major units and say so (capNaira). No floats for minor fields.
- Idempotency: wallet-mutating calls (
topup/refund/agent runs) accept an Idempotency-Key header — exactly-once per user. Refunds require it.
- Errors: non-2xx is
{ "error": "message" } (some add fields, e.g. verificationRequired). Key codes: 401 unauth · 402 top-up required (wallet+allowance can’t cover) · 403 scope/role/operator · 404 not found or not yours · 429 rate-limited · 451 residency-blocked · 503 inference/webhook not configured.
- Rate limits: on by default, per client IP, four buckets (
auth/webhook/money/global). Over-limit ⇒ 429 + Retry-After + RateLimit-*. /health, /auth/config, /openapi.json, /app/* are exempt. Behind an LB, set TRUST_PROXY.
- Streaming:
/agent/run-loop and /chat/threads/:id/messages return text/event-stream (data: {json}\n\n frames).
- Operator scope: cross-org reads take
?scope=all or ?orgId=<other> and require operator.
| Method |
Path |
Auth |
Notes |
| GET |
/health |
none |
Liveness. |
| GET |
/readyz |
none |
DB-ready (503 if down). |
| GET |
/metrics |
token / operator |
Prometheus metrics (text format). Scrapers send Authorization: Bearer $KODA_METRICS_TOKEN; with no token set it is operator-only. |
| GET |
/openapi.json |
none |
This API’s OpenAPI 3.1 spec. |
| GET |
/ |
none |
Redirects to the app. |
| Method |
Path |
Auth |
Notes |
| GET |
/auth/config |
none |
Mode (native/dev/none) + enabled methods. |
| POST |
/auth/register |
none |
Email + password (gated by signups_enabled). |
| POST |
/auth/login |
none |
→ session, or an MFA challenge. |
| POST |
/auth/mfa/challenge |
none |
Complete TOTP / recovery-code challenge. |
| GET |
/auth/verify |
none |
Consume an email-verification token. |
| POST |
/auth/magic/request |
none |
Email a magic link (non-enumerating). |
| GET |
/auth/magic/consume |
none |
Consume a magic link → session. |
| POST |
/auth/password/forgot |
none |
Email a reset link. |
| POST |
/auth/password/reset |
none |
Consume a reset token. |
| POST |
/auth/mfa/enroll |
session |
Begin TOTP enrolment (returns provisioning data). |
| POST |
/auth/mfa/verify |
session |
Confirm TOTP + get recovery codes. |
| POST |
/auth/mfa/disable |
session |
Disable MFA. |
| POST |
/auth/logout |
session |
Revoke the current session. |
| POST |
/auth/dev-login |
none (dev only) |
Sign in by email without a password. |
| Method |
Path |
Auth |
Notes |
| GET |
/me |
any |
Principal, role, scopes, currency, wallet balance, plan + included-usage. |
| GET |
/me/usage |
wallet:read |
Own usage series + rollups. |
| GET |
/me/sessions |
session |
Active sessions. |
| DELETE |
/me/sessions/:id |
session |
Revoke a session. |
| POST |
/auth/keys |
keys:manage |
Create a key (returned once). |
| GET |
/auth/keys |
any |
List keys. |
| DELETE |
/auth/keys/:id |
any |
Revoke a key. |
| Method |
Path |
Auth |
Notes |
| GET |
/wallet |
any |
Balance. |
| GET |
/wallet/ledger |
any |
Ledger entries. |
| GET |
/wallet/settings |
owner/admin |
Low-balance threshold. |
| PATCH |
/wallet/settings |
owner |
Set the threshold. |
| POST |
/wallet/topup |
any (+ Idempotency-Key) |
Start a top-up on a currency-appropriate rail (card/USSD/OPay/PalmPay/M-Pesa/MoMo/Ozow/Fawry/…). Settles via the provider webhook. |
| POST |
/wallet/verify |
any |
Server-side verify a pending top-up (webhook fallback). |
| POST |
/wallet/refund |
operator (+ Idempotency-Key) |
Refund (cites a settled payment, capped) or adjustment (uncapped credit). |
| Method |
Path |
Auth |
Notes |
| POST |
/agent/run |
agent:run (+ Idempotency-Key) |
One turn → structured edits + cost. Open-default routing; 402/503 as applicable. |
| POST |
/agent/run-loop |
agent:run |
SSE. plan→edit→verify→retry, metered per iteration. Streams {delta} token frames + {step} per iteration, then a final result. maxIters is capped by plan tier (individual 3 / team 4 / enterprise 6; hard cap 8). Optional sandbox + testCmd (docker recommended); un-runnable sandbox ⇒ 400. |
| Method |
Path |
Auth |
Notes |
| POST |
/repo/index |
agent:run |
Embed + upsert files (metered as open-lane usage). 400 outside vector mode. |
| GET |
/repo/index |
any |
Status: {enabled, chunks, paths}. |
| DELETE |
/repo/index |
any |
Clear the org index, or one ?path. |
| Method |
Path |
Auth |
Notes |
| GET |
/chat/threads |
agent:run |
List threads. |
| POST |
/chat/threads |
agent:run |
Create (mode: general/code). |
| GET |
/chat/threads/:id |
agent:run |
Thread + messages. |
| PATCH |
/chat/threads/:id |
agent:run |
Rename / set mode / archive. |
| DELETE |
/chat/threads/:id |
agent:run |
Delete. |
| POST |
/chat/threads/:id/messages |
agent:run |
SSE. Stream {delta} frames then {message} (cost + balance). Unaffordable ⇒ 402 before any delta. |
| Method |
Path |
Auth |
Notes |
| GET |
/plans |
any |
Plans for the org currency. |
| GET |
/subscription |
any |
Current subscription + plan + included remaining. |
| POST |
/subscription/checkout |
owner |
Subscribe. Free/comped activate instantly; paid card plans return a hosted URL. |
| POST |
/subscription/change |
owner |
Change plan. Upgrade charges the prorated delta now; downgrade is scheduled for period end. Re-selecting the current plan clears a pending downgrade. |
| POST |
/subscription/seats |
owner |
Set the purchased seat count. Added seats charge the plan’s per-seat price, prorated; cannot drop below occupied seats. |
| POST |
/subscription/cancel |
owner |
Cancel at period end. |
| GET |
/payment-methods |
any |
List stored cards (never exposes the authorization code). |
| POST |
/payment-methods |
owner |
Add a card — returns a hosted verify-charge URL; the card is stored when its webhook settles. |
| POST |
/payment-methods/{id}/default |
owner |
Set the default card (charged first on renewals/upgrades). |
| DELETE |
/payment-methods/{id} |
owner |
Remove a stored card; promotes another to default. |
| GET |
/invoices |
any |
Invoice history (each carries a receiptUrl). |
| GET |
/invoices/{id}/receipt |
any |
Branded printable HTML receipt for one invoice (org-scoped). ?download=1 sends it as a file. |
| GET |
/billing/summary |
any |
Wallet + subscription + included-usage + seats + scheduled change. |
| Method |
Path |
Auth |
Notes |
| POST |
/teams |
any |
Create an org/team. |
| GET |
/teams/:orgId/members |
member |
List members. |
| POST |
/teams/:orgId/members |
owner/admin |
Invite/add (seat-capped; 402 if allotment exceeded). |
| PATCH |
/teams/:orgId/members/:userId |
owner/admin |
Change role / monthly cap. |
| DELETE |
/teams/:orgId/members/:userId |
owner/admin |
Remove. |
| GET |
/teams/:orgId/usage |
owner/admin |
Per-member usage. |
| Method |
Path |
Auth |
Notes |
| GET / PUT |
/orgs/:orgId/policy |
operator |
Tier/residency/isolation/lanes; PUT provisions schema isolation on the shared→isolated switch. |
| GET/POST/PATCH/DELETE |
/orgs/:orgId/sso |
operator |
OIDC/SAML connection CRUD. |
| POST |
/orgs/:orgId/sso/domains |
operator |
Verified email-domain routing. |
| GET |
/auth/sso/initiate |
none |
Begin an SSO sign-in. |
| GET |
/auth/sso/oidc/callback |
none |
OIDC callback. |
| POST |
/auth/sso/saml/acs |
none |
SAML assertion consumer. |
| GET |
/audit |
operator (?scope=all) |
Append-only audit log. |
| Method |
Path |
Notes |
| GET |
/metrics/overview · /metrics/usage · /metrics/timeseries · /metrics/kpis |
Fleet/org KPIs. mrrMinor is trailing recognised-usage revenue; netCreditedMinor is cash in (larger while wallets hold float). |
| GET |
/metrics/orgs |
Cross-org list — operator only. |
| Method |
Path |
Notes |
| GET/PUT |
/admin/flags · /admin/flags/:key |
Global feature flags. |
| GET |
/admin/posture |
Environment-gated features and whether each is live on this instance (any staff tier, read-only). |
| PUT/DELETE |
/orgs/:orgId/flags/:key |
Per-org flag override. |
| GET/POST/DELETE |
/admin/operators · /admin/operators/:userId |
Grant/revoke platform staff (last-operator guard). |
| GET |
/admin/orgs |
Fleet: plan, members, subscription, KYC, suspension. |
| POST |
/admin/orgs/:id/suspend · /unsuspend |
Suspend blocks the org’s paid actions (403). |
| GET |
/admin/kyc |
KYC submissions (?status=). |
| POST |
/admin/kyc/:orgId/review |
Verify/reject a submission. |
| POST |
/kyc |
Org self-submit (owner/admin), not operator. |
| GET/PUT |
/admin/settings · /admin/settings/:key |
Platform switches (signups_enabled, maintenance_mode, …). |
| Method |
Path |
Auth |
Notes |
| POST |
/internal/billing/run-renewals |
x-internal-secret (KODA_INTERNAL_SECRET) |
Charge due card subscriptions; the scheduled cron triggers it. No-op without a live Paystack key. |
| Method |
Path |
Trust |
| POST |
/webhooks/paystack |
HMAC-SHA512 signature + IP allowlist. 503 without a live signing key. |
| POST |
/webhooks/mpesa |
IP allowlist + secret token in the callback URL (unsigned rail). |
| POST |
/webhooks/momo |
IP allowlist + secret token (unsigned rail). |
Populate *_IP_ALLOWLIST and TRUST_PROXY before go-live (see the go-live runbook). OPay/PalmPay/Ozow/Fawry top-ups
settle via /wallet/verify + reconciliation rather than a dedicated webhook route.
Related: the SDK guide · the interactive reference at /docs · the machine-readable /openapi.json.