Skip to main content
Usage records are timestamped data points stored in a MongoDB timeseries collection. They are the source of truth for all usage tracking in SolvaPay — limit checks, billing calculations, and analytics dashboards all query from the same data stream.

Record Structure

Each usage record contains:
FieldTypeDescription
timestampDateWhen the event occurred
metadata.providerIdObjectIdThe provider that owns this event
metadata.meterNamestringWhich meter this record belongs to (e.g. requests)
metadata.customerRefstringThe customer who triggered the event
valuenumberNumeric value (defaults to 1 for counters)
propertiesobject?Arbitrary key-value tags for filtering and analysis
Usage records are immutable. Once recorded, they cannot be modified or deleted. MongoDB’s timeseries engine automatically compresses and indexes records by metadata and time for efficient aggregation.

Recording Usage

Usage is recorded via UsageService.recordUsage(), which writes to the Usage timeseries collection.

Via SDK Endpoint

Record usage directly from your backend using the SDK API:
# Record a single event
POST /v1/sdk/meter-events
{
  "customerRef": "cus_abc123",
  "value": 1,
  "properties": {
    "endpoint": "/api/v1/search",
    "method": "GET"
  }
}
# Record events in bulk
POST /v1/sdk/meter-events/bulk
{
  "records": [
    { "customerRef": "cus_abc123", "value": 1 },
    { "customerRef": "cus_def456", "value": 1 }
  ]
}

Via Usage Endpoint

The usage endpoint records generic usage events (not direct meter-name events):
POST /v1/sdk/usages
{
  "customerRef": "cus_abc123",
  "actionType": "api_call",
  "units": 1,
  "outcome": "success",
  "productRef": "prd_P9Q0R1S2"
}
Use /v1/sdk/meter-events when you need explicit meterName + value semantics.

Via NATS Event Bus

For internal services, usage can be published over NATS:
// Record a single usage record
await nats.publish('solvapay.meter.record', {
  meterName: 'requests',
  userId: 'cus_abc123',
  value: 1,
  properties: { endpoint: '/search' }
})

// Record usage in bulk
await nats.publish('solvapay.meter.recordBulk', {
  events: [
    { meterName: 'requests', userId: 'cus_abc123', value: 1 },
    { meterName: 'requests', userId: 'cus_def456', value: 1 }
  ]
})

Automatic MCP Tool Tracking

Every MCP tool call through the SolvaPay proxy automatically records a usage record against the provider’s requests meter. No SDK integration needed — the proxy records usage with the outcome as a property:
{
  "meterName": "requests",
  "customerRef": "cus_abc123",
  "value": 1,
  "properties": {
    "outcome": "success"
  }
}
This happens fire-and-forget so it never blocks the tool call response.

Querying Usage

Aggregated Usage

Query total usage for a specific meter and customer within a time range:
GET /ui/meters/mtr_XXXXXXXX/usage?from=2025-01-01&to=2025-02-01&customerRef=cus_abc123

Time-Bucketed Aggregation

Get usage broken down by time intervals for charting:
GET /ui/meters/mtr_XXXXXXXX/usage?from=2025-01-01&to=2025-02-01&granularity=day
Supported granularities: hour, day, week, month. When no customerRef is specified, the aggregation runs across all customers.

Analytics Queries

The Usage timeseries powers several analytics views:
QueryDescription
Overall daily statsTotal usage records per day
Usage by customerTotal usage grouped by customer

How Usage Powers Billing and Limits

Limit Checking

When a customer makes a request to a protected endpoint, the system:
  1. Finds the customer’s active purchase for the product
  2. Looks up the plan to get the limit
  3. Resolves the plan’s auto-assigned requests meter
  4. Queries UsageService.sumForMeter() for usage from periodStart to now
  5. Compares the total against the plan’s hard cap
If usage exceeds the limit, the request is denied with a paywall response including a checkout URL.
Request → Find Purchase → Resolve Requests Meter

                    Query Usage timeseries
                    (sum from periodStart to now)

                    used >= limit? → Paywall
                    used < limit?  → Allow + Record Usage

End-of-Period Billing

For post-paid and hybrid plans, a daily cron job:
  1. Finds purchases with expired billing periods
  2. Queries the Usage timeseries for total usage in the period
  3. Subtracts free units from the total
  4. Applies the plan’s pricing (flat rate or tiered)
  5. Creates a payment intent for the calculated amount
See Billing for the full billing flow.

Record Properties

The optional properties field supports arbitrary key-value pairs for filtering and analysis:
{
  "meterName": "requests",
  "customerRef": "cus_abc123",
  "value": 1,
  "properties": {
    "endpoint": "/api/v1/search",
    "method": "POST",
    "region": "us-east-1",
    "status": 200
  }
}
Properties are stored with the usage record and available in analytics queries. Use them to add context like API endpoints, tool names, regions, or response codes.

Environment Isolation

Usage records are stored in environment-specific collections:
  • Usage_sandbox — sandbox environment records
  • Usage_live — live environment records
Records in sandbox never mix with live data. The environment is determined by the provider’s configuration.

Next Steps

  • Meters — meter definitions
  • Plans — how plans use meters for limit enforcement
  • Billing — end-of-period usage billing