> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.cloudraker.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.cloudraker.com/_mcp/server.

# Redact

`POST /v1/redact` takes a file. It returns a **new** file without the personal information. In a PDF, CloudRaker strips the text out of the content stream. It does not cover the text with a rectangle. Copy-paste and text search find nothing, because the text is gone. In audio, CloudRaker beeps or silences the words and rewrites the transcript.

## How it works

1. You send a **file** (a URL or a file id you already have) and, optionally, the `categories` that count as sensitive.
2. CloudRaker parses the document, locates every match, and rewrites the file destructively.
3. Routing follows the input's MIME type. Audio and video go to audio redaction (`style`). All other files go to document redaction (`mode`). If you send the parameter for the other medium, the API returns a `400`.
4. The call **holds until the run finishes**, up to `?wait=` seconds (default 60, max 120). At the cap, the call returns `202` with a run id instead of an error.
5. The run, its input, and the redacted output expire after the `ttl` (default 24 hours). To retain them, [keep](/paperwork/developers/runs#keep-a-run) them.

Redaction is destructive by design. The output is a new file. The original stays untouched. To remove the original, call `DELETE /v1/files/:id`, or let the run's `ttl` purge both files.

## Quickstart

The sample uses a blank IRS Form W-9, so the call works with no local files.

```bash title="curl"
curl -X POST https://api.cloudraker.com/v1/redact \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file": { "url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf", "name": "w9.pdf" },
    "categories": ["ssn", "ein"],
    "mode": "targeted"
  }'
```

```ts title="TypeScript"
const res = await fetch("https://api.cloudraker.com/v1/redact", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CLOUDRAKER_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    file: { url: "https://www.irs.gov/pub/irs-pdf/fw9.pdf", name: "w9.pdf" },
    categories: ["ssn", "ein"],
    mode: "targeted",
  }),
});

const run = await res.json();
console.log(run.status, run.output?.entities);
```

```python title="Python"
import os, requests

res = requests.post(
    "https://api.cloudraker.com/v1/redact",
    headers={"Authorization": f"Bearer {os.environ['CLOUDRAKER_API_KEY']}"},
    json={
        "file": {"url": "https://www.irs.gov/pub/irs-pdf/fw9.pdf", "name": "w9.pdf"},
        "categories": ["ssn", "ein"],
        "mode": "targeted",
    },
)

run = res.json()
print(run["status"], run.get("output", {}).get("entities"))
```

The TypeScript and Python samples use plain HTTP, so they run with no installed packages. This endpoint is also a top-level method on both [SDKs](/paperwork/developers/sdks) as of 0.3.0: `client.redact(…)`. That page has a full worked example.

## Example response

```json
{
  "object": "redact_run",
  "id": "rdr_01KYD72106R2BXBJRF537T1JSH",
  "status": "processed",
  "expiresAt": "2026-07-26T18:02:06.093Z",
  "statusUrl": "/v1/runs/rdr_01KYD72106R2BXBJRF537T1JSH",
  "files": [
    { "id": "a04d6597-4e34-4a99-94ea-964c289a4c68", "name": "w9.pdf", "status": "processed" }
  ],
  "file": { "id": "a04d6597-4e34-4a99-94ea-964c289a4c68", "name": "w9.pdf", "status": "processed" },
  "output": {
    "file": {
      "id": "c8c8437a-0dfd-4924-9358-c638fb0002e4",
      "name": "w9 (redacted).pdf",
      "url": "https://cdn.cloudraker.com/…/latest?token=…"
    },
    "files": [
      { "id": "c8c8437a-0dfd-4924-9358-c638fb0002e4", "name": "w9 (redacted).pdf", "url": "https://cdn.cloudraker.com/…/latest?token=…" }
    ],
    "entities": { "ein": 5, "ssn": 10 },
    "skipped": 0
  }
}
```

## Key fields

| Field             | What it is                                                                                                 |
| ----------------- | ---------------------------------------------------------------------------------------------------------- |
| `object`          | Always `redact_run`.                                                                                       |
| `id`              | The run id (`rdr_…`).                                                                                      |
| `status`          | `queued`, `processing`, `processed`, `failed`, `cancelled`, or `expired`.                                  |
| `files[]`         | The inputs, with per-file `status` and `error`. `file` is an alias for `files[0]`.                         |
| `output.files[]`  | One redacted file per input: `{id, name, url}`. `output.file` is an alias for the first.                   |
| `output.file.url` | Signed, time-limited download URL. `GET /v1/runs/:id/output/:name` is the stable alias for the same bytes. |
| `output.entities` | The count of redacted items per category, for example `{"ssn": 10, "ein": 5}`.                             |
| `output.skipped`  | The count of detections that CloudRaker could not remove. If the count is not zero, review the output.     |

CloudRaker registers the output file's bytes a moment after the run reports `processed`. In that window, `output.file` can arrive without its `url`, and the `/output/` alias can answer `404`. Re-fetch `GET /v1/runs/:id` to get the URL.

## Configuration

Every field is optional except `file`.

| Field          | Type                                  | What it does                                                                                                             |
| -------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `file`         | `{url, name?, processing?}` or `{id}` | **Required.** The document or recording to redact.                                                                       |
| `categories`   | array of strings, 1–50                | What counts as sensitive. Defaults cover names, government ids, addresses, phone, email, and date of birth.              |
| `instructions` | string, ≤ 4000 chars                  | Free-text additions ("also redact internal project codenames").                                                          |
| `mode`         | `targeted` \| `lines`                 | **Documents only.** `targeted` removes the matched span; `lines` removes the whole line containing it.                   |
| `style`        | `beep` \| `silence`                   | **Audio only.** How the removed speech sounds in the output.                                                             |
| `action`       | string                                | The id or slug of a [saved config](/paperwork/capabilities/actions). The API merges inline fields over the saved config. |
| `metadata`     | object                                | Your own key/values. The API echoes them back on the run body, not on webhook deliveries. Max 10 KB.                     |
| `webhook`      | `{url}` or `{id}`                     | Where to deliver terminal events. See [Webhooks](/paperwork/developers/webhooks).                                        |
| `ttl`          | integer seconds, 1–604800             | How long the run and its files live. Default 24 hours, max 7 days.                                                       |

If you send `style` on a PDF, or `mode` on audio, the API returns `400 invalid_request`. The error names the parameter that does not apply.

## Sync vs async

The behavior matches [extract](/paperwork/capabilities/extract#sync-vs-async). Calls are synchronous by default. `?wait=` accepts `0` to `120` seconds. At the cap, the API returns a `202` handle instead of a timeout error.

```json
{
  "object": "redact_run",
  "id": "rdr_01KYD72106R2BXBJRF537T1JSH",
  "status": "processing",
  "statusUrl": "/v1/runs/rdr_01KYD72106R2BXBJRF537T1JSH"
}
```

Long recordings and large scans normally land here. Pass `?wait=0` and poll, or use a webhook.

## Save it as a config

Redaction policy is usually organization-wide, not per call. Save the `categories`, `instructions`, and `mode` once in the redact config library. Then every call shrinks to `{"file": …, "action": "hr-offboarding"}`:

```bash
curl -X POST https://api.cloudraker.com/v1/redact/configs \
  -H "Authorization: Bearer $CLOUDRAKER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "HR offboarding",
    "mimeType": "application/pdf",
    "config": { "categories": ["ssn", "email", "phone"], "mode": "lines" }
  }'
```

`mimeType` picks the variant (document or audio) at save time. Only this library accepts it. `GET`, `PATCH` and `DELETE /v1/redact/configs/{idOrSlug}` do the rest. The flat `/v1/actions` routes remain as a deprecated alias. See [Saved configs](/paperwork/capabilities/actions).

## Next steps

#### [Saved configs](/capabilities/actions)

Save a redaction policy once and reference it by slug.

#### [Pipelines](/capabilities/pipeline)

Extract and redact the same file set in one call.

#### [Runs](/developers/runs)

Statuses, downloading outputs, TTL, and keeping a result.

#### [Errors](/developers/errors)

The error envelope and which failures are worth retrying.