> ## 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.

# Retry forwarding

> Re-send an approved media buy whose forward did not complete, including one already marked terminal

`POST /api/v2/storefront/media-buy-approvals/{mediaBuyId}/retry-forward`

Re-attempts the upstream forward for a media buy you already approved. The buy keeps its `mediaBuyId` and your original approval — you are re-sending the same decision, not making a new one. Over MCP the same operation is `retry_forward_media_buy_approval`.

This is an API control. The retry action in Pending operations and in chat covers transient failures only; a buy a source refused outright routes to escalation there, not to a retry button.

## Which state is your buy in?

`forwardedAt` alone does not tell you. It is stamped both when a forward **succeeds** and when one is **terminalized**, so read it together with the buy's forwarding legs.

| State          | How to recognize it                                        | What to send              |
| -------------- | ---------------------------------------------------------- | ------------------------- |
| **Delivered**  | `forwardedAt` set, legs carry an `upstreamMediaBuyId`      | Nothing. The buy is live  |
| **Incomplete** | `forwardedAt` `null`                                       | `{}` — a plain retry      |
| **Terminal**   | `forwardedAt` set, legs are all failed with no upstream id | `{"forceTerminal": true}` |

Read the legs from [Get an approval](/v2/storefront/media-buy-approvals/tasks/get-approval) or the buy timeline before deciding. Forcing a retry at a delivered buy is refused rather than duplicated, but the error will not tell you the buy is healthy.

A terminal forward is stamped `forwardedAt` even though nothing was delivered. That stamp is what stops the background worker from re-attempting it, which is correct — re-sending an identical payload to a source that structurally refused it fails the same way every time. `forceTerminal` is the operator override for when the underlying cause has since been fixed.

## What `forceTerminal` cannot recover

<Warning>
  **Precondition failures.** A buy terminalized before any source was contacted — a paused storefront, a storefront not ready to transact, an unconfigured or unsupported settlement currency, an expired FX quote — records no forwarding leg at all. `forceTerminal` is refused on these with `400 VALIDATION_ERROR`, because there is no evidence the prior forward went unsent. Fix the storefront-level condition and have the buyer submit a new media buy.

  **Update approvals.** This control applies to `create` approvals. On an approval with `kind: "update"`, `forceTerminal` is ignored and the call returns the row unchanged with no `forwardOutcome`.

  **Rejected buys.** Only an entry in `approved` state can be re-forwarded. A buy you rejected has nothing to re-send.
</Warning>

## Before you force a retry

The platform refuses a forced retry when it has any record of an upstream booking, and it replays the original AdCP idempotency key so a source that honors it will not double-book. Neither protection is total: a source that partially wrote before failing — an ad server that created the order and then failed trafficking its line items — can leave an order upstream that we never recorded an id for, and not every third-party sales agent honors idempotency keys.

So check four things first:

1. **Look in the ad server.** Search for an existing order or line item for this buy. The platform's guard cannot see a booking it never recorded.
2. **Confirm the buyer still wants it.** Nothing on this path checks whether the buyer has since canceled or moved on. A forced retry can traffic a real order for a campaign nobody is expecting.
3. **Confirm the cause is actually fixed.** The identical payload is replayed. If the refusal reason still holds, the retry just fails again.
4. **Check the flight window.** See below.

Legitimate reasons to force a retry: a platform-side fix shipped; the product, placement, or creative was corrected; the format or size was enabled in the ad server; a credential or authorization landed after the fact; or the source's error was operational but got classified as a refusal.

## Flight dates and budget are replayed as submitted

A forced retry re-sends the payload the buyer originally submitted. Only `end_time` is refreshed from the live media buy — `start_time` and every package budget go out exactly as approved, and a cross-currency buy keeps its original FX snapshot.

Days after the original approval that matters:

* **Window partly elapsed** — the buy carries a start date in the past and its full original budget over fewer remaining days. Expect front-loaded pacing, and reconfirm with the buyer before forcing.
* **Window fully elapsed** — nothing refuses the retry. You would book a commitment that cannot deliver. Do not force it; the buy needs a re-issued flight, not a re-forward.

## Request

<CodeGroup>
  ```bash Retry an incomplete forward theme={null}
  curl -X POST \
    "https://api.interchange.io/api/v2/storefront/media-buy-approvals/mb_2026_q2_ctv/retry-forward" \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{}'
  ```

  ```bash Recover a terminal forward theme={null}
  curl -X POST \
    "https://api.interchange.io/api/v2/storefront/media-buy-approvals/mb_2026_q2_ctv/retry-forward" \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "forceTerminal": true,
      "reason": "Dimension-derivation fix shipped; no GAM order exists for this buy."
    }'
  ```
</CodeGroup>

## Parameters

| Field           | Type    | Required | Notes                                                                                                                                                                                          |
| --------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mediaBuyId`    | string  | Yes      | Path param — buyer-supplied media buy identifier (AdCP `media_buy_id`)                                                                                                                         |
| `forceTerminal` | boolean | No       | Defaults to `false`. Set `true` to re-forward a buy already stamped `forwardedAt`. Refused unless every source route is a failed, unsent leg                                                   |
| `reason`        | string  | No       | Up to 500 characters. Why you are re-sending. Recorded against the attempt, so a later reviewer can tell a considered recovery from a reflexive one. Strongly recommended with `forceTerminal` |

The body rejects unknown fields. `force_terminal` — the snake\_case spelling AdCP uses elsewhere — returns `400 VALIDATION_ERROR`.

## Response

Returns the approval-queue entry with the outcome of the dispatch this call triggered.

```json theme={null}
{
  "id": "42",
  "kind": "create",
  "storefrontId": "1234",
  "mediaBuyId": "mb_2026_q2_ctv",
  "buyerCustomerId": 8801,
  "submittedPayload": {
    "media_buy_id": "mb_2026_q2_ctv",
    "total_budget": 50000,
    "currency": "USD"
  },
  "status": "approved",
  "reviewedBy": "5567",
  "reviewedAt": "2026-06-02T12:30:00Z",
  "reviewerNotes": null,
  "forwardedAt": null,
  "createdAt": "2026-06-02T10:00:00Z",
  "updatedAt": "2026-06-02T12:35:00Z",
  "forwardOutcome": "deferred",
  "forwardResult": []
}
```

Read `forwardOutcome` to know what happened:

| Value                 | Meaning                                                                    |
| --------------------- | -------------------------------------------------------------------------- |
| `deferred`            | The retry was accepted and is dispatching                                  |
| `all_completed`       | Every source accepted the buy                                              |
| `all_submitted`       | Every source accepted asynchronously and is still finishing. Not a failure |
| `partial`             | Some sources accepted it                                                   |
| `all_failed`          | No source accepted it                                                      |
| `precondition_failed` | The forward could not be attempted                                         |

The endpoint responds before the upstream call finishes, so `deferred` is the normal outcome on a successful retry rather than a sign of trouble. Poll [Get an approval](/v2/storefront/media-buy-approvals/tasks/get-approval) for the settled result; the claim lease is 60 seconds, so wait at least that long before treating a still-unsettled buy as stuck.

<Note>
  If the response has **no** `forwardOutcome`, nothing was re-dispatched — do not read it as a fresh success. Three causes: the buy was already forwarded; it is terminal and you did not send `forceTerminal`; or the background worker holds the lease and owns the attempt. In the last case `forwardedAt` is `null`.
</Note>

## Errors

All are `400 VALIDATION_ERROR` unless noted. Use `error.field` to tell them apart.

| `field`         | Cause                                                     | What to do                                                                                      |
| --------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `status`        | The entry is not `approved`                               | Nothing to retry — check the decision first                                                     |
| `forceTerminal` | A source already carries an upstream media-buy or task id | The buy reached upstream. Reconcile or cancel it in the ad server; never force                  |
| `forceTerminal` | No failed source route is persisted                       | Usually a precondition failure. Fix the storefront-level condition; the buy must be resubmitted |
| —               | Unknown field in the body                                 | Check spelling — the body is strict                                                             |

`404 NOT_FOUND` — no approval entry with that media buy id exists for your storefront.

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

## Related

<CardGroup cols={2}>
  <Card title="Get an approval" href="/v2/storefront/media-buy-approvals/tasks/get-approval" icon="magnifying-glass">
    Check `forwardedAt` and the forwarding legs before and after a retry
  </Card>

  <Card title="Decide an approval" href="/v2/storefront/media-buy-approvals/tasks/decide-approval" icon="gavel">
    The original approve or reject decision
  </Card>

  <Card title="Media-buy approval tasks" href="/v2/storefront/media-buy-approvals/tasks" icon="list-check">
    All media-buy approval operations
  </Card>

  <Card title="Media-buy approvals overview" href="/v2/storefront/media-buy-approvals/overview" icon="clipboard-check">
    Queue lifecycle and concepts
  </Card>
</CardGroup>
