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

# Decide a review

> Approve or reject a pending creative review

`POST /api/v2/storefront/creative-reviews/{creativeId}/decide`

Records a terminal decision on one pending creative review version. Only
`pending → approved` and `pending → rejected` transitions are allowed —
revoking a previously-approved creative is a separate gesture and `revoked` is
not a valid value here. Approval wakes the separate per-media-buy delivery
ledger; it does not itself report the creative live. Your reviewer note travels
back to the buyer in their `sync_creatives` result. Send the `contentDigest`
from the exact queue row you reviewed as `expected_content_digest`. If the
buyer revises that creative before your decision lands, the API returns a
conflict instead of applying your decision to the revised content. Send
`sourceTrust.reviewVersionDigest` as `expected_review_version_digest` to bind
the decision to the same source-trust classification and content version.

## Request

<CodeGroup>
  ```bash Approve theme={null}
  curl -X POST \
    "https://api.apostra.com/api/v2/storefront/creative-reviews/review%3A42/decide" \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "status": "approved",
      "expected_content_digest": "0db36cf6c4bfb43123aee1c3eaf5ee965e1c34c9df606b02a56c72a226ad097a",
      "expected_review_version_digest": "4fde1cc1468051f05492bd3694348ea7f6dbb37bbbe5ff35967c4d4d149d9a4e"
    }'
  ```

  ```bash Reject theme={null}
  curl -X POST \
    "https://api.apostra.com/api/v2/storefront/creative-reviews/review%3A42/decide" \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "status": "rejected",
      "expected_content_digest": "0db36cf6c4bfb43123aee1c3eaf5ee965e1c34c9df606b02a56c72a226ad097a",
      "expected_review_version_digest": "4fde1cc1468051f05492bd3694348ea7f6dbb37bbbe5ff35967c4d4d149d9a4e",
      "reviewer_notes": "Logo lockup violates publisher brand-safety guidelines"
    }'
  ```
</CodeGroup>

## Parameters

| Field                            | Type   | Required                   | Notes                                                                                                                                                                                                                                                               |
| -------------------------------- | ------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `creativeId`                     | string | Yes                        | Path param — use the `reviewRef` returned by the queue. Bare values always select an AdCP creative id, including when numeric                                                                                                                                       |
| `status`                         | enum   | Yes                        | Decision to record. `approved` or `rejected` only — `revoked` is not allowed here                                                                                                                                                                                   |
| `expected_content_digest`        | string | Required after revision    | Exact `contentDigest` from the review version shown to the operator. During the mixed-client rollout a legacy caller may omit it only while content has never advanced and the frozen work item still matches. Revised or mismatched content returns `409 CONFLICT` |
| `expected_review_version_digest` | string | Recommended during rollout | Exact immutable `sourceTrust.reviewVersionDigest` from the same row. It fences the source-trust evidence as well as content. Current Pages send it; an N-1 client may omit it only at the rolling-upgrade boundary.                                                 |
| `reviewer_notes`                 | string | No                         | Free-text note visible to the buyer in their `sync_creatives` follow-up. Max 2000 chars                                                                                                                                                                             |

## Response

```json theme={null}
{
  "id": "42",
  "reviewRef": "review:42",
  "storefrontId": "1234",
  "creativeId": "cr_abc123",
  "contentDigest": "0db36cf6c4bfb43123aee1c3eaf5ee965e1c34c9df606b02a56c72a226ad097a",
  "mediaBuyId": "mb_2026_q2_ctv",
  "buyerCustomerId": 8801,
  "submittedPayload": {
    "creative_id": "cr_abc123",
    "format": "video",
    "media_url": "https://cdn.acme-brand.example/spots/launch-30s.mp4",
    "account": { "account_id": "acme-buying" },
    "buyer_ref": "launch-q2-sync",
    "context": { "context_id": "ctx_launch_q2" }
  },
  "status": "rejected",
  "reviewedBy": "5567",
  "reviewedAt": "2026-06-02T13:45:00Z",
  "reviewerNotes": "Logo lockup violates publisher brand-safety guidelines",
  "createdAt": "2026-06-02T11:00:00Z",
  "updatedAt": "2026-06-02T13:45:00Z"
}
```

Returns the decided `CreativeReviewResponse`. Its `submittedPayload` preserves
the creative fields plus any request-level `account`, `buyer_ref`, and
`context`; buyer webhook configuration is removed. `reviewedBy`, `reviewedAt`,
and `updatedAt` are stamped at decision time. Deciding a row that is already
decided is rejected — re-deciding is not a no-op.

## Errors

* `400 VALIDATION_ERROR` — missing/invalid `status`, `expected_content_digest`, or `expected_review_version_digest`.
* `409 CONFLICT` — either expected version is stale, `expected_content_digest` was omitted after the content generation advanced, the creative id matches multiple buyers, or the review content/work-item authorization changed. No decision is recorded. Reload the review and decide with its latest `reviewRef`, `contentDigest`, and `sourceTrust.reviewVersionDigest`.
* `404 NOT_FOUND` — no creative review row with that id exists for your storefront.

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Get a review" href="/v2/storefront/creative-reviews/tasks/get-review" icon="magnifying-glass">
    Inspect the creative before deciding
  </Card>

  <Card title="List reviews" href="/v2/storefront/creative-reviews/tasks/list-reviews" icon="list">
    See the pending queue
  </Card>

  <Card title="Creative review tasks" href="/v2/storefront/creative-reviews/tasks" icon="list-check">
    All creative-review operations
  </Card>

  <Card title="Creative reviews overview" href="/v2/storefront/creative-reviews/overview" icon="image">
    Queue lifecycle and concepts
  </Card>
</CardGroup>
