Overview

The billing decisions nobody talks about

Retries never double-charge#

Every usage event carries an idempotencyKey. The same key twice means billed once.

await recordUsage({
  idempotencyKey: req.headers['idempotency-key'],
  endpoint: '/tools/search',
  status: 'completed',
})
// same idempotencyKey again → 200 OK, not billed twice

The guarantee lives in a UNIQUE constraint at the database level, enforced by catching the P2002 violation. It is not a SELECT before INSERT, which would leave a race window open under concurrency.

Partial failures are not billed by default#

A call that dies mid-way is recorded with status: "partial" and kept out of Stripe unless you say otherwise.

await recordUsage({
  idempotencyKey,
  endpoint: '/tools/search',
  status: 'partial', // client disconnected mid-call
})
// logged for audit, excluded from billing by default

Streaming bills once, at the end#

One usage event per completed stream, not one per token. Accumulate units across chunks and send a single event when the stream finishes.

stream.on('end', () =>
  recordUsage({ idempotencyKey, endpoint: '/tools/search', status: 'completed' })
)
// interrupted mid-stream → status: 'partial', same rule as above

Where these came from#

These were not designed upfront. They emerged from a real implementation problem: deciding what to do when Stripe confirmation arrives after the tool has already run. The write-up on Dev.to covers the full reasoning, including the exchange with an engineering lead at AppSignal who validated the approach.

Related: how billing works, known trade-offs.