Skip to main content

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: Nak with backoff; NATS retries up to the stream's MaxDeliverCount.
  • On duplicate operatorMessageId: immediate Ack — 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',
};