# POST /v1/content/:containerId/reject (/docs/api/reference/approval/reject-content)



<Endpoint method="POST" path="/v1/content/:containerId/reject" phase="1" />

Flips `approvalStatus` to `rejected` and stamps the reviewer. Rejected containers can't be scheduled. To produce a new take, call [`POST /v1/projects/:projectId/content`](/docs/api/reference/content/slideshow-builder) with a fresh `hook`.

## Path parameters [#path-parameters]

<Parameters
  rows="[
  { name: 'containerId', type: 'string (uuid)', required: true, description: 'The container to reject.' },
]"
/>

## Body [#body]

<Parameters
  title="Body"
  rows="[
  { name: 'reason', type: 'string', required: true, description: 'Rejection note, 1–1024 chars. Stored on the container for audit.' },
]"
/>

## Request [#request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```sh title="terminal"
    curl -X POST https://api.layers.com/v1/content/{containerId}/reject \
      -H "Authorization: Bearer $LAYERS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "reason": "Hook is off-brand; want something punchier." }'
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts title="reject.ts"
    const res = await fetch(
      `https://api.layers.com/v1/content/${containerId}/reject`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.LAYERS_API_KEY}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ reason: 'Needs a new angle.' }),
      },
    );
    const result = await res.json();
    ```
  </Tab>

  <Tab value="Python">
    ```py title="reject.py"
    import os, httpx

    r = httpx.post(
        f"https://api.layers.com/v1/content/{container_id}/reject",
        headers={
            "Authorization": f"Bearer {os.environ[\'LAYERS_API_KEY\']}",
            "Content-Type": "application/json",
        },
        json={"reason": "Off-brand hook."},
    )
    result = r.json()
    ```
  </Tab>
</Tabs>

## Responses [#responses]

<Response status="200" description="Rejected.">
  ```json
  {
    "id": "cnt_7d18b9a1...",
    "approvalStatus": "rejected",
    "rejectedAt": "2026-04-18T09:44:50Z",
    "rejectedBy": "api_key_c2037bb9...",
    "reason": "Hook is off-brand; want something punchier."
  }
  ```
</Response>

<Response status="409" description="Container is already rejected or approved.">
  ```json
  {
    "error": {
      "code": "CONFLICT",
      "message": "Container is already rejected.",
      "requestId": "req_...",
      "details": { "approvalStatus": "rejected" }
    }
  }
  ```
</Response>

<Response status="409" description="Container does not require approval (e.g. project policy is `auto_approve`, or the first-N gate self-disabled before this container was generated).">
  ```json
  {
    "error": {
      "code": "CONFLICT",
      "message": "Container does not require approval.",
      "requestId": "req_...",
      "details": { "approvalStatus": "not_required" }
    }
  }
  ```

  `approvalStatus: "not_required"` means the container was never gated — there is nothing to reject. If you want to suppress an ungated container, skip [`schedule`](/docs/api/reference/publishing/schedule-content) entirely or cancel it after the fact.
</Response>

<Response status="422" description="Missing or empty `reason` field (Zod schema validation).">
  ```json
  {
    "error": {
      "code": "VALIDATION",
      "message": "Invalid body.",
      "requestId": "req_...",
      "details": { "issues": [{ "path": ["reason"], "message": "Required" }] }
    }
  }
  ```
</Response>

<Response status="422" description="State precondition failed — container exists but its generation hasn't completed yet.">
  ```json
  {
    "error": {
      "code": "VALIDATION",
      "message": "Container status must be completed to reject.",
      "requestId": "req_...",
      "details": { "status": "processing" }
    }
  }
  ```

  The `VALIDATION` 422 has two distinct shapes: **body-level** (missing/empty `reason` — the schema rejects it before any DB read; `details.issues[]` carries Zod paths) and **state-level** (container exists and is approval-eligible but `status !== "completed"`; `details.status` carries the live status). Both share the same code; differentiate via `details`.
</Response>

## Notes [#notes]

* **Approved → rejected isn't supported.** Once a container is `approved`, rejecting it is a `CONFLICT`. If approved content is wrong, cancel any scheduled posts and call generate again with a fresh `hook`.
* **Rejection counts toward the first-N threshold same as approval.** Both are "this container moved out of pending". The counter measures throughput of human review, not net positive verdicts — so once `firstN` containers have hit any terminal state (approved or rejected), the gate self-disables and subsequent generations land at `not_required`.
* **To produce a new take**, call [`POST /v1/projects/:projectId/content`](/docs/api/reference/content/slideshow-builder) with a fresh `hook`.

## Errors [#errors]

| Code         | When                                              |
| ------------ | ------------------------------------------------- |
| `CONFLICT`   | Container is already rejected or approved.        |
| `VALIDATION` | Container isn't `completed`; or missing `reason`. |
| `NOT_FOUND`  | Container id not in this org.                     |

## See also [#see-also]

* [Approve a container](/docs/api/reference/approval/approve-content)
* [Generate a new container](/docs/api/reference/content/slideshow-builder)
* [The approval policy](/docs/api/reference/approval/policy)
