3.2 KiB
Suppression route schemas
Purpose
The suppression-route schemas describe the OpenAPI contract for the
/suppression route tree. The package's local
CampaignSuppressionStore interface is multi-channel — keyed on the
(tenantId, channel, channelValue) triple — so the schemas surface
channel (free-string with "email" default) and channelValue
rather than the email-only channelType shape used by the upstream
port. This keeps the same routes reusable for SMS, push, and future
delivery channels without a v2 endpoint family.
The schemas back four endpoints: list (paginated), create (single
entry, defends against duplicates at the store), delete (by id), and
check (point lookup by channel + channelValue). The check endpoint
is read-only and idempotent, and is intended to be hit on every
outgoing-message decision in a sender pipeline.
Features
SuppressionReasonSchema— 5-member enum mirroringCampaignSuppressionReason.CreateSuppressionSchema— body validator withchannel: "email"default and optionalsource/metadata.SuppressionQuerySchema— list filters with coerced pagination.SuppressionCheckQuerySchema— requiredchannel+channelValue.SuppressionIdParamSchema—:idpath param.- 3 response schemas:
SuppressionResponseSchema,SuppressionListResponseSchema,SuppressionCheckResponseSchema.
Usage: Basic
import { CreateSuppressionSchema } from "@suaveplan/api-plugin-campaigns";
const parsed = CreateSuppressionSchema.parse(await c.req.json());
await store.add({ tenantId, ...parsed });
Usage: Advanced
import {
SuppressionCheckQuerySchema,
SuppressionCheckResponseSchema,
} from "@suaveplan/api-plugin-campaigns";
const q = SuppressionCheckQuerySchema.parse({
channel: c.req.query("channel"),
channelValue: c.req.query("channelValue"),
});
const entry = await store.check(tenantId, q.channel, q.channelValue);
return c.json(
SuppressionCheckResponseSchema.parse({
suppressed: entry !== null,
entry,
}),
200
);
API Reference
CreateSuppressionSchema,SuppressionQuerySchema,SuppressionCheckQuerySchema,SuppressionIdParamSchema— input validators.SuppressionResponseSchema,SuppressionListResponseSchema,SuppressionCheckResponseSchema— response shapes.type CreateSuppressionBody,SuppressionQueryInput— inferred TS types.
Implementation Notes
The channel field defaults to "email" rather than enumerating
known channels because the suppression store is intentionally
channel-agnostic (a tenant may opt into SMS or push channels without
schema changes here). The trade-off: the OpenAPI consumer sees a
free-string rather than a closed enum. The enforcement boundary moves
to the recipient delivery pipeline, which always knows its channel
identifier before consulting suppression.
metadata is z.record(z.string(), z.unknown()) so adapters can
attach provider-specific context (bounce subcodes, complaint headers,
…) without a schema migration. The schema is strict-on-input only
for fields it owns; per-channel extensions live inside metadata and
are validated at the integration boundary, not here.