v1 · REST · JSON base: https://api.your-domain.com

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).

Base URL
https://api.your-domain.com
Format
application/json
Precision
DECIMAL(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.

Request headers
Authorization: Bearer abx_live_xxxxxxxxxxxxx
Content-Type:  application/json
cURL example
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",...}'
Key format: Production keys start with 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.

Field
Behaviour
idempotency_keyrequired on events
Any unique string (max 255 chars). UUID v4 recommended. Events require this field; all other endpoints accept it as optional but strongly recommended.
Idempotency-Keyheader alt
Can also be passed as an HTTP header instead of in the body. Body field takes precedence if both are present.

Errors

The API uses standard HTTP status codes. Error bodies always include error.code (machine-readable) and error.message (human-readable).

200OK — synchronous success
201Created — resource created
202Accepted — async (events)
207Multi-Status — batch partial
400Bad Request — invalid payload
401Unauthorized — bad API key
404Not Found — resource missing
409Conflict — idempotency mismatch
422Unprocessable — business rule violation
429Rate Limited — slow down
400 Error body
{
  "error": {
    "code":    "METRIC_KEY_DUPLICATE",
    "message": "A metric with key 'api_calls' already exists.",
    "field":   "key"
  }
}
Metrics

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.

POST /v1/metrics Create a metric

Define a new billable signal. The key must be unique and URL-safe — it is used as the stable identifier in all downstream references.

Field
Type
Description
keyrequired
string
Unique slug. Lowercase, hyphens/underscores OK. Immutable after creation.
display_namerequired
string
Human-readable label shown in the dashboard and invoices.
aggregation_typerequired
enum
sum · count · max · min · last · unique_count · percentile
value_typeoptional
enum
integer (default) or decimal. Determines allowed event value format.
filtersoptional
string[]
Property keys from event payloads that can be used to filter this metric's aggregation.
POST /v1/metrics
{
  "key":              "api_calls",
  "display_name":     "API Calls",
  "aggregation_type": "sum",
  "value_type":       "integer",
  "filters":          ["region", "tier"]
}
201 Response
{
  "key":              "api_calls",
  "display_name":     "API Calls",
  "aggregation_type": "sum",
  "value_type":       "integer",
  "active":           true,
  "created_at":       "2026-03-17T10:00:00Z"
}
GET /v1/metrics List metrics
Query param
Type
Description
activeoptional
boolean
Filter by active status. Omit to return all.
limitoptional
integer
Max results per page (default 50, max 500).
cursoroptional
string
Opaque cursor from meta.next_cursor for pagination.
GET /v1/metrics?active=true&limit=50
{
  "data": [ { "key": "api_calls", ... } ],
  "meta": {
    "total": 12,
    "next_cursor": "eyJrZXkiOiJzdG9y..."
  }
}
PATCH /v1/metrics/:key Update metric

Updates display_name and filters. The key and aggregation_type are immutable after creation to preserve historical correctness.

DELETE /v1/metrics/:key Deactivate metric

Soft-deactivates the metric — sets active: false. Existing events and historical aggregates are preserved. New events using this key will be rejected with 422.

Price Plans

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.

POST /v1/price-plans Create plan
Field
Type
Description
idrequired
string
Stable plan identifier. Re-using an existing id creates a new version.
namerequired
string
Human-readable plan name.
currencyrequired
string
ISO 4217 currency code (e.g. USD).
chargesrequired
Charge[]
Array of charges. Each charge links a metric to a pricing model.
Pricing Models
per_unitquantity × unit_amount
tieredEach tier billed independently
volumeAll units at the tier the total falls in
packageRounds up to whole packages of package_size
flat_feeFixed amount regardless of usage
POST /v1/price-plans
{
  "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" }
    }
  ]
}
201 Response
{
  "id": "plan_growth",
  "version": 1,
  "name": "Growth",
  "currency": "USD",
  "charges": [ /* ... */ ],
  "created_at": "2026-03-17T10:00:00Z"
}
GET /v1/price-plans List plans

Returns the latest version of each plan. Supports ?active=true filter and cursor pagination.

GET /v1/price-plans/:id/versions List all versions of a plan

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.

Customers

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.

POST /v1/customers Create a customer
Stripe requirement: Always include provider_customer_id (Stripe's cus_xxx) alongside provider_payment_method. Stripe requires a Customer to reuse a PaymentMethod across multiple PaymentIntents.
Field
Type
Description
idrequired
string
Your stable customer identifier.
namerequired
string
Display name for dashboards and invoices.
billingrequired
object
Payment provider config. Contains provider and credentials.
payment_methodoptional
object
Default payment method. See schema below.
emailoptional
string
Billing email address. Used when sending invoice emails.
metadataoptional
object
Arbitrary key-value pairs. Returned on every response but never used in billing logic.
POST /v1/customers
{
  "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
  }
}
GET /v1/customers List customers

Cursor-paginated list. Supports ?status=active filter. Response includes display fields only — payment credentials are never returned.

GET /v1/customers/:id Retrieve a customer

Returns full customer detail including payment method display fields (display_last4, type). Raw Stripe credentials are never returned.

PATCH /v1/customers/:id Update a customer

Partial update. Mutable fields: name, email, status, metadata, and billing.credentials. The customer id is immutable.

POST /v1/customers/:id/payment-methods Replace payment method

Replaces the customer's default payment method. The new method must be attached to the Stripe customer (cus_xxx) before calling this endpoint.

Subscriptions

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.

POST /v1/subscriptions Create a subscription
Field
Type
Description
customer_idrequired
string
The customer to subscribe.
plan_idrequired
string
The price plan. The latest version is pinned at creation.
start_daterequired
string
ISO 8601 UTC datetime. The subscription is active from this date.
end_dateoptional
string
If set, the subscription auto-cancels on this date.
POST /v1/subscriptions
{
  "customer_id": "cust_acme",
  "plan_id":     "plan_growth",
  "start_date":  "2026-01-01T00:00:00Z"
}
201 Response
{
  "id":           "sub_a1b2c3",
  "customer_id": "cust_acme",
  "plan_id":     "plan_growth",
  "plan_version": 1,
  "status":      "active",
  "start_date":  "2026-01-01T00:00:00Z"
}
GET /v1/subscriptions List subscriptions

Supports ?customer_id= and ?status=active filters. Cursor-paginated.

Events

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.

POST /v1/events Ingest a single event → 202

Queued asynchronously — never blocks your request path. Returns 202 Accepted immediately. The event is guaranteed to be durably stored before the response is returned.

Field
Type
Description
customer_idrequired
string
Customer this usage belongs to.
metric_keyrequired
string
Must match an active metric key.
valuerequired
string
Numeric value as a string. Must match metric's value_type.
idempotency_keyrequired
string
Unique per event. UUID v4 recommended. Duplicate keys return the original response.
timestampoptional
string
ISO 8601 UTC. Defaults to now. Use for backfilling with exact timestamps.
subscription_idoptional
string
Scopes usage to a specific subscription. Required when the customer has multiple active subscriptions.
propertiesoptional
object
Arbitrary key-value pairs. Values for keys listed in the metric's filters can be used in usage queries.
POST /v1/events → 202
{
  "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"
  }
}
202 Response
{
  "id":               "evt_xyz",
  "status":           "accepted",
  "idempotency_key": "evt_acme_req_abc123"
}
POST /v1/events/batch Batch ingest → 207

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.

POST /v1/events/batch → 207
{
  "events": [
    { "idempotency_key": "evt_001", /* ... */ },
    { "idempotency_key": "evt_002", /* ... */ }
  ]
}

// Response: 207
{
  "results": [
    { "idempotency_key": "evt_001", "status": 202 },
    { "idempotency_key": "evt_002", "status": 202 }
  ]
}
POST /v1/events/backfill Backfill historical events

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.

GET /v1/events List events

Cursor-paginated. Supports filters: customer_id, metric_key, subscription_id, from, to (ISO 8601).

GET /v1/events/histogram Event histogram

Returns bucketed event counts. Query params: customer_id, metric_key, from, to, bucket=day|week|month.

Usage

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.

GET /v1/usage/summary Eventual summary — fast

Reads from pre-computed aggregates. May lag by meta.lag_seconds. The response includes meta.consistency: "eventual". Best for high-frequency dashboard polling.

Query param
Type
Description
customer_idrequired
string
Customer to query.
metric_keyrequired
string
Metric to aggregate.
period_startrequired
string
ISO 8601 UTC start of the period.
period_endrequired
string
ISO 8601 UTC end of the period.
GET /v1/usage/summary
# 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
  }
}
POST /v1/usage/compute Exact computation — authoritative

Real-time scan of the raw events table. Returns meta.consistency: "exact". This is the value used internally when billing is calculated. Idempotent.

POST /v1/usage/compute
{
  "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" }
}
Pricing

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.

POST /v1/pricing/calculate Calculate billing

Computes the exact amount owed for a subscription period. Uses usage/compute internally for each metric. The result is idempotent per idempotency_key.

Field
Type
Description
customer_idrequired
string
The customer being billed.
subscription_idrequired
string
The subscription to calculate charges for.
period_startrequired
string
ISO 8601 UTC billing period start.
period_endrequired
string
ISO 8601 UTC billing period end.
idempotency_keyoptional
string
Recommended. Same key returns the cached calculation result.
POST /v1/pricing/calculate
{
  "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"
}
200 Response
{
  "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" }
  ]
}
POST /v1/pricing/preview Preview pricing

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.

POST /v1/pricing/preview
{
  "plan_id": "plan_growth",
  "usage": [
    { "metric_key": "api_calls", "value": "50000" },
    { "metric_key": "seats",     "value": "1" }
  ]
}
Invoices

Invoices capture a billing period's calculated amount and move through a well-defined lifecycle: issuedpaid or archived. Invoices with a $0 total are rejected. Re-running with the same effective period is idempotent.

issued → charge → paid | issued → archive → archived | paid → archive → archived
POST /v1/invoices Create an invoice

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.

Field
Type
Description
customer_idrequired
string
Customer to invoice.
subscription_idsoptional
string[]
Scope to specific subscriptions. Omit to include all active subscriptions.
cutoff_daterequired
string
ISO 8601 UTC. Usage up to and including this moment is included.
POST /v1/invoices
{
  "customer_id":      "cust_acme",
  "subscription_ids": ["sub_a1b2c3"],
  "cutoff_date":      "2026-03-31T23:59:59Z"
}
201 Response
{
  "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"
}
POST /v1/invoices/bulk Bulk create invoices

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.

GET /v1/invoices List invoices

Cursor-paginated. Supports filters: customer_id, status, from, to.

GET /v1/invoices/:id/events Audit trail
200 Audit trail response
{
  "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" }
  ]
}
POST /v1/invoices/:id/send-email Send invoice email

Dispatches the invoice email to customer.email. Only valid from issued status. Idempotent — re-sending returns a 200 without dispatching a duplicate.

POST /v1/invoices/:id/charge Charge the invoice

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.

POST /v1/invoices/:id/archive Archive invoice

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.