Xtopay Docs
Usage Billing

Reporting Events

Send usage data to Xtopay in real time as your customers consume your product.

Overview

Report a usage event every time a metered action occurs. Xtopay aggregates events over the billing period and charges the customer at period end.

Report a single event

await xtopay.meters.reportEvent({
  meter_id: "mtr_a1b2c3",
  customer_id: "cus_abc123",
  event_name: "api.request",
  timestamp: new Date().toISOString(),  // when the event occurred (not when you report it)
  quantity: 1,                          // for "count" meters, always 1
  payload: {                            // arbitrary metadata
    endpoint: "/v1/payments",
    latency_ms: 120,
  },
});

Report a batch of events

For high-volume scenarios, batch up to 1,000 events per request:

await xtopay.meters.reportEvents([
  {
    meter_id: "mtr_a1b2c3",
    customer_id: "cus_abc123",
    event_name: "api.request",
    timestamp: "2026-05-24T10:00:01Z",
    quantity: 1,
  },
  {
    meter_id: "mtr_a1b2c3",
    customer_id: "cus_def456",
    event_name: "api.request",
    timestamp: "2026-05-24T10:00:02Z",
    quantity: 1,
  },
]);

Events in a batch are processed atomically — either all are accepted or all are rejected.

Idempotency for events

Use event_id to prevent duplicate counting if you retry a failed report:

await xtopay.meters.reportEvent({
  meter_id: "mtr_a1b2c3",
  customer_id: "cus_abc123",
  event_name: "sms.sent",
  event_id: "evt_unique_abc123",  // your own unique ID
  timestamp: new Date().toISOString(),
  quantity: 1,
});

Reporting the same event_id twice counts as one event.

Backdating events

You can report events up to 48 hours in the past. This is useful if you batch-process usage data rather than reporting in real time.

Events cannot be reported for a billing period that has already been invoiced.

Event timestamps

Always use the time the event occurred in your system — not the time you reported it to Xtopay. This ensures accurate billing even if there's latency in your reporting pipeline.

// Good: use the actual event time
await xtopay.meters.reportEvent({
  timestamp: requestTimestamp,   // when the API call happened
  ...
});

// Bad: don't use the current time if the event happened earlier
await xtopay.meters.reportEvent({
  timestamp: new Date().toISOString(),  // misleading if event happened 30s ago
  ...
});

High-volume patterns

For very high-volume metering (millions of events/day):

  1. Buffer events in memory for 1–5 seconds
  2. Send batches of 1,000 events at a time
  3. Use a background queue (BullMQ, SQS) for reliability
  4. Report failed batches with exponential backoff
// Example: buffer and batch with BullMQ
meterQueue.add("report", { events: bufferedEvents }, {
  attempts: 5,
  backoff: { type: "exponential", delay: 1000 },
});

On this page