Skip to content

API Reference

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.
Section titled “Auth (native: password · magic-link · TOTP MFA)”
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.

Repo index (org-scoped retrieval; vector mode)

Section titled “Repo index (org-scoped retrieval; vector mode)”
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.

Admin / platform (operator, except POST /kyc)

Section titled “Admin / platform (operator, except POST /kyc)”
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.

Webhooks (provider callbacks — not for direct client use)

Section titled “Webhooks (provider callbacks — not for direct client use)”
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.