API reference · v1
Redaction as an API call
Upload a document, read what Northdocs found, decide, apply and download the redacted file. Plain JSON over HTTPS, bearer keys, hosted in the EU.
- Base URL
- https://northdocs.com/api/v1
- Auth
- Bearer API key
- Rate limit
- 120 req/min per key
- Hosting
- EU only
Introduction
The Northdocs API is a REST API with predictable resource URLs, JSON bodies and standard HTTP status codes. Every path in this reference is relative to the base URL.
Documents are stored and processed in the EU, exactly as when they are uploaded in the app. Each call runs with the role of the member who created the key, so a reviewer key can decide detections but cannot upload or apply.
https://northdocs.com/api/v1Authentication
Authenticate with an API key in the Authorization header. Admins and the security role create keys in Settings, API keys. A key is shown once: store it in your secret manager straight away.
Authorization: Bearer bd_live_3f9c...e21a
Keys start with bd_live_. Never ship one in a browser or mobile app.
A key stops working when it is revoked or when the member who created it leaves the organization. Calls then answer 401 or 403.
curl "https://northdocs.com/api/v1/documents" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"Scopes
Each key carries scopes. A call without the scope it needs answers 403. Give each integration the smallest set it needs.
documents:readList documents and read status, detections, text and downloads. Also reads cases and jobs.documents:writeUpload documents, decide or reclassify detections and apply. Also creates cases.cases:readList and read cases.cases:writeCreate cases.jobs:readList recent processing jobs.outbound_guardText sanitizing and the Outbound Guard endpoints used by the Outlook add-in.
Quick start
Four calls take a PDF from upload to redacted download. The script on the right is the whole flow in bash with curl and jq.
- 1
Upload the file. Northdocs extracts text, runs OCR where needed and detects personal data.
- 2
Poll until the status is ready_for_review, or pass resultUrl on upload and wait for the callback.
- 3
Apply. acceptAll accepts every pending detection first; decide single detections before this call to keep some.
- 4
Download the redacted PDF once the status is completed.
# 1. Upload
DOC=$(curl -s -X POST "https://northdocs.com/api/v1/documents" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" \
-F "file=@contract.pdf" | jq -r .id)
# 2. Wait for review
until [ "$(curl -s "https://northdocs.com/api/v1/documents/$DOC" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" | jq -r .status)" = "ready_for_review" ]; do
sleep 3
done
# 3. Accept every finding and redact
curl -s -X POST "https://northdocs.com/api/v1/documents/$DOC/apply" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"redact","acceptAll":true}'
# 4. Download when completed
curl -s "https://northdocs.com/api/v1/documents/$DOC/download" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" -o contract-redacted.pdfDocument lifecycle
A document moves through these statuses. Poll GET /documents/{id} or subscribe to the document.completed webhook.
uploaded- Stored, waiting for a worker.
extracting- Reading text and running OCR on scanned pages.
detecting- Finding personal data.
ready_for_review- Detections are ready to read and decide.
applying- Burning redactions into the file and verifying the result.
completed- The redacted output is ready to download.
failed- Processing stopped. The error field says why.
/documentsUpload a document
Uploads a file and starts processing. Send multipart/form-data with the file in the file field. PDF, Office documents, images, e-mail and plain text are accepted.
Scopedocuments:write
Parameters
filefileformrequiredThe file to process.
caseIdstringformThe case id.
resultUrlurlformPublic HTTPS URL that receives a signed POST when the document completes.
redirectUrlurlformWhere the in-app review sends the user afterwards. Must be on the app origin.
curl -X POST "https://northdocs.com/api/v1/documents" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" \
-F "file=@contract.pdf" \
-F "caseId=case_7Hq2"{
"id": "doc_9fK3a",
"status": "uploaded"
}/documentsList documents
Documents the key owner can open, newest first. Filter by case with caseId.
Scopedocuments:read
Parameters
caseIdstringqueryOnly documents in this case.
curl "https://northdocs.com/api/v1/documents?caseId=case_7Hq2" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"[
{
"id": "doc_9fK3a",
"name": "contract.pdf",
"status": "ready_for_review",
"pageCount": 12,
"caseId": "case_7Hq2"
}
]/documents/{id}Retrieve a document
Status, page count, case and detected language of one document. Poll this while a document processes.
Scopedocuments:read
Parameters
idstringpathrequiredThe document id.
curl "https://northdocs.com/api/v1/documents/doc_9fK3a" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"{
"id": "doc_9fK3a",
"name": "contract.pdf",
"status": "ready_for_review",
"pageCount": 12,
"caseId": "case_7Hq2",
"language": "da"
}/documents/{id}/applyApply redactions
Burns accepted detections into the file, or replaces them with consistent pseudonyms. Answers 202 at once; the status moves to applying and then completed.
Scopedocuments:write
Parameters
idstringpathrequiredThe document id.
mode"redact" | "pseudonymize"bodyredact (default) blacks out values; pseudonymize replaces them consistently.
acceptAllbooleanbodyAccept every pending detection before applying.
curl -X POST "https://northdocs.com/api/v1/documents/doc_9fK3a/apply" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"redact","acceptAll":true}'{
"status": "applying"
}/documents/{id}/downloadDownload the output
Returns the redacted PDF. With format=text it returns the redacted text per page as JSON instead. Answers 409 until redactions are applied.
Scopedocuments:read
Parameters
idstringpathrequiredThe document id.
format"text"queryReturn redacted text as JSON instead of the PDF.
curl "https://northdocs.com/api/v1/documents/doc_9fK3a/download?format=text" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"{
"id": "doc_9fK3a",
"pages": [{ "pageNumber": 1, "text": "Lejer: [REDACTED], Nørregade 12" }],
"fullText": "Lejer: [REDACTED], Nørregade 12"
}/documents/{id}/textRead extracted text
Raw text per page before redaction, for search and indexing. Not an export, and refused where the organization restricts unredacted access.
Scopedocuments:read
Parameters
idstringpathrequiredThe document id.
curl "https://northdocs.com/api/v1/documents/doc_9fK3a/text" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"{
"pages": [
{ "pageNumber": 1, "text": "Lejer: Jens Hansen, Nørregade 12", "ocr": false }
]
}/documents/{id}/detectionsList detections
Everything Northdocs found, with category, page, text and decision status. A status of possible is an uncertain suggestion: never redacted until set to accepted. Values are masked for roles that may not see them.
Scopedocuments:read
Parameters
idstringpathrequiredThe document id.
curl "https://northdocs.com/api/v1/documents/doc_9fK3a/detections" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"[
{
"id": "det_41xZ",
"category": "person",
"text": "Jens Hansen",
"pageNumber": 1,
"status": "pending",
"entityGroupId": "ent_02",
"pseudonym": null
}
]/documents/{id}/detections/{detectionId}Update a detection
Accept, skip or reset one detection, or move it to another category. Reviewer keys may decide but not reclassify.
Scopedocuments:write
Parameters
idstringpathrequiredThe document id.
detectionIdstringpathrequiredThe detection id from List detections.
status"pending" | "accepted" | "skipped"bodyThe decision for this detection.
categorystringbodyMove the detection to another category key.
curl -X PATCH "https://northdocs.com/api/v1/documents/doc_9fK3a/detections/det_41xZ" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"skipped"}'{
"ok": true
}/casesCreate a case
Creates a case to group documents. Pass its id as caseId on upload.
Scopecases:write
Parameters
namestringbodyrequiredCase name, shown in the app.
referencestringbodyYour own reference, such as a journal number.
defaultMode"redact" | "pseudonymize"bodyDefault apply mode for documents in the case.
curl -X POST "https://northdocs.com/api/v1/cases" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Aktindsigt 2026-114","reference":"J.nr. 2026-114"}'{
"id": "case_7Hq2"
}/casesList cases
Cases the key owner can open.
Scopecases:read
Parameters
No parameters.
curl "https://northdocs.com/api/v1/cases" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"[
{
"id": "case_7Hq2",
"name": "Aktindsigt 2026-114",
"status": "open",
"reference": "J.nr. 2026-114"
}
]/cases/{id}Retrieve a case
One case with its status and reference.
Scopecases:read
Parameters
idstringpathrequiredThe case id.
curl "https://northdocs.com/api/v1/cases/case_7Hq2" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"{
"id": "case_7Hq2",
"name": "Aktindsigt 2026-114",
"status": "open",
"reference": "J.nr. 2026-114"
}/jobsList jobs
Recent processing jobs with kind, status and progress.
Scopejobs:read
Parameters
No parameters.
curl "https://northdocs.com/api/v1/jobs" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY"[
{
"id": "job_k21P",
"kind": "process",
"status": "completed",
"documentId": "doc_9fK3a",
"progress": 100
}
]/sanitizeSanitize text
Finds and redacts or replaces personal data in plain text. Nothing is stored.
Scopeoutbound_guard
Parameters
textstringbodyrequiredThe text to sanitize.
mode"redact" | "replace"bodyredact (default) blacks out values; replace swaps in a placeholder.
curl -X POST "https://northdocs.com/api/v1/sanitize" \
-H "Authorization: Bearer $NORTHDOCS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Ring til Jens Hansen på 20 30 40 50","mode":"redact"}'{
"cleanText": "Ring til ███████████ på ███████████",
"findings": [
{
"category": "person",
"label": "Person name",
"text": "Jens Hansen",
"start": 9,
"end": 20,
"replacement": "███████████",
"source": "regex"
},
{
"category": "phone",
"label": "Phone number",
"text": "20 30 40 50",
"start": 24,
"end": 35,
"replacement": "███████████",
"source": "regex"
}
],
"byCategory": { "person": 1, "phone": 1 },
"mode": "redact"
}Webhooks
Add an endpoint in Settings, Webhooks. Northdocs sends a signed POST with a JSON body to your public HTTPS URL when a subscribed event happens.
document.completedA document finished applying and the output is ready to download.outbound_guard.alertOutbound Guard stopped or warned about an outgoing mail.
Verify every delivery. x-northdocs-signature is the hex HMAC SHA-256 of the raw body, keyed with the signing secret shown when you added the endpoint.
POST /northdocs-hook HTTP/1.1
Content-Type: application/json
x-northdocs-event: document.completed
x-northdocs-signature: 5f2c8a...e91a
{
"event": "document.completed",
"data": { "documentId": "doc_9fK3a", "mode": "redact" },
"timestamp": 1793500000000
}import { createHmac, timingSafeEqual } from "node:crypto";
export function isFromNorthdocs(rawBody: string, signature: string, secret: string) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}Errors
Errors use standard HTTP status codes and a JSON body with an error message. Some carry a machine readable code as well.
400A field is missing or invalid.401The API key is missing, wrong or revoked.402The plan or quota does not cover this call.403Missing scope, or the key owner's role cannot do this.404Not found, or the key owner cannot open it.409Wrong state, for example still processing.413The file is too large.415The file type is not supported.429Rate limited. Back off and retry.
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": "api key missing documents:write scope"
}Rate limits
Each key may make 120 requests per minute. Above that the API answers 429; wait a few seconds and retry. Polling one document every few seconds stays well inside the limit.
Uploads count toward the organization's monthly page quota, exactly as uploads in the app do.
OpenAPI spec
The full API is described in an OpenAPI 3.1 document. Import it into Postman or Insomnia, or generate a typed client.
Missing an endpoint? Tell us from Contact support in the app.
https://northdocs.com/api/v1
https://northdocs.com/api/v1/openapi.json