API Reference
Complete reference for the ABAXUS billing API. Every operation is a REST call. Every number is DECIMAL(20,10) — never float. Every write endpoint is idempotent.
Overview
ABAXUS is a self-hosted, API-first usage-based billing engine. You deploy it inside your infrastructure; your application calls it over HTTP. There is no external dependency beyond your chosen payment provider (Stripe is the default).
https://api.your-domain.comapplication/jsonDECIMAL(20,10)Authentication
All requests require a Bearer token in the Authorization header. API keys are generated in the ABAXUS admin dashboard. Keys never expire but can be rotated at any time.
Authorization: Bearer abx_live_xxxxxxxxxxxxx Content-Type: application/json
curl -X POST \ -H "Authorization: Bearer abx_live_xxx" \ -H "Content-Type: application/json" \ https://api.your-domain.com/v1/events \ -d '{"customer_id":"cust_acme",...}'
abx_live_. Test keys start with abx_test_. Test keys accept all requests but do not trigger real payment charges.Idempotency
All POST and PATCH endpoints accept an idempotency_key field in the request body. Sending the same key a second time returns the original response without re-executing the operation — safe to retry on any network failure.
idempotency_keyrequired on eventsIdempotency-Keyheader altErrors
The API uses standard HTTP status codes. Error bodies always include error.code (machine-readable) and error.message (human-readable).
{
"error": {
"code": "METRIC_KEY_DUPLICATE",
"message": "A metric with key 'api_calls' already exists.",
"field": "key"
}
}A metric is the definition of a billable signal. It tells ABAXUS how to aggregate raw events into a usage total. The key is the stable identifier used throughout the system — across events, charges, and usage queries.
Define a new billable signal. The key must be unique and URL-safe — it is used as the stable identifier in all downstream references.
keyrequireddisplay_namerequiredaggregation_typerequiredsum · count · max · min · last · unique_count · percentilevalue_typeoptionalinteger (default) or decimal. Determines allowed event value format.filtersoptional{
"key": "api_calls",
"display_name": "API Calls",
"aggregation_type": "sum",
"value_type": "integer",
"filters": ["region", "tier"]
}{
"key": "api_calls",
"display_name": "API Calls",
"aggregation_type": "sum",
"value_type": "integer",
"active": true,
"created_at": "2026-03-17T10:00:00Z"
}activeoptionallimitoptionalcursoroptionalmeta.next_cursor for pagination.{
"data": [ { "key": "api_calls", ... } ],
"meta": {
"total": 12,
"next_cursor": "eyJrZXkiOiJzdG9y..."
}
}Updates display_name and filters. The key and aggregation_type are immutable after creation to preserve historical correctness.
Soft-deactivates the metric — sets active: false. Existing events and historical aggregates are preserved. New events using this key will be rejected with 422.
A price plan contains one or more charges, each linked to a metric and a pricing model. Plans are immutable — creating a plan with an existing id creates a new version. Active subscriptions always pin to their original plan version.
idrequiredid creates a new version.namerequiredcurrencyrequiredUSD).chargesrequired| per_unit | quantity × unit_amount |
| tiered | Each tier billed independently |
| volume | All units at the tier the total falls in |
| package | Rounds up to whole packages of package_size |
| flat_fee | Fixed amount regardless of usage |
{
"id": "plan_growth",
"name": "Growth",
"currency": "USD",
"charges": [
{
"key": "api_charge",
"metric_key": "api_calls",
"model": "tiered",
"properties": {
"tiers": [
{ "up_to": 10000, "unit_amount": "0.001" },
{ "up_to": null, "unit_amount": "0.0005" }
]
}
},
{
"key": "seat_fee",
"metric_key": "seats",
"model": "flat_fee",
"properties": { "amount": "49.00" }
}
]
}{
"id": "plan_growth",
"version": 1,
"name": "Growth",
"currency": "USD",
"charges": [ /* ... */ ],
"created_at": "2026-03-17T10:00:00Z"
}Returns the latest version of each plan. Supports ?active=true filter and cursor pagination.
Returns the full version history for a plan, oldest first. Each version is a complete snapshot of charges as they were when created — historical subscriptions reference a specific version number.
A customer record holds identity, billing provider credentials, and optionally a default payment method. Customers are the billing subject — events and invoices always reference a customer ID.
provider_customer_id (Stripe's cus_xxx) alongside provider_payment_method. Stripe requires a Customer to reuse a PaymentMethod across multiple PaymentIntents.idrequirednamerequiredbillingrequiredprovider and credentials.payment_methodoptionalemailoptionalmetadataoptional{
"id": "cust_acme",
"name": "Acme Corp",
"email": "billing@acme.com",
"billing": {
"provider": "stripe",
"credentials": {
"secret_key": "sk_test_..."
}
},
"payment_method": {
"provider": "stripe",
"provider_customer_id": "cus_xxx",
"provider_payment_method": "pm_xxx",
"type": "card",
"display_last4": "4242",
"is_default": true
}
}Cursor-paginated list. Supports ?status=active filter. Response includes display fields only — payment credentials are never returned.
Returns full customer detail including payment method display fields (display_last4, type). Raw Stripe credentials are never returned.
Partial update. Mutable fields: name, email, status, metadata, and billing.credentials. The customer id is immutable.
Replaces the customer's default payment method. The new method must be attached to the Stripe customer (cus_xxx) before calling this endpoint.
A subscription activates a price plan for a customer. It records plan_version at creation time — locking the customer to that exact plan definition for the lifetime of the subscription.
customer_idrequiredplan_idrequiredstart_daterequiredend_dateoptional{
"customer_id": "cust_acme",
"plan_id": "plan_growth",
"start_date": "2026-01-01T00:00:00Z"
}{
"id": "sub_a1b2c3",
"customer_id": "cust_acme",
"plan_id": "plan_growth",
"plan_version": 1,
"status": "active",
"start_date": "2026-01-01T00:00:00Z"
}Supports ?customer_id= and ?status=active filters. Cursor-paginated.
Events are the raw usage signal. Each event records a quantity of a metric at a point in time. idempotency_key is mandatory — duplicate keys are silently skipped, making retries safe on any network failure.
Queued asynchronously — never blocks your request path. Returns 202 Accepted immediately. The event is guaranteed to be durably stored before the response is returned.
customer_idrequiredmetric_keyrequiredvaluerequiredvalue_type.idempotency_keyrequiredtimestampoptionalsubscription_idoptionalpropertiesoptionalfilters can be used in usage queries.{
"customer_id": "cust_acme",
"subscription_id": "sub_a1b2c3",
"metric_key": "api_calls",
"value": "1",
"timestamp": "2026-03-17T14:00:00Z",
"idempotency_key": "evt_acme_req_abc123",
"properties": {
"region": "us-east-1"
}
}{
"id": "evt_xyz",
"status": "accepted",
"idempotency_key": "evt_acme_req_abc123"
}Ingest up to 500 events in a single request. Returns 207 Multi-Status — each item has its own status. One invalid event never rejects the rest.
{
"events": [
{ "idempotency_key": "evt_001", /* ... */ },
{ "idempotency_key": "evt_002", /* ... */ }
]
}
// Response: 207
{
"results": [
{ "idempotency_key": "evt_001", "status": 202 },
{ "idempotency_key": "evt_002", "status": 202 }
]
}Bypasses the async queue and writes directly to the events table. Automatically invalidates all pre-computed aggregates that overlap the backfilled time range. Use for data migrations or corrections.
Cursor-paginated. Supports filters: customer_id, metric_key, subscription_id, from, to (ISO 8601).
Returns bucketed event counts. Query params: customer_id, metric_key, from, to, bucket=day|week|month.
Two endpoints with different consistency guarantees. Use summary for dashboards and customer-facing widgets. Use compute when you need the authoritative figure that will appear on an invoice.
Reads from pre-computed aggregates. May lag by meta.lag_seconds. The response includes meta.consistency: "eventual". Best for high-frequency dashboard polling.
customer_idrequiredmetric_keyrequiredperiod_startrequiredperiod_endrequired# Query params ?customer_id=cust_acme &metric_key=api_calls &period_start=2026-03-01T00:00:00Z &period_end=2026-04-01T00:00:00Z // 200 Response { "value": "85000", "metric_key": "api_calls", "meta": { "consistency": "eventual", "lag_seconds": 4 } }
Real-time scan of the raw events table. Returns meta.consistency: "exact". This is the value used internally when billing is calculated. Idempotent.
{
"customer_id": "cust_acme",
"metric_key": "api_calls",
"period_start": "2026-03-01T00:00:00Z",
"period_end": "2026-04-01T00:00:00Z",
"idempotency_key":"usage_acme_2026-03"
}
// 200 Response
{
"value": "85000",
"meta": { "consistency": "exact" }
}Run a calculation to see the full line-item breakdown before invoicing. The calculation_id is stored in the database and referenced inside the resulting invoice for a full audit trail.
Computes the exact amount owed for a subscription period. Uses usage/compute internally for each metric. The result is idempotent per idempotency_key.
customer_idrequiredsubscription_idrequiredperiod_startrequiredperiod_endrequiredidempotency_keyoptional{
"customer_id": "cust_acme",
"subscription_id": "sub_a1b2c3",
"period_start": "2026-03-01T00:00:00Z",
"period_end": "2026-04-01T00:00:00Z",
"idempotency_key": "calc_acme_2026-03"
}{
"calculation_id": "calc_xyz",
"total_amount": "127.50",
"currency": "USD",
"line_items": [
{
"charge_key": "api_charge",
"model": "tiered",
"quantity": "85000",
"amount": "52.50",
"tiers": [
{ "up_to": 10000, "quantity": 10000, "amount": "10.00" },
{ "up_to": null, "quantity": 75000, "amount": "37.50" }
]
},
{ "charge_key": "seat_fee", "amount": "75.00" }
]
}Calculates a hypothetical cost without a subscription. Pass a plan_id and a list of {metric_key, value} pairs. Useful for pricing page calculators and quote tools.
{
"plan_id": "plan_growth",
"usage": [
{ "metric_key": "api_calls", "value": "50000" },
{ "metric_key": "seats", "value": "1" }
]
}Invoices capture a billing period's calculated amount and move through a well-defined lifecycle: issued → paid or archived. Invoices with a $0 total are rejected. Re-running with the same effective period is idempotent.
cutoff_date becomes period_end. period_start is derived from the date of the last invoice or the earliest subscription start_date — whichever is later. The system runs pricing calculation internally.
customer_idrequiredsubscription_idsoptionalcutoff_daterequired{
"customer_id": "cust_acme",
"subscription_ids": ["sub_a1b2c3"],
"cutoff_date": "2026-03-31T23:59:59Z"
}{
"id": "inv_abc",
"customer_id": "cust_acme",
"status": "issued",
"total_amount": "127.50",
"currency": "USD",
"period_start": "2026-03-01T00:00:00Z",
"period_end": "2026-03-31T23:59:59Z",
"line_items": [ /* ... */ ],
"issued_at": "2026-04-01T00:00:00Z"
}Creates invoices for all active customers in a single call. Accepts cutoff_date only. Each customer gets their own invoice. Customers with $0 total are skipped. Returns a job ID — poll GET /v1/invoices/bulk/:job_id for status.
Cursor-paginated. Supports filters: customer_id, status, from, to.
{
"events": [
{ "type": "issued", "at": "2026-04-01T00:00:00Z" },
{ "type": "email_sent", "at": "2026-04-01T00:01:00Z" },
{ "type": "charged", "at": "2026-04-01T00:01:45Z",
"amount": "127.50", "payment_intent": "pi_xxx" }
]
}Dispatches the invoice email to customer.email. Only valid from issued status. Idempotent — re-sending returns a 200 without dispatching a duplicate.
Creates a Stripe PaymentIntent for the invoice total using the customer's default payment method. On success, transitions the invoice to paid and records the payment_intent ID on the audit trail. Idempotent.
Moves the invoice to archived status. Valid from both issued and paid. Archived invoices are preserved in full for audit purposes and can never be charged again.
Ready to integrate?
See the full billing flow from a fresh deployment to a paid invoice — with code at every step.