Audience Constraints

Audience constraints in AdCP are the plan-level include and exclude rules a governance agent evaluates seller targeting against during check_governance, where exclusions take precedence over inclusions.

Audience constraints in AdCP are the include and exclude rules a buyer attaches to a campaign plan so a governance agent can judge whether the seller’s targeting is allowed. The object has two properties, include and exclude, and no others.

governance/audience-constraints.json is the schema. include: “The combined targeting should satisfy at least one inclusion criterion”, with no grouping and no nesting. exclude: “Exclusions take precedence over inclusions.” Each is an array of audience selectors with minItems: 1, one of the two must be present (minProperties: 1), and additionalProperties is false, so an empty array and an empty object both fail. The schema also states what exclude is for: “protected groups, vulnerable communities, regulatory restrictions, or brand safety.”

On a plan the property is named audience, not audience_constraints. The $ref in governance/sync-plans-request.json hangs off a plan property called audience, and a plan item is additionalProperties: false, so the other name — used in docs/building/concepts/security-model.mdx, “Plan-level audience_constraints and governance policies are machine-enforceable expressions of human judgment” — produces a rejected sync_plans call rather than a warning. Only plan_id, brand, objectives, budget and flight are required, so a plan can arrive carrying no constraint at all; the schema never says what an absent audience means, and unconstrained is the only reading available to an agent.

The four audience selector shapes

core/audience-selector.json is a oneOf over four variants, discriminated by type (signal or description), then by value_type.

typevalue_typePayloadWhat it expresses
signalbinaryvalue: true | falseThe user matches the signal, or does not
signalcategoricalvalues: string[], minItems: 1Specific values of the signal
signalnumericmin_value, max_value, optional, inclusiveA range
descriptiondescription (1–2000 chars), optional categoryNatural language, when no signal exists

Every signal variant demands signal_ref or signal_id through an anyOf, and signal_id is deprecated: true, “retained for compatibility with older clients”. New code points at a signal definition.

The description variant covers a buyer with an intent and no catalogue entry. Its schema examples are “likely EV buyers”, “high net worth individuals” and “vulnerable communities”, and “vulnerable communities” is also one of the phrases the container’s own exclude description reserves for exclusions.

Where the schema stops checking

include is an unordered OR, and the schema defines no AND and no nesting. A buyer who wants both in-market for EVs and a household-income floor writes two selectors with no operator to join them, and the plan records the union rather than the intersection.

Inside a selector, validation is loose. All four variants are additionalProperties: true and the numeric branch requires only type and value_type, so an unknown key, a selector with no bounds at all, and min_value: 100 with max_value: 5 all validate — the last because the ordering rule is stated in prose only. Nothing downstream can distinguish a mistyped range field from an absent one. The oneOf rejects only a mistyped value_type, which matches no branch.

A binary selector with value: false inside exclude also validates. value means “whether to include (true) or exclude (false) users matching this signal”, and the container’s rule is already “must not overlap with these”, so the payload carries two negations and 3.1.13 does not say which wins.

How a governance agent reads the constraints

A check_governance request carries plan_id and the action under review, never the constraint object itself, so the agent has to resolve the stored plan before it can judge anything. The schema splits an intent check (tool plus payload, proposed) from an execution check (planned_delivery, committed). core/planned-delivery.json has audience_targeting, built from the same audience-selector.json the buyer wrote the constraint in, and governance agents “MUST use this for bias/fairness validation and SHOULD ignore audience_summary”. Both sides speak one type, and the governance agent diffs them.

The reply cannot name what failed. A findings[] item is additionalProperties: false, policy_id is optional, and category_id is agent-internal with callers told they “MUST NOT pattern-match” it. No field in the object can name the offending selector. mode on the response is optional and x-status: experimental, and in audit mode “the governance agent always returns approved”, so an approved verdict does not indicate whether the agent was enforcing.

min_audience_size and restricted_attributes

min_audience_size is a k-anonymity floor on the plan, and an agent that cannot size the audience must “produce a finding with reduced confidence rather than silently passing”.

The floor “Applies to the estimated combined (intersection) audience when multiple criteria are used”, while include is an OR and a union is never smaller than its parts. The specification does not reconcile the two: “criteria” may mean the seller’s combined targeting or the plan’s inclusion list, and nothing in 3.1.13 states which reading applies.

restricted_attributes bars personal-data categories from targeting anywhere in the campaign, from a ten-value enum cited to “EU DSA Article 26 compliance”; that enum belongs to policy categories.

AAMP’s audience plan

AAMP has no field of that name. What it has is an AudiencePlan, specified in buyer-agent/docs/api/audience_plan_wire_format.md, marked “Status: Active” and implemented in Pydantic on both sides. The AAMP column below describes that document rather than a published schema.

AdCP audience (3.1.13)AAMP AudiencePlan
Rolesinclude, excludeprimary, constraints, extensions, exclusions
Set algebraundefined; include is ORintersect / union / subtract, per role
Ref provenancenoneexplicit / resolved / inferred, required
Confidencenone0–1, MUST be null when explicit
Consent metadatanonecompliance_context on agentic refs
Content hashnonecanonicalised SHA-256 over the four roles
Capability negotiationnone400 audience_plan_unsupported, per-path
Published schemayes, $id-pinned, strictno
k-anonymity floormin_audience_sizeprose only, in a draft
Restricted-attribute enum10 valuesnone

On the wire, DealBookingRequest.audience_plan is {"type": "object", "additionalProperties": true}, described as “open object; typed model lands with the audience-plan bead”, and iab-agentic-primitives v0.5.0 carries no audience schema file at all. The two protocols are set against each other in AdCP vs AAMP.

An audience selector is not AdCP’s Audience object, the buyer-uploaded list registered through sync_audiences; the two are set out side by side under audience vs signal.