DLR Processor — API Contracts
Status: populated Owner: Platform Engineering Last updated: 2026-04-18 Companion: EVENT_SCHEMAS · SECURITY_MODEL
1. HTTP API Surface
The DLR Processor exposes no customer-facing HTTP API. It is event-driven. The following infrastructure endpoints are exposed internally only (not routed through Kong).
GET /health
Kubernetes liveness probe.
Response 200 OK:
{ "status": "ok" }
Response 503 Service Unavailable (NATS or PG unhealthy):
{ "status": "error", "details": { "nats": "disconnected", "postgres": "ok" } }
GET /ready
Kubernetes readiness probe. Returns 200 only when NATS consumer is active and PG connection pool is healthy.
GET /metrics
Prometheus text-format metrics endpoint (scrape target for internal monitoring).
2. NATS — Consumed Subjects
sms.dlr.inbound
Produced by: smpp-connector
Stream: SMS_DLR
Consumer: dlr-processor (durable, AckExplicit, MaxConcurrency 10)
Message Schema (JSON):
interface InboundDlrEvent {
eventId: string; // UUIDv4 — message-level dedup
operatorMessageId: string; // Carrier-assigned message ID (max 64 chars)
operatorId: string; // UUIDv4 of operator record
stat: string; // Raw SMPP stat or carrier status string
errorCode?: string; // Carrier error code (optional)
deliveredAt: string; // ISO-8601 UTC timestamp
sourceAddr?: string; // Originating address (optional)
destAddr?: string; // Destination address (optional)
rawPayload?: Record<string, unknown>; // Full carrier payload (passthrough)
}
Ack Semantics:
AckExplicit— message is only acked after both DB write and NATS publish complete atomically (transactional outbox pattern).- On processing error:
Nakwith backoff; NATS retries up to the stream'sMaxDeliverCount. - On duplicate
operatorMessageId: immediateAck— idempotent no-op.
3. NATS — Published Subjects
billing.events
Published per correlated, terminal DLR (status: DELIVERED, FAILED, UNDELIVERED, EXPIRED, REJECTED).
Schema:
interface BillingEvent {
eventId: string; // UUIDv4
eventType: 'DLR_TERMINAL';
messageId: string; // UUIDv4
accountId: string; // UUIDv4
dlrStatus: DlrStatus;
segmentCount: number;
operatorId: string;
occurredAt: string; // ISO-8601 UTC
}
webhook.dispatch
Published per correlated DLR (all DlrStatus values including UNKNOWN).
Schema:
interface WebhookDispatchEvent {
eventId: string; // UUIDv4
accountId: string; // UUIDv4
messageId: string; // UUIDv4
dlrStatus: DlrStatus;
to: string; // E.164 destination phone
operatorId: string;
occurredAt: string; // ISO-8601 UTC
metadata?: Record<string, string>;
}
sms.dlr.unmatched
Published when operatorMessageId does not resolve to any known sms_messages row.
Schema:
interface DlrUnmatchedEvent {
eventId: string;
operatorMessageId: string;
operatorId: string;
rawStat: string;
receivedAt: string;
orphanId: string; // UUID of dlr.orphaned_receipts row
}
4. Consumer Configuration (NATS JetStream)
const consumerConfig = {
name: 'dlr-processor',
durable_name: 'dlr-processor',
ack_policy: AckPolicy.Explicit,
ack_wait: 30_000_000_000, // 30s in nanoseconds
max_ack_pending: 10, // MaxConcurrency 10
deliver_policy: DeliverPolicy.All,
filter_subject: 'sms.dlr.inbound',
};