Usage meters
Usage meters answer "how much did customer X actually use" without a separate billing vendor. Enable them once per environment, record one event per metered action, and read back the aggregate per customer per meter — all on your own project's database, under row level security.
Neither Supabase nor Neon ships a usage-metering primitive: a team on either one wires up a separate product (Stripe metering, Lago, Orb, Metronome) before it can answer that question at all. This is the same event-per-metric model Scribase runs for its own flat-rate billing internally, exposed directly as a customer-facing feature.
POST /v1/organizations/{org}/projects/{project}/environments/{env}/usage-meters
Sets usage meters up in the environment: creates
public.scribase_usage_meter_events (row-level-secured), the
public.scribase_usage_meter_totals aggregate view, and a
scribase_record_usage RPC an end user can call for their own usage.
Idempotent — safe to call again. Requires an owner or admin role.
curl -X POST "$SCRIBASE_API_URL/v1/organizations/acme/projects/store/environments/production/usage-meters" \
-H "Authorization: Bearer $SCRIBASE_ACCESS_TOKEN"Response: the same shape as the status endpoint below, immediately after setup.
GET /v1/organizations/{org}/projects/{project}/environments/{env}/usage-meters
Whether usage meters are set up, and how many events, distinct customers and distinct meters the environment has recorded so far. Any project member can call this.
{ "enabled": true, "events": 1842, "customers": 37, "meters": 3 }An environment where usage meters were never set up answers
{ "enabled": false, "events": 0, "customers": 0, "meters": 0 } rather than
a 404.
POST /v1/organizations/{org}/projects/{project}/environments/{env}/usage-meters/record
Records one usage event for one of the project's own customers — a Dodo/Stripe/Creem customer id, an org id, anything the project already bills by. This is the call a backend makes on every metered action (an AI call, an API request, a GB stored). Requires an owner, admin or developer role.
| Field | Type | Notes |
|---|---|---|
meter |
string | Required. Lowercase, dotted metric code, e.g. ai_tokens or api.calls. At most 64 characters. |
external_customer_id |
string | Required. 1–200 printable characters — your own customer id, not a Scribase one. |
quantity |
number | Required. Non-negative. |
properties |
object | Optional. Up to 4000 bytes JSON, kept with the event for later inspection. |
transaction_id |
string | Optional. Supply your own for idempotency; otherwise one is generated. A repeated transaction_id is a no-op, not a duplicate event. |
curl -X POST "$SCRIBASE_API_URL/v1/organizations/acme/projects/store/environments/production/usage-meters/record" \
-H "Authorization: Bearer $SCRIBASE_ACCESS_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"meter": "ai_tokens",
"external_customer_id": "cus_8f2a1c",
"quantity": 2140,
"transaction_id": "req_9d3e7b21"
}'Response:
{ "recorded": true, "transaction_id": "req_9d3e7b21" }"recorded": false means this transaction_id was already recorded; the
quantity was not added again. Answers 409 usage_meters_not_enabled if the
environment hasn't had usage meters set up yet.
GET /v1/organizations/{org}/projects/{project}/environments/{env}/usage-meters/summary
The aggregated quantity and event count per meter per customer, most-used first (up to 200 rows). Any project member can call this.
[
{ "external_customer_id": "cus_8f2a1c", "meter": "ai_tokens", "total_quantity": 418290, "event_count": 212 },
{ "external_customer_id": "cus_19b0aa", "meter": "api.calls", "total_quantity": 9034, "event_count": 9034 }
]An environment where usage meters haven't been set up answers [], not an
error.
From inside your own app
The scribase_record_usage RPC the setup call creates lets a signed-in user
record their own usage directly through supabase-js, with your existing RLS
policies deciding what they're allowed to see back:
await supabase.rpc('scribase_record_usage', {
p_meter: 'ai_tokens',
p_quantity: 2140,
});Use the REST endpoints above from your backend when the event is keyed by a billing customer id rather than a signed-in Scribase user, or when the write needs to happen from a server you control (a webhook handler, a cron job, a queue worker).