5.7 KiB
OpenSpec Change: enterprise-delivery-tracking
- Change ID: enterprise-delivery-tracking
- Package:
@suaveplan/delivery-tracking - Category: universal
- Target path:
universal/email/delivery-tracking - Status: IN PROGRESS
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/delivery-tracking.md
Summary
Deliver @suaveplan/delivery-tracking on the genesis branch from scratch at enterprise quality. The package provides channel-agnostic delivery event tracking: a typed DeliveryEvent model covering all SMTP/webhook status transitions (queued, sent, delivered, bounced, complained, unsubscribed, opened, clicked), a DeliveryTracker facade with pluggable store adapters, webhook payload normalization for major ESP providers (generic interface — no provider SDK deps), bounce classification (hard/soft/transient), unsubscribe propagation to @suaveplan/suppression, and per-campaign/per-recipient analytics aggregation. Genesis adds structured errors, Zod schemas, OTel instrumentation, and property-based tests.
Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/error (workspace:^) |
DeliveryTrackingError; DLVR.* error codes |
@suaveplan/types (workspace:^) |
LoggerLike, Clock, EventBusLike, StorageLike |
@suaveplan/events (workspace:^) |
Delivery event propagation |
@suaveplan/suppression (workspace:^) |
Unsubscribe/bounce → suppression list write |
@suaveplan/crypto (workspace:^) |
HMAC-SHA256 webhook signature verification |
zod (catalog:forms) |
Schema validation |
@opentelemetry/api (catalog:observability) |
Spans + counters |
Dev deps: vitest, @vitest/coverage-istanbul, fast-check (all catalog:testing).
Public API surface
Core types
type DeliveryEventType =
| 'queued' | 'sent' | 'delivered' | 'deferred'
| 'bounced' | 'complained' | 'unsubscribed'
| 'opened' | 'clicked'
type BounceType = 'hard' | 'soft' | 'transient'
interface DeliveryEvent {
id: string
messageId: string
campaignId?: string
recipientEmail: string
type: DeliveryEventType
timestamp: Date
bounceType?: BounceType
bounceCode?: string
clickUrl?: string
userAgent?: string
ipAddress?: string
metadata: Record<string, unknown>
}
interface DeliveryAnalytics {
campaignId: string
total: number
sent: number
delivered: number
bounced: number
complained: number
unsubscribed: number
opened: number
clicked: number
deliveryRate: number
openRate: number
clickRate: number
bounceRate: number
}
DeliveryTracker
interface DeliveryTracker {
record(event: DeliveryEventInput): Promise<DeliveryEvent>
get(eventId: string): Promise<DeliveryEvent | undefined>
listByMessage(messageId: string): Promise<DeliveryEvent[]>
listByRecipient(email: string, options?: ListOptions): Promise<PaginatedResult<DeliveryEvent>>
analytics(campaignId: string): Promise<DeliveryAnalytics>
}
function createMemoryDeliveryTracker(options?: MemoryTrackerOptions): DeliveryTracker
Webhook normalizer
interface WebhookNormalizeOptions {
secret?: string // HMAC secret for signature verification
providerHint?: string // 'sendgrid' | 'mailgun' | 'ses' | 'postmark' | 'generic'
}
function normalizeWebhookPayload(
body: unknown,
headers: Record<string, string>,
options?: WebhookNormalizeOptions
): DeliveryEventInput[]
Error codes
type DeliveryTrackingErrorCode =
| 'DLVR_EVENT_NOT_FOUND'
| 'DLVR_INVALID_PAYLOAD'
| 'DLVR_SIGNATURE_FAILED'
| 'DLVR_STORE_FAILED'
| 'DLVR_ANALYTICS_FAILED'
Genesis additions
DeliveryTrackingErrorextendsSuaveplanErrorwith five stable codes.- Zod schemas for
DeliveryEvent,DeliveryAnalytics,WebhookNormalizeOptions, all provider payload shapes. - Suppression auto-propagation —
bounced(hard) andcomplainedevents automatically call@suaveplan/suppressionto add the recipient. - Webhook signature verification — constant-time HMAC via
@suaveplan/crypto; configurable clock-skew window. - OTel instrumentation — counter
delivery.event_totalwith attributedelivery.event_type; histogramdelivery.analytics_duration_ms. - Property-based tests — analytics rate calculations for arbitrary event sets.
File layout
universal/email/delivery-tracking/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/
index.ts
types/
common.ts
common.md
core/
delivery-tracker/
delivery-tracker.ts
delivery-tracker.md
delivery-tracker.types.ts
delivery-tracker.test.ts
index.ts
memory-tracker/
memory-tracker.ts
memory-tracker.md
memory-tracker.test.ts
index.ts
analytics/
analytics-aggregator/
analytics-aggregator.ts
analytics-aggregator.md
analytics-aggregator.test.ts
index.ts
webhook/
webhook-normalizer/
webhook-normalizer.ts
webhook-normalizer.md
webhook-normalizer.test.ts
index.ts
signature-verifier/
signature-verifier.ts
signature-verifier.md
signature-verifier.test.ts
index.ts
__benchmarks__/
delivery-tracking.bench.ts
tests/
integration.test.ts
property.test.ts
Impact
Downstream consumers: @suaveplan/campaigns, any server-side email dispatch service.
Workspace dep prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/suppression, @suaveplan/crypto, @suaveplan/pagination.