api-gateway (Kong) — API Contracts
Status: populated Owner: TBD (Platform / SRE) Last updated: 2026-04-17 Companion: SERVICE_OVERVIEW · ADR-0001 · Service Template
1. Purpose
Catalogue the external API surface Kong exposes and the Kong Admin API used by SRE/tooling. Endpoint-level request/response schemas belong to upstream services and are referenced here, not duplicated.
2. External (proxy) surface
Base URL (prod): https://api.ghasi.io (behind Cloudflare → Kong).
Base URL (staging): https://api.staging.ghasi.io.
TLS 1.2+ (1.3 preferred). HTTP/2 upstream to Kong; HTTP/1.1 upstream from Kong to internal services.
2.1 Route prefix → upstream service
| Path prefix | Methods | Upstream | Auth | Notes |
|---|---|---|---|---|
/v1/sms/send | POST | sms-orchestrator | JWT or X-Api-Key | Size limit 64 KB; per-key + per-account rate limits |
/v1/sms/bulk | POST | sms-orchestrator | JWT or X-Api-Key | Size limit 2 MB; stricter rate limits |
/v1/sms/{id} | GET | sms-orchestrator | JWT or X-Api-Key | Read; rate-limit fail-open |
/v1/dlr/* | GET, POST | dlr-processor | JWT | Internal/integrator-facing DLR feeds |
/v1/auth/login | POST | auth-service | public | Strict IP/bot rate limits to resist credential stuffing |
/v1/auth/refresh | POST | auth-service | JWT (refresh) | — |
/v1/auth/logout | POST | auth-service | JWT | — |
/v1/accounts/* | GET, POST, PATCH | auth-service | JWT | Scope: accounts:* |
/v1/api-keys/* | GET, POST, DELETE | auth-service | JWT | Scope: api-keys:* |
/v1/billing/* | GET | billing-service | JWT | Scope: billing:read |
/v1/invoices/* | GET | billing-service | JWT | Scope: invoices:read |
/v1/analytics/* | GET | analytics-service | JWT | Scope: analytics:read |
/v1/reports/* | GET, POST | analytics-service | JWT | Scope: reports:* |
/v1/operators/* | GET, POST, PATCH | operator-management-service | JWT + role:admin | IP-restricted |
/v1/webhooks/test | POST | webhook-dispatcher | JWT or X-Api-Key | Size limit 16 KB |
/v1/webhooks/* (mgmt) | GET, POST, PATCH, DELETE | webhook-dispatcher | JWT | — |
/admin/* | GET, POST, PATCH, DELETE | admin-dashboard BFF | JWT + role:admin | IP allow-list, strict CSP at BFF |
/portal/* | GET, POST, PATCH, DELETE | customer-portal BFF | JWT | — |
/health | GET | Kong (static 200) | public | Liveness |
/ready | GET | Kong (status plugin) | public | Readiness |
Detailed request/response shapes live in each upstream service's API_CONTRACTS.md.
2.2 Required headers (client → Kong)
| Header | When | Notes |
|---|---|---|
Authorization: Bearer <jwt> | JWT routes | Signed by auth-service, validated via JWKS |
X-Api-Key: <key> | key-auth routes | Customer API key; opaque string |
Idempotency-Key | POST /v1/sms/send, /v1/sms/bulk, /v1/webhooks/test | UUID/ULID, client-chosen; forwarded verbatim to upstream |
X-Tenant-Id | optional | Overridden from JWT tid claim if present |
Accept-Language | optional | Forwarded |
traceparent | optional | W3C; Kong propagates |
2.3 Headers Kong adds upstream
| Header | Source |
|---|---|
X-Account-Id | jwt sub/tid claim, or ghasi-api-key-lookup resolution |
X-Api-Key-Id | ghasi-api-key-lookup resolution (key-auth path) |
X-Tier | Same |
X-Request-Id | correlation-id plugin (if absent) |
X-Gateway-Source: kong | request-transformer |
X-Forwarded-For | Kong built-in |
2.4 Headers Kong strips before upstream
| Header | Reason |
|---|---|
X-Api-Key | Do not leak credential past the edge |
X-Powered-By | Fingerprint reduction |
Server | Fingerprint reduction (on responses) |
3. Error responses (edge)
Edge errors use problem+json per docs/standards/ERROR_CODES.md:
| Status | type | Cause |
|---|---|---|
| 400 | https://errors.ghasi.io/gateway/bad-request | Malformed request |
| 401 | https://errors.ghasi.io/gateway/unauthorized | JWT invalid/expired or API key invalid |
| 403 | https://errors.ghasi.io/gateway/forbidden | IP restriction, bot detection |
| 404 | https://errors.ghasi.io/gateway/route-not-found | No matching Kong Route |
| 413 | https://errors.ghasi.io/gateway/payload-too-large | request-size-limiting |
| 429 | https://errors.ghasi.io/gateway/rate-limited | rate-limiting-advanced |
| 502 | https://errors.ghasi.io/gateway/bad-upstream | Upstream returned non-HTTP or malformed response |
| 503 | https://errors.ghasi.io/gateway/upstream-unavailable | Upstream health check failed |
| 504 | https://errors.ghasi.io/gateway/upstream-timeout | Upstream connect/read timeout |
Upstream-originated errors (2xx–5xx) pass through with upstream bodies unchanged.
4. Kong Admin API (internal only)
The Kong Admin API is never exposed publicly. It is accessible only from the internal network / SRE jump hosts.
| Scope | Network | Auth | Use |
|---|---|---|---|
| Read | SRE VPN / cluster-local | mTLS + admin token | Inspect routes/plugins/consumers |
| Write | CI runner only | mTLS + admin token | Applied by decK in CI; humans do not write directly |
Key Admin paths (not exhaustive):
| Path | Method | Purpose |
|---|---|---|
/status | GET | Data plane health |
/services, /routes, /plugins, /consumers | CRUD | Configuration |
/cache | DELETE | Flush rate-limit or JWKS cache (guarded operation) |
Admin API is not reachable from Cloudflare. A NetworkPolicy restricts it to pods with label: role=sre-tooling.
5. OpenAPI
Kong does not generate its own OpenAPI. The platform-facing OpenAPI is assembled from each upstream service's OpenAPI, plus route metadata from decK. The CI pipeline (see TESTING_STRATEGY §2) lints Kong route config against upstream OpenAPI — every Kong Route must correspond to a documented upstream path or CI fails.
6. Per-route configuration highlights
| Route | request-size-limiting | rate-limiting-advanced | Notes |
|---|---|---|---|
POST /v1/sms/send | 64 KB | 10/s per-key, 300/min per-account | Fail-closed on rate-limit |
POST /v1/sms/bulk | 2 MB | 2/s per-key, 30/min per-account | Fail-closed |
GET /v1/sms/{id} | n/a | 50/s per-account | Fail-open on Redis down |
POST /v1/auth/login | 8 KB | 5/min per-IP | Anti-credential-stuffing |
GET /v1/analytics/* | n/a | 30/s per-account | Fail-open |
POST /v1/webhooks/test | 16 KB | 10/min per-account | — |
/admin/* | 1 MB | 60/min per-account + IP allow | — |
Defaults above are starting points; the authoritative values live in ops/kong/ decK YAML and auth-service tier definitions.
7. Versioning
- Route prefixes are versioned (
/v1/...). Breaking changes to any upstream service's API create a new prefix (/v2/...). - Kong config itself is versioned with Git; every prod deploy tags the decK YAML commit.
8. Open questions
- Do we surface a platform-wide OpenAPI at
/openapi.jsonvia Kong, or keep per-service OpenAPI? -
/admin/*hostname split (e.g.admin.ghasi.io) vs path-based onapi.ghasi.io.