# 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 mirroring `CampaignSuppressionReason`. - `CreateSuppressionSchema` — body validator with `channel: "email"` default and optional `source` / `metadata`. - `SuppressionQuerySchema` — list filters with coerced pagination. - `SuppressionCheckQuerySchema` — required `channel` + `channelValue`. - `SuppressionIdParamSchema` — `:id` path param. - 3 response schemas: `SuppressionResponseSchema`, `SuppressionListResponseSchema`, `SuppressionCheckResponseSchema`. ## Usage: Basic ```ts import { CreateSuppressionSchema } from "@suaveplan/api-plugin-campaigns"; const parsed = CreateSuppressionSchema.parse(await c.req.json()); await store.add({ tenantId, ...parsed }); ``` ## Usage: Advanced ```ts 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.