Billing Service — Sync Contract
Status: populated Owner: Platform Engineering Last updated: 2026-04-18
1. Per-Aggregate Policy
| Aggregate | Policy | Rationale |
|---|---|---|
BillingEvent | server_authoritative | Immutable after insert; clients read via usage query API |
PricingTable | server_authoritative | Admin-managed; versioned history maintained server-side |
OperatorCost | server_authoritative | Admin-managed |
Invoice | server_authoritative | Cron-generated; download via presigned S3 URL |
UsageSummary | server_authoritative | Internal; not exposed as a resource |
2. Offline Behavior
No offline client path exists for billing. All reads require a live HTTP request to the REST API.
3. Event Consumption Ordering
billing.events messages for the same accountId may be delivered out of DLR timestamp order (NATS does not guarantee per-account ordering). Usage summaries are UPSERT-based and commutative — ordering does not affect correctness of aggregation. billing_events rows are independent (no inter-row ordering dependency).
4. Replay Tolerance
billing.eventsredelivery:ON CONFLICT (message_id) DO NOTHINGensures idempotent ingestion.billing.invoice.generatedpublication: if cron crashes after PDF upload but before NATS publish, re-running the cron detectsstatus=DRAFTand re-publishes.- Usage summary idempotency: NATS dedup window (5 min default) +
ON CONFLICT DO UPDATE SET ... = ... + EXCLUDED...is NOT idempotent by default — guarded bybilling_eventsunique constraint ensuring eachmessageIdproduces exactly one usage summary update.