comecerodocs
Tutorials

Bill for seats

Charge for the most seats a team used in a month with a gauge metric. Send a seat count every hour, include five seats and price the rest per seat.

In this tutorial you bill a team for its seats. The plan includes 5 seats. Each seat above that costs 8 USD a month, and the bill uses the most seats the team had at any hour of the month. A team that grows to 12 seats for a week and then drops back to 6 pays for 12.

This needs a gauge metric. A gauge does not add events up: each hour keeps its highest reading, and the bill uses the highest hour of the period.

Before you start

You need the same things as in Bill for API calls: usage billing turned on, a test secret token, a test monthly USD subscription and a usage key. If you did that tutorial, reuse its agreement agr_team_42 and its key.

Create a gauge metric

Request
curl https://api.comecero.com/api/v1/usage/metrics \
  -H "Authorization: Bearer $COMECERO_SECRET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "metric_id": "met_seats",
    "name": "Seats",
    "event_key": "seats",
    "unit_label": "seat",
    "aggregation": "gauge"
  }'

Choose the aggregation with care: it can not change once the usage meter has the metric. For a different one, create a new metric.

Price the seats

A flat_per_unit plan charges overage_price for each seat above included_allowance.

Request
curl https://api.comecero.com/api/v1/usage/rate_plans \
  -H "Authorization: Bearer $COMECERO_SECRET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "rate_plan_id": "seats_standard",
    "version": 1,
    "metric_id": "met_seats",
    "currency": "USD",
    "pricing_model": "flat_per_unit",
    "included_allowance": 5,
    "overage_price": 8
  }'

In the dashboard this is Usage Billing, Prices, with How it is charged set to Per unit.

Add seats to the agreement

Request
curl https://api.comecero.com/api/v1/usage/entitlements \
  -H "Authorization: Bearer $COMECERO_SECRET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entitlement_id": "ent_team_42_seats",
    "billing_agreement_id": "agr_team_42",
    "metric_id": "met_seats",
    "rate_plan_id": "seats_standard",
    "rate_plan_version": 1
  }'

An agreement can hold one entitlement per metric, so the same team can be billed for API calls and seats on one invoice.

Send the seat count every hour

Run a job once an hour that sends each team's current number of seats. Make the operation_id from the team and the hour. A rerun in the same hour is then a duplicate, never a second reading.

report-seats.js
const USAGE_URL = 'https://usage.comecero.com/v1/events';

export async function reportSeats(teams) {
  const hour = new Date();
  hour.setUTCMinutes(0, 0, 0);
  const stamp = hour.toISOString().replace('.000Z', 'Z');

  const events = teams.map((team) => ({
    operation_id: `seats_${team.id}_${stamp}`,
    external_customer_id: team.id,
    metric_key: 'seats',
    quantity: String(team.seatCount),
    timestamp: stamp,
  }));

  for (let i = 0; i < events.length; i += 1000) {
    const response = await fetch(USAGE_URL, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.COMECERO_USAGE_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ events: events.slice(i, i + 1000) }),
    });
    if (response.status !== 200) throw new Error(`usage meter answered ${response.status}: try this hour again`);
  }
}

Run it again on any error: every event of the hour is safe to resend. To catch a short spike, also send a reading when seats are added, with its own id such as seats_team_42_2026-11-10T09:41:07Z_added. The hour keeps the highest reading.

What the team pays

The bill reads the highest hour of the month:

WeekSeats
16
212
3 and 46

The highest hour is 12 seats. 12 minus the 5 included is 7 billable seats, at 8 USD: 56.00 USD on the renewal invoice, as "Seats (seat): 12 used".

If you would rather bill the average over the month, count seat-hours instead: a sum metric that gets the seat count every hour, priced per seat-hour.

Next steps

On this page