Kong Gateway Integration
Enable the Aforo metering plugin on your existing Kong cluster. Zero application code changes required.
Task Overview#
Prerequisites#
Step 1: Get the Plugin#
The Kong plugin lives in the Aforo SDK distribution repo at aforo-gateway-plugins/kong/. It is not yet on the public LuaRocks registry, so build and install it from source. Clone the repo, then make the plugin from the rockspec in that folder:
Then load it in kong.conf and reserve the buffer the log phase writes to — plugins = bundled,aforo-metering and nginx_http_lua_shared_dict = aforo_buffer 10m — then run kong reload. The shared-dict line is mandatory; without it the log phase drops every event.
Step 2: Configure#
Add the plugin to your kong.yml declarative config or apply via Admin API:
Option A: Declarative (kong.yml) — Production Ready
flush_count 50, flush_interval_ms 5000, metric_name_pattern {method} {path}. Customer identity comes only from the Kong consumer (or a validated JWT claim), never a client-settable header.X-Request-Id, or Kong's own X-Kong-Request-Id) — for WebSocket, the handshake key — so a batch the plugin re-sends after a failed flush is deduplicated by Aforo. HTTP, MCP, gRPC and GraphQL events also carry an executionStatus taken from the upstream status — see How Calls Get an Outcome.Option B: Admin API
service.id or route.id to the request.How Calls Get an Outcome#
Read this if any rate plan behind this gateway uses Outcome-Based pricing — it bills each call at a weight for its executionStatus. Every other pricing model ignores the field. The plugin sets it from the upstream response status:
| Upstream status | executionStatus |
|---|---|
| 2xx, 3xx | SUCCESS |
| 408, 504 | TIMEOUT |
| 499 (client closed the request, where the gateway reports it) | CANCELLED |
| 400, 422 | VALIDATION_FAILED |
| 401, 403, 429 | BLOCKED |
| Every other 4xx and 5xx (404 included) | ERROR |
| Missing, 0, 1xx | Not sent — the call bills at full weight |
BLOCKED and CANCELLED at 0, but plans saved before those two statuses were added to the wizard (September 30, 2026) have no weight for them, so the gateway's BLOCKED and CANCELLED calls on those plans bill in full until you give them one — open the plan in the wizard and enter a weight (the fields show blank), or set it through the rate-plan API (outcomeWeights). See Outcome-Based pricing.Override the status for specific codes
When the default table is wrong for your API — say a 404 is a normal answer, not a failure — map exact codes with the status_outcomes plugin config field, a map of status code to status:
Keys are exact HTTP codes from 200 to 599. Values must be one of the 11 statuses: SUCCESS, PARTIAL, TIMEOUT, ERROR, VALIDATION_FAILED, FAILED, FAILURE, CANCELLED, PENDING, BLOCKED, HITL_REQUIRED. Kong’s config schema rejects an invalid entry when you apply the plugin. If a code is listed twice, the last valid entry wins. An override can't give a status to a missing, 0 or 1xx code.
Requests that aren't metered
By default, responses with status 401, 403 or 429 and requests to /health, /ready or /metrics produce no event at all — they cost nothing whatever the rate plan says. All five gateway plugins share this default. A path is skipped when it equals an entry or starts with it, so /health also covers /health/live.
| Setting | Default | To meter everything |
|---|---|---|
Plugin config exclude_status_codes (array) | [401, 403, 429] | An empty array, [] |
Plugin config exclude_paths (array) | ["/health", "/ready", "/metrics"] | An empty array, [] |
Kong's config schema rejects an entry of the wrong type when you apply the plugin. A list you set replaces the default rather than adding to it: a status list of 404,500 skips only those two and starts metering 401, 403 and 429. A status you choose to meter bills as BLOCKED unless you override it above.
gRPC calls
gRPC returns HTTP 200 for most failures, so with grpc_enabled: true the plugin reads grpc-status instead — from the response header first, then the upstream trailer. Each code maps to an equivalent HTTP status, and that equivalent goes through the table, status_outcomes and exclude_status_codes above. By default that means codes 7, 8 and 16 produce no event, exactly like a 403, 429 or 401.
| grpc-status | Treated as | Default executionStatus |
|---|---|---|
| 0 OK | 200 | SUCCESS |
| 1 CANCELLED | 499 | CANCELLED |
| 3 INVALID_ARGUMENT, 9 FAILED_PRECONDITION, 11 OUT_OF_RANGE | 400 | VALIDATION_FAILED |
| 4 DEADLINE_EXCEEDED | 504 | TIMEOUT |
| 16 UNAUTHENTICATED | 401 | BLOCKED — not metered by default |
| 7 PERMISSION_DENIED | 403 | BLOCKED — not metered by default |
| 8 RESOURCE_EXHAUSTED | 429 | BLOCKED — not metered by default |
| 5 NOT_FOUND | 404 | ERROR |
| 6 ALREADY_EXISTS, 10 ABORTED | 409 | ERROR |
| 12 UNIMPLEMENTED | 501 | ERROR |
| 14 UNAVAILABLE | 503 | ERROR |
| 2 UNKNOWN, 13 INTERNAL, 15 DATA_LOSS, any code above 16 | 500 | ERROR |
| Not readable | The HTTP status | Per the HTTP table above |
A gRPC-Web upstream that puts the status in the response body can't be read at the gateway; those calls fall back to the HTTP status. The Apigee, AWS, Azure and MuleSoft plugins don't meter gRPC.
Step 3: Deploy#
After configuration, reload Kong to activate the plugin:
Performance: Zero Latency Impact#
The Aforo Kong plugin is designed for zero impact on your API response times:
log phase for metering and access phase for entitlement + margin checks. L1-L3 enforcement runs before the request reaches your upstream — unprofitable traffic is blocked at the gateway.Fallback Behavior#
If the Aforo ingestion endpoint is unreachable, the plugin keeps your API traffic flowing and retries the batch. A batch that still fails is dropped and counted (aforo_metering_dropped_events_total), never retried forever:
aforo_buffer shared dict and flushed every 5s to api.aforo.ailog phase, after the response is sent — so a metering failure never blocks or delays an API request. Entitlement/margin checks in the access phase fail open (traffic continues) if Aforo can't be reached.Verification#
Verify the handshake by checking Kong logs for a successful connection:
PaperPlaneTilt a Test Event
[aforo-metering] ERROR entries. Common issues: invalid API key (401), network timeout to api.aforo.ai, or missing X-Tenant-Id header on the request.