Activation approval state errors
When your organization turns on activation approval (maker-checker), every audience activation needs a second person’s sign-off before any hashed identifier leaves the platform: your submit enqueues a pending approval request, an owner or admin decides it, and your re-submit consumes it. TheACTIVATION_APPROVAL_* codes below fire when the approval you present no
longer matches a pending, matching, intact decision. None of them means the
ad network refused anything; the run never dispatches.
The approval lifecycle first
- Request.
POST /api/v1/cdp/audience/activate/:platformwithout anapproval_idrecords apendingapproval request instead of dispatching. - Decide. A different person with owner or admin role calls
POST /api/v1/cdp/audience/approvals/:id/decisionwithdecision: "approve" | "reject". - Consume. You re-submit the activation with the returned
approval_id; the approval is single-use and turnsconsumed.
- A decided approval stays decided. Once an approval is
approved,rejected, orconsumed, it can never be re-decided (ACTIVATION_APPROVAL_ALREADY_DECIDED), and a non-approvedapproval can never dispatch a run (ACTIVATION_APPROVAL_NOT_ACTIONABLE). - You cannot decide your own request. Segregation of duties: the person who asked for the activation must not be the person who signs it off. This applies to the requester even when they hold an owner or admin role.
- The approval is bound to the exact batch it was minted for. The
platform fingerprints the destination platform, audience, segment,
operation, and member set at request time. If the membership drifts
afterwards, the original decision is invalid for the new batch
(
ACTIVATION_APPROVAL_BATCH_CHANGED). - The approval is bound to the exact target. Platform, audience,
segment, and operation must match at consume time
(
ACTIVATION_APPROVAL_MISMATCH).
Read the envelope first
Every one of these failures arrives as the standard error envelope:meta.request_id, the approval id
(apr_...), and the activation run id if one exists. Every code below is
deterministic: backoff-retry never turns one into a success.
The six codes
ACTIVATION_APPROVAL_NOT_FOUND (404)
No approval row with that id exists in this organization: the id was
mistyped, it belongs to a different organization, or the request rolled off
the bounded approval queue. Re-enumerate the live queue with
GET /api/v1/cdp/audience/approvals and copy the id from there. If the
request genuinely no longer exists, mint a fresh one by re-submitting the
activation without an approval_id. Anything else, for example the id
appeared in the list a moment ago and now 404s, is an escalation: open a
ticket with meta.request_id and the approval id.
ACTIVATION_APPROVAL_NOT_ACTIONABLE (409)
The approval exists but is not approved; the message names the current
status. A consumed approval already dispatched its run and cannot be
replayed: if the audience must go out again, mint a fresh approval request.
A rejected one means an approver declined it: read the decision reason,
fix what was flagged, and submit a new request rather than retrying the old
id.
ACTIVATION_APPROVAL_ALREADY_DECIDED (409)
A decision was submitted for an approval that is no longer pending,
usually two approvers racing on the same request. The first decision wins
and stands. Re-read the approval from
GET /api/v1/cdp/audience/approvals and act on the status it now carries.
ACTIVATION_APPROVAL_SELF_DECISION (403)
You are both the requester and the decider. Maker-checker exists so that a
PII audience export always has two sets of eyes, so the control refuses the
decision even when you hold an owner or admin role. Route the approval to a
different owner or admin. If your organization has exactly one owner or
admin, invite a second approver first; the check itself cannot be waived
per request while the policy is on.
ACTIVATION_APPROVAL_BATCH_CHANGED (409)
The member batch you submitted differs from the one the approver signed
off on: rows were added or removed since the approval was minted, typically
because the segment recomputed or the audience was edited in between. The
protective check rejects the dispatch on purpose; an approval must never
cover a batch nobody reviewed. Re-issue the activation request with the
current members so a fresh approval is minted for the batch you actually
dispatch, then have it decided and consumed. Do not edit the activation or
campaign in place expecting the check to pass, and do not re-send the old
approval_id.
ACTIVATION_APPROVAL_MISMATCH (409)
The approval was granted for a different destination, segment, or
operation than the run you are submitting, for example an approval minted
for facebook / add presented to a google / remove run. Either
re-enumerate approvals and present the one that matches this exact
platform, audience, segment, and operation, or submit the run the approval
names.
The operator move per code
What never to do
- Do not replay the same approval id expecting success. Approvals are single-use and status-locked; a consumed or rejected id stays refused.
- Do not edit the activation or campaign in place to “fix” a
BATCH_CHANGED. The fingerprint compares your submitted batch against the approved one, so the protective check rejects the attempt; the only path is a fresh approval for the current batch. - Do not decide your own request from another session. The control keys on your user identity, not the session.
- Do not retry with backoff. These are deterministic 404/403/409 responses, not transient failures.
Worked envelopes and escalation payload
The five refuses you will see in the wild, verbatim from the API:meta.request_id from the envelope, the approval
id (apr_...), the activation run id from
GET /api/v1/cdp/audience/activations when a run exists, plus the
platform, segment, and operation of the run you attempted. That bundle is
sufficient to reconstruct the decision chain on our side.
Related
- Campaign and conference state-machine transition errors for the overlapping 409 family on campaign launches and approvals.
- Audience activation pipeline for the maker-checker flow end to end, including the preflight override interplay.