Skip to main content
See also: Contact Policies concept and configuration for what this API powers, when to call it, and how it is configured.
Contact policies control how often and under what conditions a customer can be contacted. They enforce frequency caps, cooldowns, budget exhaustion, mutual exclusion, and other suppression rules during the filtering stage of a Decision Flow.
See the Contact Policies feature page for UI guidance and conceptual overview.

Base path


List contact policies

Returns a paginated list of contact policies, ordered by priority (highest first), then creation date (newest first).

Query parameters

Response 200


Create a contact policy

Creates a new contact policy. Requires the admin role.

Request body

Rule types and config shapes

Fourteen rule types are supported. Each has a specific config object shape:
Campaign-aware frequency_cap: withinCampaignId (when set) restricts the cap to summary rows whose campaignId matches that batch-run id — other campaigns and non-campaign sends are ignored. excludeCampaignIds drops impressions from the listed campaigns; rows with no campaign attached are still counted. campaignScoped: true marks the cap as per-campaign-run: it only applies while a batch campaign run is evaluating candidates and counts only impressions recorded within that run — real-time /recommend traffic skips the rule entirely (an explicit withinCampaignId still takes precedence over the current run’s id). Interaction history and summaries carry an additive nullable campaignId field populated when one is supplied at send time.
offer_category_cap matching: targetCategory is matched case-insensitively against the candidate’s Offer.category marketing string (with a fallback match on the taxonomy categoryId). The interaction-summary store denormalizes Offer.category onto each row, so the cap counts only contacts in the target category. Legacy summary rows written before that denormalization carry a NULL category and are not counted.
For time_window rules, the timezone value in config is validated against the IANA timezone database at write time. Invalid timezones are rejected with a 400 error.
For outcome_based rules, config.afterOutcome accepts either a non-empty string (legacy single-outcome form) or a non-empty array of strings (preferred). Empty values are rejected with outcome_based requires afterOutcome as a non-empty string or non-empty string array. config.suppressForDays must be >= 0. The Studio UI exposes a one-click Adverse Outcomes preset that fills the array with ["complaint", "unsubscribe", "hard_bounce", "spam_report"] for the standard 90-day compliance quarantine.

Example request

Response 201

Returns the created contact policy object.

Validation

All fields are validated via Zod schemas:
  • name: 1-255 characters, must be unique per tenant.
  • ruleType: Must be one of the fourteen enum values listed above. For customer_total_cap, config.maxTotal is required (non-negative number) and config.periodType (if set) must be one of daily, weekly, monthly, alltime. For offer_category_cap, config.targetCategory (non-empty string) and config.maxTotal (non-negative number) are required, and config.periodType follows the same enum. For frequency_cap, config.withinCampaignId (when set) must be a non-empty string, config.excludeCampaignIds (when set) must be a non-empty string array, and config.campaignScoped (when set) must be a boolean.
  • scope: Must be one of: global, offer, creative, channel, category, subcategory.
  • priority: Integer, 0-100.
  • config: Free-form JSON object (structural validation per rule type is not enforced by the schema).

Error codes


Update a contact policy

Updates an existing contact policy. Only provided fields are changed. Requires the admin role.

Request body

All fields from the create schema are accepted as optional, plus:

Example request

Response 200

Returns the updated contact policy object.

Delete a contact policy

Soft-deletes a contact policy by ID. The record is marked as deleted but retained in the database for audit purposes. The response includes warnings if the policy is still referenced by any Decision Flow’s draftConfig.

Query parameters

Response 200

This endpoint uses soft-delete — the record is not physically removed from the database. It is excluded from GET results by default. To include soft-deleted records, pass ?includeDeleted=true on the GET request.
The API checks all Decision Flows in the tenant for references to this policy ID in their draftConfig.stages.filter.contactPolicyIds array and returns warnings for any matches.

Error codes


Role requirements


Soft-delete and audit

Contact policies use soft-delete with audit snapshots. When a policy is deleted:
  1. The deletedAt timestamp is set (record is retained).
  2. An audit snapshot is captured with the full state before deletion.
  3. Ghost reference warnings are returned if the policy is still referenced by any Decision Flow.
  4. Any materialized suppressions written by this policy (see Suppressions) are removed, and the per-tenant policy cache is invalidated — the policy stops firing on the very next /recommend, not after a cache TTL. Deactivating a policy (PUT with status other than active) clears its suppressions the same way. As a backstop, decision-time suppression reads also ignore any row whose policy is no longer active.
Creates and updates invalidate the same cache, so a new or edited policy is enforced on the next /recommend immediately. Updates also create audit snapshots via auditedUpdate, incrementing the version field on each change. To include soft-deleted policies in GET responses, add ?includeDeleted=true to the query string.
Multi-scope policies are evaluated differently: global-scoped policies are evaluated in the Decision Flow’s Filter node, while entity-scoped policies (offer, channel, creative) are automatically evaluated per-candidate during the recommend pipeline.

Preview policy impact

Projects how a proposed frequency_cap-style policy would have affected recent traffic. Samples up to 1,000 active customers, counts matching interactions in the lookback window, and returns affected counts plus an optional segment breakdown.

Request body

Response 200

The Studio Contact Policies editor surfaces this endpoint as a Preview Impact button on the frequency_cap config panel. The button derives maxFrequency and periodDays from the largest cap on the form (daily → 1 day, weekly → 7, monthly → 30, total → 365).

Contact Policies

Learn more about configuring contact policies in the platform UI.