Skip to main content

Analytics Service — Event Schemas

Status: populated Owner: Platform Engineering Last updated: 2026-04-18 Companion: APPLICATION_LOGIC · DATA_MODEL

1. Consumed Events

This service publishes no events. It is a pure subscriber.

SubjectStreamProducerDurable ConsumerPurpose
billing.eventsBILLING_EVENTSbilling-serviceanlyt-billing-consumerCost + volume aggregation
sms.dlr.inboundSMS_DLRdlr-processoranlyt-dlr-consumerDelivery status + latency aggregation

billing.eventssms.billed.v1 (consumed)

interface SmsBilledEvent {
schemaVersion: '1';
eventId: string; // UUIDv4 — used for dedup
messageId: string;
accountId: string;
tenantId: string;
operatorId: string;
segmentCount: number;
costPerSegment: string; // decimal string
totalCost: string; // decimal string
currency: string; // ISO 4217
status: 'BILLED' | 'REFUNDED';
billedAt: string; // ISO 8601
}

Fields used by analytics: eventId, accountId, operatorId, segmentCount, totalCost, status, billedAt.


sms.dlr.inbound.v1 (consumed)

interface DlrInboundEvent {
schemaVersion: '1';
eventId: string;
messageId: string;
operatorId: string;
accountId: string;
deliveryStatus: 'DELIVERED' | 'FAILED' | 'UNDELIVERABLE' | 'PENDING';
errorCode: string | null;
latencyMs: number; // ms between message submission and DLR receipt
tpsSnapshot: number; // operator TPS at time of DLR
receivedAt: string; // ISO 8601
}

Fields used: eventId, operatorId, accountId, deliveryStatus, latencyMs, tpsSnapshot, receivedAt.


2. Idempotency Guarantee

Both consumers use the eventId field (UUIDv4 from producer) to deduplicate in anlyt.processed_events. NATS Nats-Msg-Id header is also asserted equal to eventId in validation.

3. Backfill

If analytics data is lost (e.g. PG partition dropped) and source events are still within NATS stream retention (7 d for BILLING_EVENTS, 3 d for SMS_DLR), a backfill by seeking the durable consumer to a prior sequence number is possible. Backfill idempotency is guaranteed by processed_events — already-processed events are silently skipped.

4. Schema Evolution

  • Analytics service is a pure consumer — it must tolerate additive changes to upstream schemas without deployment.
  • Destructive changes (field removal, type change) require coordination: analytics updates first (ignores new field), then producer changes, then analytics deploys to use new field.