Meter your product's usage and send it to Aforo for billing — in code with a language SDK, or with zero code at your API gateway. Everything in this repo is what you install to get usage events flowing into Aforo.
Distribution status: these packages are being prepared for public registries (npm · PyPI · Maven Central · Go modules) and the gateway plugins for tagged GitHub Releases. Until a package is published, install it from source — each package directory has its own README with the steps. The integration model below is stable.
| You want to… | Use | Where |
|---|---|---|
| Meter from inside your service code | A language SDK + framework middleware | aforo-metering-sdks/ |
| Meter without touching app code | A gateway plugin (Tier 0) | aforo-gateway-plugins/ |
| Meter an MQTT broker (EMQX) | The broker plugin | aforo-emqx-plugin/ |
| Meter a GraphQL / gRPC / WebSocket / MQTT surface | A protocol-specific SDK | aforo-metering-sdks/<lang>-<protocol>/ |
| Meter AI agents or MCP tool calls | An MCP/agent SDK or the transport proxy | aforo-metering-sdks/{node-mcp, python-mcp, mcp-proxy, node-agent} |
| Send events with no SDK at all | Direct REST POST /v1/ingest/batch |
See "Common model" below |
Most teams start with a gateway plugin (no code) or the base SDK for their language, then add protocol/MCP variants as needed.
| Language | Base | GraphQL | gRPC | WebSocket | MQTT | MCP | Agent | Proxy (CLI) |
|---|---|---|---|---|---|---|---|---|
| Node | node |
node-graphql |
node-grpc |
node-ws |
node-mqtt |
node-mcp |
node-agent |
mcp-proxy |
| Python | python |
python-graphql |
python-grpc |
python-ws |
python-mqtt |
python-mcp |
— | — |
| Java | java |
java-graphql |
java-grpc |
java-ws |
java-mqtt |
— | — | — |
| Go | go |
go-graphql |
go-grpc |
go-ws |
go-mqtt |
— | — | — |
Base SDKs ship framework middleware (Express/Fastify/Koa, FastAPI/Django/Flask, Spring Boot servlet filter, net/http + Chi). The MCP SDKs wrap your tool handlers to auto-meter tool invocations; the transport proxy meters stdio/SSE MCP servers without code changes.
| Gateway | Folder | Artifact |
|---|---|---|
| Kong | aforo-gateway-plugins/kong |
Lua plugin (.rockspec) |
| Apigee | aforo-gateway-plugins/apigee |
Shared-flow bundle (JS callout) |
| AWS API Gateway | aforo-gateway-plugins/aws-lambda |
Lambda + SAM template + JWT authorizer |
| Azure APIM | aforo-gateway-plugins/azure-apim |
Outbound XML/C# policy |
| MuleSoft | aforo-gateway-plugins/mulesoft |
DataWeave custom policy |
Plus IaC templates in aforo-gateway-plugins/aws-cloudformation and aforo-gateway-plugins/azure-arm-templates, and per-gateway deployment notes in aforo-gateway-plugins/docs. All five gateway plugins detect MCP tools/call JSON-RPC and meter the tool name + agent id alongside standard HTTP requests.
Broker-level metering for EMQX 5.x — an Erlang OTP plugin that meters MQTT client connections, publishes, and subscriptions at the broker instead of at an API gateway. See aforo-emqx-plugin/README.md.
Every artifact ships three docs in its own folder: a README (install + quickstart + config table), a USER_GUIDE.md (step-by-step walkthrough to a verified event in Aforo), and a CHANGELOG.md. Each carries its own version — in the manifest (package.json / setup.py / pom.xml / .rockspec) or a VERSION file (Go, Apigee, IaC) — recorded in both code and docs. See VERSIONING.md for the per-artifact SemVer convention.
Everything here funnels usage to one ingestion API. SDKs and the gateway/broker plugins batch events and POST /v1/ingest/batch with a body of {"events":[...]} (1–1000 events; plugins do it from the log/response phase, non-blocking).
- Endpoint base:
https://api.aforo.ai(override per environment). The full batch URL ishttps://api.aforo.ai/v1/ingest/batch. - Auth:
X-API-Key: <AFORO_API_KEY>— thesk_live_.../sk_test_...key you create in the Aforo UI. Do not send it asAuthorization: Bearer: the ingestor parses a Bearer value as a JWT and rejects the request 401, even whenX-API-Keyis also present. - Tenant scope: derived from the API key on the server. Where an SDK or plugin still asks for a tenant id (the protocol SDKs, Kong), the key's tenant always wins.
- Event fields (camelCase):
customerId,metricName(must match a billable metric in your Aforo catalog),quantity(> 0),occurredAt(ISO-8601),idempotencyKey, andproductType— one ofAPI,AGENTIC_API,AI_AGENT,MCP_SERVER,GRPC_API,GRAPHQL_API,WEBSOCKET_API,MQTT_BROKER.productTypeis required in production; every SDK sets it from a client-level option (base SDKs default toAPI, protocol SDKs to their own type) that you can override per event. - Idempotency: every SDK and plugin mints a unique
idempotencyKeyper event (a random UUID, or a per-request id from the gateway) when the event is created, and re-sends that same key on retries — so a retried batch is deduplicated but two genuinely distinct events are never confused. Dedup is opt-in: pass your ownidempotencyKeyand it is sent verbatim, and the ingestor dedupes on it.
Gateway/broker plugins take aforo_endpoint, api_key, and product_type (default API); the Kong plugin still also requires tenant_id. Each plugin README shows where to set them.
SDKs/
├── aforo-metering-sdks/ # language SDKs (Node / Python / Java / Go × base + protocols + MCP/agent)
├── aforo-gateway-plugins/ # API gateway plugins (Kong / Apigee / AWS / Azure / MuleSoft) + IaC + docs
├── aforo-emqx-plugin/ # MQTT broker (EMQX) metering plugin — experimental
├── .github/ # CI + release/publish workflows, issue/PR templates, CODEOWNERS
├── VERSIONING.md # per-artifact SemVer convention (version in code + docs)
├── PUBLISHING.md # release + registry-publish runbook (maintainers)
├── CONTRIBUTING.md
├── SECURITY.md · SUPPORT.md · CODE_OF_CONDUCT.md
├── README.md
└── LICENSE
Apache License 2.0 — see LICENSE.