> ## Documentation Index
> Fetch the complete documentation index at: https://docs.interchange.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Creative reviews

> Review the queue of buyer-submitted creatives and approve or reject pending ones

When a buyer submits creatives to your storefront, each one lands in your creative review queue as a `pending` row. You inspect the submitted creative payload and record a terminal decision: `approved` or `rejected`. Approved creatives are forwarded to the underlying sales agent verbatim; rejected ones carry your reviewer note back to the buyer.

For when this queue is used — the `creativeApproval` setting, the lowest-risk "review everything" posture, and how you find out something is waiting — see [Reviewing buyer transactions](/v2/storefront/approvals/overview). For **how** a creative is judged against your rules — the rendered-image check and the deterministic locked-asset fidelity guarantees that hold an exact-fidelity product, logo, or cover for human review when it isn't deterministically preserved — see [Acceptance policy](/v2/concepts/acceptance-policy#how-creatives-are-evaluated-against-your-policy).

A review moves through `pending → approved` or `pending → rejected`. The `revoked` state — pulling a previously-approved creative — is a separate gesture and cannot be set on the decide endpoint. Each creative is identified by the AdCP `creativeId` the buyer supplied at submit time.

## Buyer status and callbacks

Buyer agents submit creatives with AdCP `sync_creatives`. In manual-review mode the first response can return `pending_review` for each queued creative:

```json theme={null}
{
  "creatives": [
    {
      "creative_id": "cr_abc123",
      "action": "created",
      "status": "pending_review"
    }
  ]
}
```

If the buyer calls `sync_creatives` again for a creative that has already been decided, the storefront returns the current decision in the per-creative status: `approved` with `action: "updated"`, or `rejected` with `action: "failed"` and the reviewer note as `message` when one was supplied.

Buyers can attach AdCP push-notification config to the original `sync_creatives` request:

```json theme={null}
{
  "creatives": [
    {
      "creative_id": "cr_abc123",
      "format": "video",
      "media_url": "https://cdn.acme-brand.example/spots/launch-30s.mp4"
    }
  ],
  "push_notification_config": {
    "url": "https://buyer.example.com/adcp/tasks",
    "token": "buyer-echo-token",
    "authentication": {
      "schemes": ["Bearer"],
      "credentials": "buyer-webhook-token"
    }
  }
}
```

Use `push_notification_config` for canonical AdCP requests. The storefront also accepts `pushNotificationConfig` from clients that pass camelCase JSON through a REST/MCP proxy. Callback URLs must be public HTTPS endpoints; localhost, private-network, link-local, metadata, and internal hostnames are rejected. Supported authentication schemes are `Bearer` and `HMAC-SHA256`.

When the operator approves or rejects the creative, the buyer webhook receives a signed ADCP task event with `status: "completed"` and a `result.creatives[]` entry whose status is `approved` or `rejected`. Rejected webhook results use `action: "failed"` and include the reviewer note as `message` when present.

### Per-creative webhook outcomes

The task envelope is always `status: "completed"` — the per-creative outcome lives in `result.creatives[]` as an `action` and `status` pair. **Read both fields:** `action: "failed"` on its own does not mean the operator declined the creative.

| Outcome                             | `action`  | `status`   | Other fields                                                                                                    |
| ----------------------------------- | --------- | ---------- | --------------------------------------------------------------------------------------------------------------- |
| Approved and delivered to inventory | `updated` | `approved` | —                                                                                                               |
| Operator rejected                   | `failed`  | `rejected` | `message`: reviewer note, when supplied                                                                         |
| Approved but could not be delivered | `failed`  | `approved` | `errors[].code`: `delivery_failed`; `message`: "Creative was approved but could not be delivered to inventory." |

`action: "failed"` with `status: "approved"` means the operator approved the creative but the storefront could not deliver it to the underlying inventory source after bounded retries — the creative is **not** live. Treat it as an actionable delivery failure (re-submit, or contact the operator), distinct from a content rejection: the discriminator is `errors[].code: "delivery_failed"` together with `status: "approved"`. The inventory source is never named, and a permanent failure and a retry-deadline failure are indistinguishable on the wire — both surface as this row. A forward that reached at least one source is reported as the approved row, not a delivery failure.

<Note>
  The creative review row returned to storefront operators omits `push_notification_config` and `pushNotificationConfig` from `submittedPayload` so webhook credentials are not exposed in the queue UI or REST responses.
</Note>

All examples use the storefront base URL:

```
https://api.interchange.io/api/v2/storefront
```

Authenticate every request with `Authorization: Bearer $SCOPE3_API_KEY`.

## Task reference

<CardGroup cols={2}>
  <Card title="List reviews" href="/v2/storefront/creative-reviews/tasks/list-reviews" icon="list">
    `GET /creative-reviews` — the review queue, newest first
  </Card>

  <Card title="Get a review" href="/v2/storefront/creative-reviews/tasks/get-review" icon="magnifying-glass">
    `GET /creative-reviews/{creativeId}` — one creative review row
  </Card>

  <Card title="Decide a review" href="/v2/storefront/creative-reviews/tasks/decide-review" icon="gavel">
    `POST /creative-reviews/{creativeId}/decide` — approve or reject
  </Card>
</CardGroup>
