Idempotency. One event, once.
The Tracking API stores each messageId + timestamp pair once. Give the same real-world event the same id every time, and retries, duplicate webhooks and re-run jobs never count it twice.
Generate deterministic ids
Derive the id from the real event, not at random: purchase:<order number>, trial_started:<account id>. If an event can be emitted twice, send the business time (when the payment was received) as timestamp, not “now”.
messageId is at most 100 printable ASCII characters. Without one the server derives it from a hash of the content, and the server SDK gives a missing id a random UUID; do not rely on either for business events.
import { deterministicMessageId } from '@metrics/node';
deterministicMessageId('purchase_completed', order.id); // one purchase per order
deterministicMessageId('subscription_renewed', sub.id, period.start.toISOString());The answer to a duplicate
- A duplicate counts as accepted (
200, withinaccepted) and is stored once. - Do not resend a rejected message (
rejected[]) unchanged; it is rejected again. - On 5xx and network errors, retry with exponential backoff and the same
messageId. 429(rate_limited) is the rate limit, and nothing of the batch was stored: wait the number of seconds in theRetry-Afterheader, then resend the same batch with the samemessageIds.
Events that must not be lost
Write sign-ups, purchases and renewals in the same database transaction as the business change: @metrics/node/outbox-pg adds the event to the outbox with ON CONFLICT (message_id) DO NOTHING, and the dispatcher does the rest.