comecerodocs
Tutorials

Show usage in your app

Show each customer how much of their plan they have used this month, and email them at 80% with the usage threshold webhook. Uses usage so far and alerts.

In this tutorial you show a customer their usage this month in your app, as a bar such as "8,300 of 10,000 calls used". Then you email them when they pass 80% of what their plan includes, so a big bill is never a surprise.

Before you start

  • An agreement with a metered entitlement, for example from Bill for API calls.
  • A test secret token in COMECERO_SECRET_TOKEN, used only on your server.
  • A public HTTPS address for webhooks. For local testing, a tunnel such as ngrok works.

Read usage so far on your server

Get usage this period so far answers what each metered entitlement has used, what is included and the price so far, worked out as the bill will be.

usage-so-far.js
const cache = new Map();

export async function usageSoFar(agreementId) {
  const hit = cache.get(agreementId);
  if (hit && Date.now() - hit.at < 5 * 60_000) return hit.value;

  const response = await fetch(`https://api.comecero.com/api/v1/usage/agreements/${encodeURIComponent(agreementId)}/current`, {
    headers: { Authorization: `Bearer ${process.env.COMECERO_SECRET_TOKEN}` },
  });
  if (!response.ok) throw new Error(`usage so far: ${response.status}`);
  const value = await response.json();
  cache.set(agreementId, { at: Date.now(), value });
  return value;
}

Keep the answer for a few minutes, as above. Comecero keeps it for 5 minutes anyway, and an account can make about 30 uncached usage reads a minute (more answer 429). Never call this from the browser: it needs your secret token.

Show the bar

Send your page only what it needs:

server.js
app.get('/account/usage', async (req, res) => {
  const period = await usageSoFar(`agr_${req.teamId}`);
  res.json({
    period_end: period.period_end,
    lines: period.entitlements.map((line) => ({
      name: line.metric_name,
      unit: line.unit_label,
      used: line.used,
      included: line.included,
      percent: line.percent,
    })),
  });
});
usage-bar.js
const { period_end, lines } = await fetch('/account/usage').then((r) => r.json());
for (const line of lines) {
  const percent = Math.min(line.percent ?? 0, 100);
  document.querySelector('#usage').insertAdjacentHTML('beforeend', `
    <p>${line.name}: ${line.used.toLocaleString()} of ${line.included.toLocaleString()} ${line.unit}s</p>
    <progress max="100" value="${percent}"></progress>`);
}
document.querySelector('#renews').textContent = `Resets on ${new Date(period_end).toLocaleDateString()}`;

percent can be over 100 when the customer is past what is included. The line's amount is the price so far, before prepaid credit. Show it only if your customers see prices.

If your customers use the Comecero customer account page, it already shows these meters on its Subscriptions page, without prices.

Get an alert at 80%

Create an event subscription for usage:threshold_reached. Set active to true: a new event subscription is off until you do.

Request
curl https://api.comecero.com/api/v1/event_subscriptions \
  -H "Authorization: Bearer $COMECERO_SECRET_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "usage:threshold_reached",
    "method": "http",
    "format": "json",
    "destination": "https://example.com/webhooks/comecero",
    "active": true
  }'

Comecero checks usage about once an hour. When a customer passes 50%, 80% or 100% of an included allowance, it sends the event once for that threshold and period. If usage jumps past two thresholds between checks, only the higher one is sent.

Email the customer

Check the signature first, as in the webhooks guide. Then act on the alert:

webhooks.js
app.post('/webhooks/comecero', express.raw({ type: 'application/json' }), async (req, res) => {
  if (!isFromComecero(req.body, req.get('x-comecero-signature'))) return res.sendStatus(401);
  res.sendStatus(200); // answer fast, then do the work

  const event = JSON.parse(req.body);
  if (event.type !== 'usage:threshold_reached') return;
  if (await alreadyHandled(event.event_id)) return;

  const alert = event.data;
  if (alert.threshold >= 80) {
    await emailTeam(alert.customer_id, {
      subject: `You have used ${alert.percent}% of your ${alert.metric_key} this month`,
      used: alert.used,
      included: alert.included,
      resets: alert.period_end,
    });
  }
});

customer_id is your own id for the customer, the one your app sends to the usage meter. The alert has ids and numbers only, no email address, so look the customer up in your own data.

Next steps

On this page