Billing Meters
🚀 Overview
Billing meters represent usage metrics (e.g., API calls, storage use, transaction value) that your applications track and bill for. Each meter defines:
- aggregation_type (
count,sum,latest,fixed-retainer,ormax) - A field in your payload (
aggregation_field) to apply that aggregation on; - Optional filters for scoped usage capture
idis a unique code for a meter (merchant-defined or system generated)
Aggregation Types Define How Consumption Is Measured
Below is the complete list of aggregation types officially supported by XStak.
While COUNT tallies the number of times an event occurs, all other types aggregate over a single property of the event. The result of this aggregation directly determines how your customers are charged.
LATEST aggregation type is one-time only, meaning it cannot be recurring, and the number of billing units resets to 0 at the end of each period.
MAX aggregation type is one-time only, meaning it cannot be recurring, and the number of billing units resets to 0 at the end of each period.
How Fixed-Retainer Works
- The fixed fee guarantees a minimum monthly charge, regardless of usage.
- Usage events are still captured — so if the customer uses more than what is included, they pay extra usage charges.
This is ideal when merchants want to provide:
- Predictable billing
- Included usage tiers
- Simple, transparent pricing
This resource lets you create, update, get or toggle meters via REST endpoints.
🛠 Base URL
{{base_url}}/public/v1/meter⚙️ Endpoints
1. Create a Billing Meter
POST /✅ Request Body
{
"name": "My API Calls",
"active": true,
"aggregation_type": "count",
"aggregation_field": "apiCall",
"id": "api_calls_01",
"filters": { "region": "EU" }
}name: Human-friendly labelaggregation_type: One ofcount,sum,latest,maxaggregation_field: Field inside event payload to aggregateid: Unique meter identifier (merchant-specific)filters: Optional JSON object to restrict meter inputs (e.g. by metadata)
✅ Response (201 Created)
{
"id": "api_calls_01",
"name": "My API Calls",
"active": true,
"aggregation_type": "count",
"aggregation_field": "apiCall",
"filters": { "region": "EU" },
"account_id": "...",
"store_id": "...",
"mode": "live",
"createdAt": "...",
"updatedAt": "..."
}2. Update a Billing Meter
PATCH /:id✅ Request Body (Partial updates allowed)
{
"name": "Updated Name",
"active": false,
"subscription": { "next_action": "pause" },
"plan": { "active": false },
"filters": { "region": "APAC" }
}active = falserequiressubscription.next_actionandplan.active
✅ Response (200 OK)
Returns the updated meter object.
3. Change Active State
PATCH /:id/deactivate✅ Request Body
{
"subscription": {
"next_action": "cancel"
},
"plan": {
"active":false
}
}Triggers controlled disabling with plan/subscription coordination.
📩 Send Meter Event
POST /public/v1/meter/:id/eventUsed by merchants to report billing-relevant events (usage begins, data change, removal) on a specific meter.
🔹 Request Parameters
id(path parameter): string — unique meter ID- Body (JSON):
✅ Validation Rules
operation: must be "ADD" or "REMOVE"event_id: unique event reference stringtimestamp: valid ISO‑8601 timestamp- payload:
- All string fields must not include
% ? { } $ ^ * @ < >
🧱 Why This Rule Exists
Not all aggregation types support reversing or subtracting usage.
🔹 Response
- Success (200 OK):
- 400 Bad Request: invalid payload
- 404 Not Found: unknown meter
:id - 409 Conflict: duplicate
event_id - 500 Internal Error
🔍 Notes & Best Practices
"ADD"and"REMOVE"operations are used to include or exclude usage during billing aggregation. Process according to business logic (e.g., ignore"REMOVE", or reverse prior usage).- Ensure the
event_idis globally unique to avoid duplicates. - Maintain strong validation of
aggregation_fieldandvalueto avoid misuse (e.g. typos or wrong usage units). - For accurate billing cycles, attach correct
timestampin UTC.
🔐 Field Validation Patterns
name,id,aggregation_field: must match/^[^%?{}\[\]$^*@<>]*$/aggregation_type: one ofcount|sum|latest|max- If
active: false,subscriptionandplanrequired with valid inner fields