API reference
Redacted API
The Redacted API lets you run data-broker exposure scans, file verified removals, and read removal status programmatically — the same operations the dashboard and our MCP agents use. It is organized around predictable, resource-oriented URLs, accepts JSON request bodies, returns JSON responses, and uses standard HTTP verbs, status codes, and authentication.
Base URL
https://app.redacted.example/api/v1
All requests must be made over HTTPS and authenticated with a Bearer token. Every endpoint is tenant-scoped and enforced at the database layer — a credential can only ever read or act on the subject it was issued for. Personal data (PII) is never returned in list or detail responses; evidence artifacts are fetched separately through short-lived signed URLs.
Don't have a key yet? Talk to us about API access →
curl https://app.redacted.example/api/v1/status \
-H "Authorization: Bearer rdk_8f3c2a1b9d4e6f70_4d9f8e7a6b5c..."{
"exposureCount": 38,
"runningCases": 4,
"verifiedRemovedCount": 12,
"blockers": [],
"statusBreakdown": {
"found": 22,
"request_sent": 4,
"verified_removed": 12
},
"nextScanAt": "2026-07-01T09:00:00.000Z"
}Authentication
The API authenticates every request with a Bearer token in the Authorization header. A missing or invalid credential returns 401 Unauthorized — there is no unauthenticated access to any resource.
API keys
Partner API keys look like rdk_<keyId>_<secret>. The keyId is a 16-byte hex identifier and the secret is a 32-byte hex value. Only a SHA-256 hash of the secret is stored — the full token is shown exactly once when the key is created, so store it somewhere safe. Treat it like a password: it carries the scopes it was granted and can act on your tenant's data.
Other credential types
Two additional Bearer formats are accepted on the same header. JWKS-verified OAuth JWTs are used by the MCP server and external agents (Claude, ChatGPT); the token's scopes are intersected with the underlying grant. The web dashboard uses an Auth.js session cookie instead of a Bearer token. For server-to-server integrations, use an API key.
Never embed an API key in client-side code or a public repository. Keys belong on your server, loaded from an environment variable or secret manager.
curl https://app.redacted.example/api/v1/exposures \
-H "Authorization: Bearer $REDACTED_API_KEY"# No Authorization header{
"error": "Unauthenticated.",
"code": "UNAUTHENTICATED"
}Errors
Redacted uses conventional HTTP status codes. Errors return a consistent JSON body with a human-readable error, a stable machine-readable code, and an optional detail. Internal stack traces and database messages are never exposed.
| Status | Code | Meaning |
|---|---|---|
| 200 / 201 / 202 | OK / Created / Accepted | The request succeeded. |
| 400 | BAD_REQUEST | Malformed JSON or an invalid path parameter. |
| 401 | UNAUTHENTICATED | Missing or invalid credential. |
| 401 | CONFIRMATION_TOKEN_REQUIRED | A high-risk action needs a confirmation token. |
| 403 | FORBIDDEN | Authenticated, but not allowed to act on this resource. |
| 403 | INSUFFICIENT_SCOPE | The credential lacks a required scope. |
| 422 | VALIDATION_ERROR | The body or query failed schema validation. |
| 500 | INTERNAL_ERROR | An unexpected server error. |
| 502 | WORKER_UNAVAILABLE | The scan / removal worker could not be reached. |
curl https://app.redacted.example/api/v1/scans \
-X POST \
-H "Authorization: Bearer $REDACTED_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tiers": ["oops"] }'{
"error": "Policy check failed.",
"code": "INSUFFICIENT_SCOPE",
"detail": "missing scope: privacy.scan.write"
}Scopes & permissions
Authorization is scope-based. Each credential carries an explicit set of scopes, and every handler asserts the required scope before it runs any query. Below are the scopes the API recognizes.
privacy.status.readRead exposure counts, removal status, and exposure lists/details.privacy.scan.writeStart discovery scans and prepare removal campaigns.privacy.removal.submitSubmit a prepared campaign (files removal requests). High-risk.privacy.profile.writeUpsert non-sensitive identifiers (email, phone, alias…).privacy.evidence.readGenerate signed URLs for before/after evidence. High-risk.privacy.escalation.writeCreate escalation packets for non-compliant brokers.privacy.drop.assistAssist drop / opt-out flows that require interactive steps.High-risk write actions (submitting a campaign, writing sensitive profile fields, creating an escalation) additionally require an active mandate — the subject's signed authorization — and a short-lived confirmation token.
// Every handler runs assertCan(actor, action, ctx)
// before touching data. A credential without the
// required scope gets 403 INSUFFICIENT_SCOPE — the
// query never executes.
// Web sessions receive implicit scopes by role:
// consumer → read + scan + submit + profile + evidence
// admin → the full consumer setPagination
List endpoints use limit / offset pagination. Responses include a pagination object with the effective limit, offset, and the total count of matching records, so you can page through the full set.
Query parameters
limitintegeroptional- Number of records to return. Defaults to 20, clamped to a maximum of 100.
offsetintegeroptional- Number of records to skip. Defaults to 0.
curl "https://app.redacted.example/api/v1/exposures?limit=20&offset=40" \
-H "Authorization: Bearer $REDACTED_API_KEY"{
"exposures": [ /* … */ ],
"pagination": {
"limit": 20,
"offset": 40,
"total": 138
}
}Core resources
Status
A single summary of the subject's privacy-removal state: how many exposures exist, how many cases are running, how many removals are verified, any blockers, a status breakdown, and when the next scan is scheduled. Counts and IDs only — never PII.
Query parameters
subjectIduuidoptional- Required for admin actors; ignored for consumer actors (their session subject is used).
blockersbooleanoptional- Include blocker details. Defaults to
true.
curl https://app.redacted.example/api/v1/status \
-H "Authorization: Bearer $REDACTED_API_KEY"{
"exposureCount": 38,
"runningCases": 4,
"verifiedRemovedCount": 12,
"blockers": [
{ "kind": "needs_user_action", "count": 1 }
],
"statusBreakdown": {
"found": 22,
"request_sent": 4,
"needs_user_action": 1,
"verified_removed": 12
},
"nextScanAt": "2026-07-01T09:00:00.000Z"
}Exposures
An exposure is a confirmed match of the subject's data on a broker surface. List them with current removal status and a suggested next action, or fetch one for its full removal history. Raw PII ciphertext is never returned — use the evidence endpoint for screenshots.
Query parameters
statusenumoptional- Filter by removal status:
found,request_sent,broker_confirmed,verified_removed,not_found,blocked,needs_user_action,legal_exception,escalated. limitintegeroptional- Default 20, max 100.
offsetintegeroptional- Default 0.
subjectIduuidoptional- Admin only; consumers use their session subject.
Returns a single exposure with its broker and surface info, evidence hash, profile URL, and the full status progression of its removal.
curl "https://app.redacted.example/api/v1/exposures?status=found&limit=20" \
-H "Authorization: Bearer $REDACTED_API_KEY"{
"exposures": [
{
"id": "9b1f…c2",
"broker": "Acme Data LLC",
"domain": "acmedata.com",
"profileUrl": "https://acmedata.com/p/…",
"confidence": 0.92,
"status": "found",
"firstSeen": "2026-05-02T12:00:00.000Z",
"lastSeen": "2026-06-15T12:00:00.000Z",
"nextAction": "Review and confirm this is your profile…"
}
],
"pagination": { "limit": 20, "offset": 0, "total": 38 }
}curl https://app.redacted.example/api/v1/exposures/9b1f…c2 \
-H "Authorization: Bearer $REDACTED_API_KEY"Scans
Start a discovery scan. The scan runs asynchronously in the removal worker, so the endpoint returns 202 Accepted immediately with a case and workflow id you can poll via status. Requires an active mandate with scan consent.
Body parameters
tiersinteger[]optional- Broker tiers to scan. Defaults to
[1](highest-priority brokers). subjectIduuidoptional- Admin only; consumers use their session subject.
curl https://app.redacted.example/api/v1/scans \
-X POST \
-H "Authorization: Bearer $REDACTED_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tiers": [1, 2] }'{
"caseId": "c4a7…e1",
"workflowId": "wf_3f9b…",
"status": "running",
"estimatedNextUpdate": "2026-06-17T12:15:00.000Z"
}Campaigns
A campaign bundles approved exposures into a set of removal requests. Preparing a campaign is a safe, reversible draft; submitting it is the high-risk step that actually files requests with brokers.
Prepares a draft campaign from approved exposures. Files nothing yet. Requires mandate consent of delete or opt_out.
Body parameters
exposureIdsuuid[]optional- Exposures to include. If omitted, all approved hits are included.
subjectIduuidoptional- Admin only; consumers use their session subject.
Submits a prepared draft — transitions it to running and enqueues the removal workflow. This is a high-risk write: it requires an active mandate and a valid confirmation token. The campaign id acts as the idempotency key, so re-submitting is safe.
Body parameters
confirmationTokenstringrequired- A short-lived token (min 12 chars) from POST /v1/confirmations.
Lists campaigns for the subject, most recent first.
curl https://app.redacted.example/api/v1/campaigns \
-X POST \
-H "Authorization: Bearer $REDACTED_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "exposureIds": ["9b1f…c2"] }'{
"draftCampaignId": "ca_71d2…",
"requests": [
{ "broker": "Acme Data LLC", "method": "form" }
],
"requiresConfirmation": true,
"summary": { "brokerCount": 1, "fieldCount": 4 }
}curl https://app.redacted.example/api/v1/campaigns/ca_71d2…/submit \
-X POST \
-H "Authorization: Bearer $REDACTED_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "confirmationToken": "ct_9f8e7a6b…" }'{
"caseId": "c4a7…e1",
"workflowId": "wf_55ad…",
"status": "running",
"submittedCount": 1,
"blockedCount": 0
}Confirmations
High-risk actions require a short-lived confirmation token, scoped to one action on one object. The raw token is returned once and only its hash is stored — lose it and you simply request another. Tokens default to a 15-minute TTL.
Body parameters
actionenumrequired- One of
removal.submit,escalation.write,profile.write.sensitive. objectTypestringrequired- The kind of object being acted on, e.g.
campaign. objectIduuidrequired- The object being acted on.
ttlSecondsintegeroptional- Token lifetime. Defaults to 900, max 3600.
curl https://app.redacted.example/api/v1/confirmations \
-X POST \
-H "Authorization: Bearer $REDACTED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "removal.submit",
"objectType": "campaign",
"objectId": "ca_71d2…"
}'{
"confirmationToken": "ct_9f8e7a6b5c4d…",
"action": "removal.submit",
"expiresAt": "2026-06-17T12:15:00.000Z"
}Profiles
Read non-PII profile metadata, or upsert a non-sensitive identifier. The API never returns decrypted PII — only metadata such as jurisdiction, risk class, mandate state, and which kinds of identifier exist. Values are encrypted before they are written and never logged.
Returns the subject summary: subjectId, jurisdiction, riskClass, activeMandate, and identifierKinds.
Upserts a non-sensitive identifier. Sensitive fields like address, date of birth, or identity-document details are not accepted here — they go through a separate sensitive-write flow that requires mandate consent and a confirmation token.
Body parameters
kindenumrequired- One of
email,phone,alias,username,employer,relative. valuestringrequired- 1–512 characters. Encrypted at rest.
isCurrentbooleanoptional- Whether this is a current identifier. Defaults to
true.
curl https://app.redacted.example/api/v1/profiles \
-H "Authorization: Bearer $REDACTED_API_KEY"{
"subjectId": "su_4f2a…",
"jurisdiction": "US-CA",
"riskClass": "standard",
"activeMandate": true,
"identifierKinds": ["email", "phone", "alias"]
}curl https://app.redacted.example/api/v1/profiles \
-X PATCH \
-H "Authorization: Bearer $REDACTED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"kind": "email",
"value": "[email protected]"
}'Evidence
Returns a short-lived signed URL (15-minute TTL) for a before/after evidence artifact — the screenshots that prove a removal. Screenshots may contain PII, so this endpoint requires the dedicated privacy.evidence.read scope and every access is audit-logged.
Query parameters
typeenumoptional- Which artifact to sign:
before(default) orafter.
curl "https://app.redacted.example/api/v1/evidence/9b1f…c2?type=after" \
-H "Authorization: Bearer $REDACTED_API_KEY"{
"url": "https://spaces.example/evidence/…?X-Amz-Signature=…",
"type": "after",
"expiresAt": "2026-06-17T12:15:00.000Z"
}