Files
skills/docs/api-stack/api/api-plugin-campaigns/src/routes/suppression/schemas.md
2026-05-29 08:20:56 +01:00

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 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

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.