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

# AXP Unpublish API: Take Optimized Pages Off the Edge

> Remove curated AXP pages from the edge on demand and retire their rows so no automated re-stage, redeploy, or rollback republishes them.

## Overview

The AXP Unpublish API takes a list of paths off the edge for a registered AXP site and retires their optimized-page rows in a way that no automated writer will undo. It is designed for compliance-driven takedowns that must land faster than the weekly optimization cadence.

Each request runs immediately: for every path, the deterministic edge key is deleted and the row is marked as archived with a terminal `api_takedown` reason. Once a row is in that state, a re-stage, a redeploy of an older version, a rollback, or a render job will leave it retired — the page's content is preserved, but nothing brings it back live automatically.

Bringing a taken-down page back live still works from the Scrunch dashboard. The terminal state only binds automated writers; a human with `brand:update` can re-activate the page through **Optimized pages → Edit**, which clears the removal reason.

***

## When to use the AXP Unpublish API

Use this API when you need to:

* Remove a curated page from the edge immediately, for example after pulling a regulated page from your origin.
* Guarantee a removal survives your normal optimization and deploy cycles without a human watching for silent resurrection.
* Wire a delete surface from a notify or content-management integration through one shared front door.

For interactive removals, use **Optimized pages → Archive** in the Scrunch dashboard. That archive is deliberately reversible: an optimizer re-stage or a redeploy of an older version may bring the page back. Use this API when you need the removal to stick.

<Note>
  Scope is curated (Adaptive) pages only. Universal Optimization has its own removal path and is not affected by this endpoint.
</Note>

***

## Endpoint

| Method | Path                                                     | Purpose                                    |
| ------ | -------------------------------------------------------- | ------------------------------------------ |
| POST   | `/v2/orchestration/unpublish/{brand_id}/sites/{site_id}` | Unpublish a list of paths on one AXP site. |

The endpoint requires a bearer token with the `configure` scope. See [Authentication](/getting-started/authentication).

### Engine version requirement

The endpoint requires the target site to be on **AXP engine version 2.1.0 or later**. On 2.1.0+, a page is served if and only if its deterministic edge key exists, so deleting the key is the unpublish. On earlier engines a page stops being served only when the next version deploy rebuilds the routing manifest without its path.

A request against a site below 2.1.0 is refused with a `409` before any row is touched. This is deliberate: retiring the row anyway would return success for a takedown whose serving half lands at an unknown later time.

***

## Request

| Field   | Type       | Required | Description                                                                                                                     |
| ------- | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `paths` | `string[]` | Yes      | Site-relative paths to unpublish. Each entry is trimmed and given a leading slash if missing. Capped at **100 paths** per call. |

### Path normalization and deduplication

Before processing, the API normalizes each path:

* Surrounding whitespace is stripped.
* A leading `/` is added when missing.
* Duplicates after normalization are dropped, preserving input order.

Case is preserved. `/Home` and `/home` are two separate rows and are processed separately.

***

## Response

The response is an object with a `results` array holding one entry per requested path, in the order the paths were sent.

| Field               | Type             | Description                                                      |
| ------------------- | ---------------- | ---------------------------------------------------------------- |
| `results[].path`    | `string`         | The normalized path this outcome applies to.                     |
| `results[].outcome` | `string`         | What happened to this path. See the table below.                 |
| `results[].detail`  | `string \| null` | Human-readable context for outcomes a caller may need to act on. |

### Outcome values

| Outcome               | Meaning                                                                                                                                                                                                                                             |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unpublished`         | The edge key is confirmed gone and the row is terminally retired.                                                                                                                                                                                   |
| `already_unpublished` | The page was already down. Reported distinctly so a retry reads as a retry, but it is a success — the page is not being served.                                                                                                                     |
| `not_found`           | No curated page at that path on this site. The desired end state already holds.                                                                                                                                                                     |
| `failed`              | The edge did not confirm the delete, so the page may still be served. The row is deliberately left untouched, and the daily reconcile sweep retries the delete. Retry the path once the reconcile has run or contact support if it keeps recurring. |
| `skipped`             | Another active page owns the same deterministic edge key (for example `/About-Us` and `/about-us` map to one edge object). Deleting the key would take a live page down. Rename one of the paths in the Scrunch dashboard, then retry.              |

The request itself succeeds — HTTP `200` — as long as it was well-formed and the site is eligible. Failures on individual paths surface as `failed` outcomes in the body so a caller with a 100-path list is not blocked by one bad entry.

***

## Example: unpublish curated pages

```bash theme={null}
curl -X POST \
  "https://api.scrunchai.com/v2/orchestration/unpublish/1234/sites/01JEXAMPLE00000000000000" \
  -H "Authorization: Bearer $SCRUNCH_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "paths": [
      "/promo/2024-rates",
      "/legal/old-disclosure",
      "/blog/removed-post"
    ]
  }'
```

**Response:**

```json theme={null}
{
  "results": [
    { "path": "/promo/2024-rates", "outcome": "unpublished", "detail": null },
    { "path": "/legal/old-disclosure", "outcome": "already_unpublished", "detail": null },
    { "path": "/blog/removed-post", "outcome": "not_found", "detail": null }
  ]
}
```

***

## Errors

| Status | When                                                                                                                        |
| ------ | --------------------------------------------------------------------------------------------------------------------------- |
| `400`  | The site has no CDN platform configured.                                                                                    |
| `404`  | No site with the supplied ULID exists for the brand.                                                                        |
| `409`  | The site's AXP engine is below version 2.1.0, so on-demand unpublish is not supported. No pages are changed.                |
| `422`  | The request body failed validation (empty `paths`, more than 100 entries, empty path, or path longer than 2048 characters). |

***

## Limits and behavior

* Maximum **100 paths** per request. Split larger batches into multiple calls. The endpoint is idempotent, so re-sending overlapping batches is harmless.
* Each path must be 1–2048 characters after normalization.
* The row is committed as retired **before** the edge delete is issued. If the process crashes between the two, the reconcile sweep converges the state by finishing the edge delete on its next run.
* A page that was previously archived from the dashboard is upgraded to a terminal takedown on the first `unpublished` outcome. From then on, automated writers stop being able to bring it back.
* Lifting a takedown is a human action: edit the page in the Scrunch dashboard and set its status back to **Active**. That clears the terminal state and re-enables the normal reversible archive semantics.

***

## Best practices

* **Retry idempotently.** The endpoint is safe to retry after network errors or partial failures; already-taken-down paths report `already_unpublished` and are counted as success.
* **Act on `failed` and `skipped` results.** These are the only outcomes a caller usually needs to do something about — retry `failed` after the reconcile sweep has run, and resolve the key collision behind `skipped` before retrying.
* **Confirm engine eligibility before wiring a compliance flow.** If your sites are still on AXP 2.0.0, this endpoint will 409 uniformly. Contact your Customer Success representative to migrate a site to 2.1.0.
* **Audit through activity log.** Each successful takedown records a `CONTENT_TAKEN_DOWN` activity entry tagged with the API key, so removals can be traced back to the caller.
