Your first redaction job.

A complete REST flow using Python requests. Upload, start processing, poll, and download. Use your own endpoint and Bearer key.

1. Configure your endpoint and key

Set REDACTTOOL_BASE_URL to https://redacttool.app and REDACTTOOL_API_KEY to your own Bearer key. Keep credentials on your server, never in frontend code.

Keys are session-bound and expire within 24 hours; regenerating a key revokes the previous one. Keep credentials on your server, never in frontend code, URLs, or prompts.

Run an example

export REDACTTOOL_BASE_URL="https://redacttool.app"
export REDACTTOOL_FILE="agreement.txt"
# Supply REDACTTOOL_API_KEY securely in your environment.
# The official endpoint uses HTTPS.

# Python REST (pip install requests)
python redact.py
# Node.js 22+
node redact.mjs
# cURL and jq
bash redact.sh
# Python MCP (pip install requests)
python mcp_redact.py

2. Run the full flow

Copyable example
"""RedactTool REST example. Supply your own endpoint and key.
No network activity occurs until this script is explicitly executed.
Requires: pip install requests
"""
import os
import time
import uuid
from pathlib import Path
from urllib.parse import urlparse
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

base = os.environ["REDACTTOOL_BASE_URL"].rstrip("/")
key = os.environ["REDACTTOOL_API_KEY"]
source = Path(os.environ.get("REDACTTOOL_FILE", "sample.txt"))
parsed = urlparse(base)
if parsed.scheme != "https" and parsed.hostname not in {"localhost", "127.0.0.1"} and os.environ.get("REDACTTOOL_ALLOW_HTTP_TRIAL") != "1":
    raise ValueError("Use HTTPS, or explicitly set REDACTTOOL_ALLOW_HTTP_TRIAL=1 for non-confidential trial files")
output = source.with_name(source.stem + "-redacted" + source.suffix)
if output.exists():
    raise FileExistsError(output)
session = requests.Session()
# Retry safe reads when an idle connection closes. Do not replay POST bodies.
retry = Retry(total=3, connect=3, read=3, backoff_factor=0.5,
              allowed_methods=frozenset({"GET"}), status_forcelist=(502, 503, 504))
for scheme in ("https://", "http://"):
    session.mount(scheme, HTTPAdapter(max_retries=retry))
session.headers["Authorization"] = "Bearer " + key

def checked(response):
    response.raise_for_status()
    return response.json()

with source.open("rb") as content:
    uploaded = checked(session.post(base + "/v1/files",
        files={"file": (source.name, content)}, timeout=(15, 120)))
# Keep this ID if you retry this same creation request.
request_id = uuid.uuid4().hex
print("Keep file_id and Idempotency-Key for recovery:", uploaded["file_id"], request_id)
job = checked(session.post(base + "/v1/redactions",
    headers={"Idempotency-Key": request_id},
    json={"file_id": uploaded["file_id"], "mode": "placeholder"},
    timeout=(15, 60)))
print("Job:", job["id"])
# A local wait timeout does not cancel server processing.
deadline = time.monotonic() + 3600
while job["status"] not in {"completed", "failed"}:
    if time.monotonic() >= deadline:
        raise TimeoutError("Resume polling this job ID; do not create a new job")
    time.sleep(5)
    job = checked(session.get(base + "/v1/redactions/" + job["id"],
        timeout=(15, 60)))
if job["status"] == "failed":
    raise RuntimeError(job.get("error") or "job_failed")
# Use a known same-origin path, never an arbitrary returned host.
with session.get(base + "/v1/redactions/" + job["id"] + "/file",
                 stream=True, timeout=(15, 120)) as result:
    result.raise_for_status()
    with output.open("xb") as saved:
        for chunk in result.iter_content(65536):
            saved.write(chunk)
print("Saved:", output)

3. Handle interruptions deliberately

Persist the file ID, job ID and idempotency key in your application. Retry the same job creation with the same file, mode and idempotency key. If local waiting ends, resume status polling rather than starting the file again.

This example is intentionally small: HTTP errors stop it. A production integration should add bounded backoff for 429 and transient failures, securely persist job state, and renew credentials when required.

Runnable examples

Node.js 22+ · cURL + jq · Python MCP · Live REST demo