Administration

Recording consumption

Record arbitrary numeric metrics per user per application and retrieve aggregates.

The consumption API lets backend services record arbitrary numeric usage metrics (API calls, bytes stored, seats used, etc.) per user per application. Aggregates are stored in a dedicated table and can be read back — or reset — via the API. A key that prices a metered feature of the user's plan is handled differently, see Metered keys.

Authentication

The consumption endpoints (record and read) accept two forms of authentication:

  1. client_credentials access token — a Bearer token issued by the OAuth client_credentials grant for the application. This is the recommended approach for server-to-server calls. The token is bound to its own application: using it for another application's applicationId returns 403 AUTH_011. A token bound to a user is refused with 403 CONS_004, and an invalid or expired token with 401 AUTH_001.
  2. Admin session cookie — an active session with the admin or superadmin role. It may act on any application.

Any other request receives 403 CONS_004. Resetting a counter is admin-session only (see below).

Record consumption

POST /api/consumption
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "applicationId": "<app-uuid>",
  "userId": "<user-id>",
  "key": "api.calls",
  "value": 10
}
FieldTypeDescription
applicationIdstring (UUID)Target application
userIdstringTarget user
keystringMetric name — alphanumeric plus dots (1–64 chars), e.g. api.calls, storage.bytes. An invalid key returns 400 CONS_001
valuenumberAmount to add — must be a finite number (positive or negative), otherwise 400 CONS_002
idempotencyKeystringOptional retry key (1–200 chars). Honoured for metered keys only

A raw entry (in consumptionEntries) is inserted and the running aggregate (in consumptionAggregates) is updated with an ON CONFLICT … DO UPDATE upsert, in two successive statements. Returns 404 CONS_003 if the user has no access record for the application. The response is:

{ "success": true, "aggregate": { "key": "api.calls", "total": "1540" } }

Read aggregates

GET /api/consumption/:userId/:appId

Returns the latest aggregate for every key the user has recorded for the application:

{
  "aggregates": [
    { "key": "api.calls", "total": "1540", "updatedAt": "2026-04-12T10:00:00Z" },
    { "key": "storage.bytes", "total": "52428800", "updatedAt": "2026-04-11T15:30:00Z" }
  ]
}

total is returned as a string because the underlying column uses numeric (arbitrary precision). Parse with parseFloat() or a decimal library.

A single key can be read with GET /api/consumption/:userId/:appId/:key, which returns { "aggregate": { "key", "total", "updatedAt" } }, or 404 CONS_003 when nothing was recorded. A malformed identifier returns 400 CONS_005.

Administrators can also read all users of an application with GET /api/admin/applications/:id/consumption ({ "entries": [...] } with user name, email, plan id, key, total and updatedAt), and the entries of one calendar month, grouped by user and key, with GET /api/admin/applications/:appId/consumption/monthly?year=2026&month=10 (year and month default to the current ones; the month boundaries use the server's local time zone). In the admin interface these are shown on the Consumption tab of an application.

Metered keys

When the key is the featureKey of a metered price on the plan of the user's active subscription, the report is not stored in the counters above. Instead it is recorded as metered usage for the current UTC month, subject to the plan's cap, and, when Stripe is configured, pushed to Stripe as a meter event (see the Stripe integration guide). In that case:

  • value must be greater than zero (400 BIL_002 otherwise);
  • a report that would take the month's total past usageLimit is refused with 403 BIL_003 and not recorded (-1 means no cap);
  • the same idempotencyKey reported twice counts once; without one, every call counts;
  • the response is { "success": true, "metered": true, "aggregate": { "key", "total" } }, where total is the month's usage;
  • GET and DELETE on /api/consumption/... do not see this usage. Read it with the application billing API.

Reset a counter

Admin only (requires an admin session — client_credentials tokens are not accepted for DELETE):

DELETE /api/consumption/:userId/:appId/:key

Deletes all raw entries and the aggregate row for the given userId + appId + key combination and returns 204.

Copyright © 2026