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.