Skip to main content

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 prefixMethodsUpstreamAuthNotes
/v1/sms/sendPOSTsms-orchestratorJWT or X-Api-KeySize limit 64 KB; per-key + per-account rate limits
/v1/sms/bulkPOSTsms-orchestratorJWT or X-Api-KeySize limit 2 MB; stricter rate limits
/v1/sms/{id}GETsms-orchestratorJWT or X-Api-KeyRead; rate-limit fail-open
/v1/dlr/*GET, POSTdlr-processorJWTInternal/integrator-facing DLR feeds
/v1/auth/loginPOSTauth-servicepublicStrict IP/bot rate limits to resist credential stuffing
/v1/auth/refreshPOSTauth-serviceJWT (refresh)
/v1/auth/logoutPOSTauth-serviceJWT
/v1/accounts/*GET, POST, PATCHauth-serviceJWTScope: accounts:*
/v1/api-keys/*GET, POST, DELETEauth-serviceJWTScope: api-keys:*
/v1/billing/*GETbilling-serviceJWTScope: billing:read
/v1/invoices/*GETbilling-serviceJWTScope: invoices:read
/v1/analytics/*GETanalytics-serviceJWTScope: analytics:read
/v1/reports/*GET, POSTanalytics-serviceJWTScope: reports:*
/v1/operators/*GET, POST, PATCHoperator-management-serviceJWT + role:adminIP-restricted
/v1/webhooks/testPOSTwebhook-dispatcherJWT or X-Api-KeySize limit 16 KB
/v1/webhooks/* (mgmt)GET, POST, PATCH, DELETEwebhook-dispatcherJWT
/admin/*GET, POST, PATCH, DELETEadmin-dashboard BFFJWT + role:adminIP allow-list, strict CSP at BFF
/portal/*GET, POST, PATCH, DELETEcustomer-portal BFFJWT
/healthGETKong (static 200)publicLiveness
/readyGETKong (status plugin)publicReadiness

Detailed request/response shapes live in each upstream service's API_CONTRACTS.md.

2.2 Required headers (client → Kong)

HeaderWhenNotes
Authorization: Bearer <jwt>JWT routesSigned by auth-service, validated via JWKS
X-Api-Key: <key>key-auth routesCustomer API key; opaque string
Idempotency-KeyPOST /v1/sms/send, /v1/sms/bulk, /v1/webhooks/testUUID/ULID, client-chosen; forwarded verbatim to upstream
X-Tenant-IdoptionalOverridden from JWT tid claim if present
Accept-LanguageoptionalForwarded
traceparentoptionalW3C; Kong propagates

2.3 Headers Kong adds upstream

HeaderSource
X-Account-Idjwt sub/tid claim, or ghasi-api-key-lookup resolution
X-Api-Key-Idghasi-api-key-lookup resolution (key-auth path)
X-TierSame
X-Request-Idcorrelation-id plugin (if absent)
X-Gateway-Source: kongrequest-transformer
X-Forwarded-ForKong built-in

2.4 Headers Kong strips before upstream

HeaderReason
X-Api-KeyDo not leak credential past the edge
X-Powered-ByFingerprint reduction
ServerFingerprint reduction (on responses)

3. Error responses (edge)

Edge errors use problem+json per docs/standards/ERROR_CODES.md:

StatustypeCause
400https://errors.ghasi.io/gateway/bad-requestMalformed request
401https://errors.ghasi.io/gateway/unauthorizedJWT invalid/expired or API key invalid
403https://errors.ghasi.io/gateway/forbiddenIP restriction, bot detection
404https://errors.ghasi.io/gateway/route-not-foundNo matching Kong Route
413https://errors.ghasi.io/gateway/payload-too-largerequest-size-limiting
429https://errors.ghasi.io/gateway/rate-limitedrate-limiting-advanced
502https://errors.ghasi.io/gateway/bad-upstreamUpstream returned non-HTTP or malformed response
503https://errors.ghasi.io/gateway/upstream-unavailableUpstream health check failed
504https://errors.ghasi.io/gateway/upstream-timeoutUpstream 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.

ScopeNetworkAuthUse
ReadSRE VPN / cluster-localmTLS + admin tokenInspect routes/plugins/consumers
WriteCI runner onlymTLS + admin tokenApplied by decK in CI; humans do not write directly

Key Admin paths (not exhaustive):

PathMethodPurpose
/statusGETData plane health
/services, /routes, /plugins, /consumersCRUDConfiguration
/cacheDELETEFlush 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

Routerequest-size-limitingrate-limiting-advancedNotes
POST /v1/sms/send64 KB10/s per-key, 300/min per-accountFail-closed on rate-limit
POST /v1/sms/bulk2 MB2/s per-key, 30/min per-accountFail-closed
GET /v1/sms/{id}n/a50/s per-accountFail-open on Redis down
POST /v1/auth/login8 KB5/min per-IPAnti-credential-stuffing
GET /v1/analytics/*n/a30/s per-accountFail-open
POST /v1/webhooks/test16 KB10/min per-account
/admin/*1 MB60/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.json via Kong, or keep per-service OpenAPI?
  • /admin/* hostname split (e.g. admin.ghasi.io) vs path-based on api.ghasi.io.