This is the full developer documentation for Dockt
# Dockt documentation
> Integrate document and workforce compliance verification into your product.
Dockt helps your product answer two related questions: what does an uploaded document show, and does a worker’s complete set of information satisfy the requirements that apply to them?
Your backend sends worker information and documents to the Dockt API. Dockt processes that information asynchronously and returns structured results that your product can store, display, or route to a reviewer. You keep control of the user experience and the action your product takes.
Use the production API at `https://api.dockt.com`. Authenticate backend requests with a workspace API credential.
## Choose your workflow
[Section titled “Choose your workflow”](#choose-your-workflow)
### Verify one document
[Section titled “Verify one document”](#verify-one-document)
Choose a standalone Document when one file can be handled on its own. For example, you may want to identify the document type, extract its public facts, run applicable checks, and decide whether the file can continue or needs review.
Upload the PDF or image to `POST /v1/documents`. The completed Document contains a result of `valid`, `review_required`, or `invalid`, together with the details that explain that result.
[Verify a document end to end](/guides/verify-document/)
### Evaluate one worker
[Section titled “Evaluate one worker”](#evaluate-one-worker)
Choose an Assessment when the answer depends on more than one file. An Assessment brings together one worker’s identity, the circumstances you are evaluating, and all supporting Documents.
Dockt first tells you which additional context and evidence the Assessment needs. As you provide them, Dockt evaluates the current case and creates a Decision that explains whether it is incomplete, compliant, needs review, or is non-compliant.
[Complete an assessment end to end](/guides/run-assessment/)
## The four resources to understand
[Section titled “The four resources to understand”](#the-four-resources-to-understand)
* A **Workspace** is the environment in which your configuration, credentials, Documents, Assessments, and webhooks live.
* A **Document** represents one uploaded PDF or image and its processing result.
* An **Assessment** represents the complete case for one worker at a point in time.
* A **Decision** is an immutable evaluation of one version of an Assessment.
These resources build on each other. A standalone Document can be used by itself. Documents uploaded to an Assessment become evidence for that Assessment. The Assessment’s current inputs and evidence produce a Decision.
## How an integration fits together
[Section titled “How an integration fits together”](#how-an-integration-fits-together)
1. Create a workspace credential with the permissions your backend needs.
2. Decide whether your workflow needs one standalone Document or a complete Assessment.
3. Send the information available in your product and upload each document from your backend.
4. Follow Dockt’s response: provide requested Assessment inputs or evidence when needed.
5. Wait for processing by polling during development or receiving signed webhooks in production.
6. Read the completed Document result or the Assessment’s latest Decision and its explanation.
7. Store Dockt resource IDs alongside your own records for later reads, reconciliation, and support.
Dockt processes uploads asynchronously. A successful upload means the file was accepted, not that verification is complete.
## Understand the results
[Section titled “Understand the results”](#understand-the-results)
A standalone Document finishes with `valid`, `review_required`, or `invalid`. This result describes the file and the checks that apply to it; it does not evaluate the worker’s complete situation.
An Assessment Decision is `incomplete`, `compliant`, `review_required`, or `non_compliant`. It evaluates the complete Assessment input version and includes a green, yellow, or red signal for display.
Use the semantic result as the source of truth. The accompanying requirements, Findings, issues, and explanations tell you why Dockt returned that result and what your product may need to ask the user to do next.
## Start integrating
[Section titled “Start integrating”](#start-integrating)
Begin with [Get started](/getting-started/) to configure authentication and make a first API call. Use the [API reference](/api/) for exact fields, response schemas, permissions, and status codes.
# Get started with Dockt
> Prepare your workspace, authenticate your backend, and choose your first verification workflow.
This section takes you from a new Dockt workspace to a verified API connection, then points you to the right first workflow. You don’t need to understand every API resource before you begin.
Dockt is designed for backend integration. Your application collects the worker information or document, your backend sends it to Dockt, and your application presents or acts on the structured result. Dockt API credentials and uploaded files should never pass through untrusted client code.
## Before you begin
[Section titled “Before you begin”](#before-you-begin)
You need:
* A Dockt account and workspace.
* Access to create a workspace API credential in the Dockt control plane.
* A backend or server-side function that can keep credentials and uploaded files private.
* `curl` and `jq` for the examples.
Never put API keys, client secrets, document bytes, or webhook signing secrets in browser or mobile code.
## Integration path
[Section titled “Integration path”](#integration-path)
1. [Create and store a workspace credential](/getting-started/authentication/).
2. [Call `/v1/auth/me`](/getting-started/first-api-call/) to confirm its scope and permissions.
3. [Plan your backend integration](/getting-started/plan-integration/) and decide which Dockt IDs your application stores.
4. Choose a first workflow:
* [Verify one document](/guides/verify-document/).
* [Complete a worker assessment](/guides/run-assessment/).
5. [Receive signed webhooks](/guides/receive-webhooks/) before moving asynchronous processing into production.
6. [Test the integration in a separate Workspace](/guides/test-integration/).
7. Review the [production checklist](/guides/production-checklist/).
The standalone Document guide is the smaller starting point. It teaches upload, asynchronous processing, and result handling with one file. The Assessment guide adds worker context, evidence requirements, and versioned Decisions.
## What Dockt returns
[Section titled “What Dockt returns”](#what-dockt-returns)
Dockt uses normal HTTP responses and JSON resource envelopes. Uploads are asynchronous: the first response confirms acceptance and gives you an ID, while a later read or webhook tells you that processing has finished.
For a Document, the final result answers whether the file passed its applicable checks, needs review, or is invalid. For an Assessment, the latest Decision answers whether the current worker case is incomplete, compliant, needs review, or is non-compliant. Both include structured details so you can explain the result and choose the next step in your own workflow.
## Base URL and API version
[Section titled “Base URL and API version”](#base-url-and-api-version)
Send production requests to:
```text
https://api.dockt.com/v1
```
The `/v1` prefix is part of every product API route. The machine-readable contract is available at [OpenAPI JSON](https://api.dockt.com/openapi.json).
Use the public documentation and OpenAPI contract to build and validate your integration.
## Choose a credential scope
[Section titled “Choose a credential scope”](#choose-a-credential-scope)
An Account is your top-level Dockt customer boundary. A Workspace is an isolated operational environment inside that Account. Most integrations use a separate Workspace for each environment or operational boundary they need to keep isolated.
Use a workspace credential for document, assessment, Decision, Features, and webhook operations. The credential always acts in its assigned Workspace, so your requests don’t need to include a Workspace selector.
Use an account credential only when your integration must manage the account, workspaces, or account-scoped credentials. Most verification backends need only a workspace credential.
## Choose a credential type
[Section titled “Choose a credential type”](#choose-a-credential-type)
An `api_key` is an opaque bearer secret and is the shortest path to a first request. An `m2m` credential uses a client ID and secret to obtain short-lived access tokens. Both authenticate Dockt API requests with the `Authorization: Bearer` header.
[Compare credential types](/getting-started/authentication/#choose-a-credential-type)
# Authenticate your backend
> Create an API key or M2M credential and send authenticated Dockt API requests.
Dockt supports API keys and machine-to-machine (M2M) credentials for backend integrations. Create a workspace-scoped credential in the Dockt control plane, then store the returned secret immediately. Secret material is returned only when the credential is created.
## Choose a credential type
[Section titled “Choose a credential type”](#choose-a-credential-type)
Use an `api_key` when you want a long-lived opaque bearer credential with the least setup. You can set an expiration when you create it.
Use an `m2m` credential when your service can exchange a client ID and client secret for short-lived access tokens. Refresh the token before its `expires_in` period ends.
Both credential types have a fixed `account` or `workspace` scope and an explicit `allowed_scopes` permission list.
## Use an API key
[Section titled “Use an API key”](#use-an-api-key)
Store the one-time `api_key` value as a secret and export it for the examples:
```sh
export DOCKT_API_BASE_URL='https://api.dockt.com'
export DOCKT_API_TOKEN='REPLACE_WITH_API_KEY'
```
Send the key as a bearer token:
```sh
curl --fail-with-body \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/auth/me"
```
## Use an M2M access token
[Section titled “Use an M2M access token”](#use-an-m2m-access-token)
Store the one-time `client_secret` together with the returned `client_id` and `token_url`:
```sh
export DOCKT_CLIENT_ID='REPLACE_WITH_CLIENT_ID'
export DOCKT_CLIENT_SECRET='REPLACE_WITH_CLIENT_SECRET'
export DOCKT_TOKEN_URL='REPLACE_WITH_TOKEN_URL'
```
Exchange the client credentials for an access token:
```sh
token_response="$(
curl --fail-with-body --silent --show-error \
-X POST "$DOCKT_TOKEN_URL" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode "client_id=$DOCKT_CLIENT_ID" \
--data-urlencode "client_secret=$DOCKT_CLIENT_SECRET" \
--data-urlencode 'grant_type=client_credentials'
)"
export DOCKT_API_TOKEN="$(printf '%s' "$token_response" | jq -r '.access_token')"
```
The token response includes `access_token`, `token_type`, and `expires_in`. Cache the token on your server and repeat the exchange before it expires.
## Grant only required permissions
[Section titled “Grant only required permissions”](#grant-only-required-permissions)
For a complete assessment workflow, a workspace credential typically needs:
* `assessments:read`
* `assessments:write`
* `documents:read`
* `documents:create`
* `decisions:read`
Add `webhooks:manage` if the same service configures webhook endpoints. Add `decisions:outcome` only if it reports what happened after a Decision.
For a standalone document workflow, use `documents:create` and `documents:read`. Add `documents:delete` if your integration withdraws documents.
The [authentication and permissions reference](/reference/authentication-permissions/) lists every public permission.
## Verify the credential
[Section titled “Verify the credential”](#verify-the-credential)
Call `GET /v1/auth/me` after loading your bearer token:
```sh
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/auth/me" | jq '.data | {
auth_type,
api_credential_type,
principal_scope_type,
permissions,
active_scope
}'
```
Confirm that `principal_scope_type` is `workspace` before calling workspace routes. Confirm that `permissions` includes every operation your integration will use.
## Protect and rotate credentials
[Section titled “Protect and rotate credentials”](#protect-and-rotate-credentials)
* Keep credentials in a server-side secret manager.
* Use separate credentials for separate applications and environments.
* Disable or delete a credential before replacing it.
* Never log bearer tokens, API keys, client secrets, or document bytes.
* Treat a `401` response as an authentication failure and a `403` response as a scope or permission failure.
# Make your first API call
> Confirm your Dockt credential, scope, permissions, and response handling.
Use `GET /v1/auth/me` to confirm that your backend can reach Dockt and that its credential has the expected workspace scope.
## Set environment variables
[Section titled “Set environment variables”](#set-environment-variables)
Export the production base URL and an API key or M2M access token:
```sh
export DOCKT_API_BASE_URL='https://api.dockt.com'
export DOCKT_API_TOKEN='REPLACE_WITH_BEARER_TOKEN'
```
## Read your authorization context
[Section titled “Read your authorization context”](#read-your-authorization-context)
Send the request:
```sh
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/auth/me" | jq
```
The response uses Dockt’s single-resource envelope:
```json
{
"object": "single",
"data": {
"object": "auth_context",
"auth_type": "api_credential",
"api_credential_type": "api_key",
"principal_scope_type": "workspace",
"permissions": [
"assessments:read",
"assessments:write",
"documents:read",
"documents:create",
"decisions:read"
],
"active_scope": {
"object": "principal_scope",
"scope_type": "workspace",
"scope_id": "wsp_example",
"account_id": "acc_example",
"name": "Production",
"role": null,
"status": "active"
},
"available_scopes": [],
"actor": null,
"onboarding_required": false,
"updated_at": "2026-08-06T09:30:00Z"
}
}
```
The values in your response differ. Check these fields:
* `auth_type` is `api_credential`.
* `principal_scope_type` is `workspace` for verification workflows.
* `active_scope.scope_id` is the workspace you intend to use.
* `permissions` contains the operations your integration needs.
## Keep the request ID
[Section titled “Keep the request ID”](#keep-the-request-id)
Dockt returns an `x-request-id` response header. Log it with your own request correlation ID, without logging secrets or documents. Include the Dockt request ID when you contact `hello@dockt.com` about a failed request.
## Continue to a verification
[Section titled “Continue to a verification”](#continue-to-a-verification)
Your connection is ready. Continue with [Verify one document](/guides/verify-document/) or [Complete an assessment](/guides/run-assessment/).
# Use Dockt with coding agents
> Give a coding agent the right Dockt documentation and implementation constraints.
Dockt publishes documentation in formats that coding agents can read without scraping page layouts:
* [`/llms.txt`](/llms.txt) is a compact index and recommended entry point.
* [`/llms-small.txt`](/llms-small.txt) contains the most important integration context.
* [`/llms-full.txt`](/llms-full.txt) contains the complete documentation set.
* [OpenAPI JSON](https://api.dockt.com/openapi.json) is the source for exact paths, fields, enums, permissions, and response schemas.
## Give your coding agent a prompt
[Section titled “Give your coding agent a prompt”](#give-your-coding-agent-a-prompt)
Replace the workflow and language placeholders, then give this prompt to your coding agent:
```text
Implement a backend integration with the Dockt API for WORKFLOW.
Read https://docs.dockt.com/llms.txt first. Use
https://api.dockt.com/openapi.json as the source of truth for every endpoint,
field, enum, permission, and response type. Do not invent fields or rely on
internal Dockt implementation details.
Requirements:
- Use LANGUAGE and the existing HTTP/client patterns in my application repository.
- Use only Dockt's public documentation and OpenAPI contract.
- Read the bearer token from DOCKT_API_TOKEN and call https://api.dockt.com.
- Keep credentials, uploaded files, and webhook secrets on the server.
- Send Idempotency-Key on supported create and upload requests.
- Treat document processing as asynchronous and handle every documented state.
- Parse non-2xx responses as application/problem+json.
- Log x-request-id without logging secrets or document contents.
- If receiving webhooks, verify the signature over the raw request body,
enforce the five-minute replay window, and deduplicate by event ID.
- Add focused tests for the response parsing and retry logic you implement,
plus webhook verification when applicable.
Before writing code, summarize the API operations and public states the
integration will use. After implementation, run the repository's checks.
```
Use `verify a standalone document` or `complete a worker assessment` for `WORKFLOW`. Use your application’s implementation language for `LANGUAGE`.
## Keep generated code aligned
[Section titled “Keep generated code aligned”](#keep-generated-code-aligned)
Generate client types from the OpenAPI document when your toolchain supports OpenAPI 3.1. Pin generated output to a reviewed contract revision in your repository, and review schema changes before updating it.
Do not ask an agent to infer undocumented fields from examples. Examples explain a workflow; the OpenAPI schema defines the request and response contract.
# Plan your backend integration
> Choose a Dockt workflow, API client, storage model, and asynchronous processing strategy.
Dockt is an API that your backend calls to verify documents or evaluate a worker case. Before you write application code, decide which workflow you need and where Dockt state fits in your system.
You don’t need to reproduce Dockt’s evaluation logic. Your backend sends inputs, stores Dockt resource IDs, and acts on the returned result.
## Choose your workflow
[Section titled “Choose your workflow”](#choose-your-workflow)
Start with one of these workflows:
* Use a **standalone Document** when one uploaded file needs its own result.
* Use an **Assessment** when you need to evaluate one worker, their circumstances, and one or more supporting Documents together.
An Assessment is not a batch of unrelated Documents. It represents one worker case that can change as you provide context or upload evidence. Each evaluation creates an immutable Decision.
If you are still deciding, [compare Documents and Assessments](/#choose-your-workflow).
## Keep Dockt behind your backend
[Section titled “Keep Dockt behind your backend”](#keep-dockt-behind-your-backend)
Your application normally has three parts:
1. Your client collects worker information or a file.
2. Your backend sends the request to Dockt with a workspace credential.
3. Your backend stores the returned IDs and presents or acts on the result.
Keep API credentials, webhook secrets, and document uploads out of browser and mobile code. If a browser uploads a file to your product, send it to your backend first.
## Use the backend API surface
[Section titled “Use the backend API surface”](#use-the-backend-api-surface)
Most verification integrations use these endpoint groups:
| Goal | Endpoint group |
| ------------------------- | --------------------------------------------------- |
| Confirm authentication | `GET /v1/auth/me` |
| Read active configuration | `/v1/features` and `/v1/social-compliance-packages` |
| Verify one file | `/v1/documents` |
| Evaluate one worker case | `/v1/assessments` and `/v1/decisions` |
| Receive completion events | `/v1/webhooks` |
The API reference also contains browser-session, Account, Workspace, user, and credential-administration operations. You don’t need those operations for a backend that already has a workspace credential. Use them only when your product also manages Dockt administration.
## SDKs and generated clients
[Section titled “SDKs and generated clients”](#sdks-and-generated-clients)
Dockt supports its REST API and [OpenAPI JSON](https://api.dockt.com/openapi.json) as the integration surface. You can call the API with your HTTP client or generate types and client code from OpenAPI.
For a generated TypeScript type file, run:
```sh
npx openapi-typescript https://api.dockt.com/openapi.json \
--root-types \
--root-types-no-schema-prefix \
--output src/dockt-schema.d.ts
```
Pin the generated file in your repository. Review the OpenAPI diff before replacing it. This keeps a contract change from silently changing your application build.
The [TypeScript backend guide](/guides/build-backend/) uses native `fetch` and generated OpenAPI types. The same resource flow applies in other languages.
## Store resource lineage
[Section titled “Store resource lineage”](#store-resource-lineage)
Store Dockt IDs beside the corresponding record in your system. At minimum, retain:
* Your own case or upload ID.
* The Dockt Workspace ID used by the integration.
* The Assessment or standalone Document ID.
* Every uploaded Document ID.
* The Decision ID and `assessment_input_version` your product acted on.
* The idempotency key used for each create or upload operation.
* The latest known status and the time you last reconciled it.
Don’t use a filename, worker name, or `external_id` as the Dockt resource identifier. Dockt IDs remain the canonical values for later API calls.
## Plan for asynchronous results
[Section titled “Plan for asynchronous results”](#plan-for-asynchronous-results)
Document processing and Assessment evaluation continue after the create or upload response. A `202 Accepted` response confirms that Dockt accepted the work; it is not a verification result.
Use this production pattern:
1. Store the resource ID before returning success from your own backend.
2. Receive signed webhooks as the main completion signal.
3. Fetch the canonical resource after each event.
4. Run periodic API reconciliation for resources that did not receive an event.
5. Apply an application-owned deadline without converting elapsed time into a Dockt result.
The public API does not define a guaranteed completion time. Continue to treat `uploaded` and `processing` as unresolved states until Dockt returns a terminal state.
## Choose the next step
[Section titled “Choose the next step”](#choose-the-next-step)
* [Build a typed TypeScript backend](/guides/build-backend/).
* [Verify a standalone Document](/guides/verify-document/).
* [Complete an Assessment](/guides/run-assessment/).
* [Test your integration](/guides/test-integration/).
# Call Dockt from TypeScript
> Create a typed Dockt client in your application using the public OpenAPI contract.
This guide shows how to call Dockt from a TypeScript backend using the public OpenAPI contract and a workspace credential.
The example uses native `fetch`. It creates an Assessment, uploads evidence, and reads the resulting Decision.
## Before you begin
[Section titled “Before you begin”](#before-you-begin)
In your own application project, make sure you have:
* Node.js 20 or another server runtime with `fetch`, `FormData`, and `Blob`.
* A workspace credential stored as `DOCKT_API_TOKEN`.
* TypeScript configured for your backend.
Never expose `DOCKT_API_TOKEN` to browser or mobile code.
## Generate public API types
[Section titled “Generate public API types”](#generate-public-api-types)
Generate a TypeScript declaration file from Dockt’s public OpenAPI URL:
```sh
npx openapi-typescript https://api.dockt.com/openapi.json \
--root-types \
--root-types-no-schema-prefix \
--output src/dockt-schema.d.ts
```
This command creates `src/dockt-schema.d.ts` in your project with the public request and response types.
Commit the generated file so you can review contract changes before updating your integration.
## Create a Dockt client
[Section titled “Create a Dockt client”](#create-a-dockt-client)
In your application, create `src/dockt.ts`:
```ts
import type {
Assessment,
CreateAssessmentBody,
Decision,
Document,
ProblemDetails
} from './dockt-schema';
type SingleEnvelope = {
object: 'single';
data: T;
};
export class DocktApiError extends Error {
constructor(
readonly status: number,
readonly requestId: string | null,
readonly problem: ProblemDetails | null
) {
super(problem?.detail ?? `Dockt request failed with status ${status}.`);
}
}
export const createDocktClient = (token: string) => {
const request = async (path: string, init: RequestInit = {}) => {
const headers = new Headers(init.headers);
headers.set('authorization', `Bearer ${token}`);
headers.set('accept', 'application/json');
const response = await fetch(new URL(path, 'https://api.dockt.com'), {
...init,
headers
});
const body = await response.json().catch(() => null);
if (!response.ok) {
throw new DocktApiError(
response.status,
response.headers.get('x-request-id'),
body as ProblemDetails | null
);
}
return body as T;
};
return {
async createAssessment(body: CreateAssessmentBody, idempotencyKey: string) {
const response = await request>('/v1/assessments', {
method: 'POST',
headers: {
'content-type': 'application/json',
'idempotency-key': idempotencyKey
},
body: JSON.stringify(body)
});
return response.data;
},
async getAssessment(assessmentId: string) {
const response = await request>(
`/v1/assessments/${encodeURIComponent(assessmentId)}`
);
return response.data;
},
async uploadAssessmentDocument(
assessmentId: string,
file: Blob,
filename: string,
idempotencyKey: string
) {
const form = new FormData();
form.set('file', file, filename);
const response = await request>(
`/v1/assessments/${encodeURIComponent(assessmentId)}/documents`,
{
method: 'POST',
headers: { 'idempotency-key': idempotencyKey },
body: form
}
);
return response.data;
},
async getDecision(decisionId: string) {
const response = await request>(
`/v1/decisions/${encodeURIComponent(decisionId)}`
);
return response.data;
}
};
};
```
Do not set a multipart `Content-Type` header when you send `FormData`. Your runtime adds the required boundary.
## Create an Assessment
[Section titled “Create an Assessment”](#create-an-assessment)
Use the client from your server code:
```ts
import { createDocktClient } from './dockt';
const dockt = createDocktClient(process.env.DOCKT_API_TOKEN!);
const assessment = await dockt.createAssessment(
{
external_id: 'worker-case-1042',
worker: {
first_name: 'Amina',
last_name: 'Diallo',
date_of_birth: '1990-05-17',
nationality: 'FR'
},
context: [
{ code: 'employment.relationship', value: 'posted_employee' },
{ code: 'employment.employer_country', value: 'FR' }
]
},
'assessment-worker-case-1042'
);
console.log(assessment.id, assessment.input_requests, assessment.requirements);
```
Store `assessment.id` and the idempotency key in your database before reporting success to the caller of your backend.
If `input_requests` contains entries, collect those context values and update the Assessment before requesting evidence. The [Assessment inputs reference](/reference/assessment-inputs/) explains each value type and code.
## Upload evidence
[Section titled “Upload evidence”](#upload-evidence)
Read the file in your backend and upload it to the Assessment. This example uses a `Blob`, so it also works in server runtimes that don’t expose a filesystem:
```ts
const document = await dockt.uploadAssessmentDocument(
assessment.id,
new Blob([documentBytes], { type: 'application/pdf' }),
'passport.pdf',
'assessment-worker-case-1042-passport'
);
console.log(document.id, document.status);
```
`documentBytes` is the file content your backend received from your application’s private upload or storage layer. Store `document.id` with that upload record.
The upload response normally has `status: "uploaded"` or `status: "processing"`. Wait for a webhook or poll the canonical resource before reading a result.
## Read the Decision
[Section titled “Read the Decision”](#read-the-decision)
After `assessment.completed`, fetch the Assessment and its referenced Decision:
```ts
const current = await dockt.getAssessment(assessment.id);
if (current.input_requests.length > 0) {
console.log('More context is required', current.input_requests);
} else if (!current.latest_decision) {
console.log('Assessment processing is not complete');
} else {
const decision = await dockt.getDecision(current.latest_decision.decision_id);
console.log(decision.decision, decision.findings, decision.requirements);
}
```
Store the Decision ID and `assessment_input_version` your application acts on. Before a delayed or high-impact action, fetch the Assessment again and confirm that its `latest_decision.decision_id` still matches.
## Prepare the client for production
[Section titled “Prepare the client for production”](#prepare-the-client-for-production)
Add these controls in your application:
* Set connection and response timeouts.
* Retry only failures that can succeed later.
* Reuse the same idempotency key and body when retrying a create or upload.
* Verify and deduplicate signed webhooks.
* Reconcile unresolved resources with canonical API reads.
* Log `x-request-id`, but never log credentials or document contents.
Continue with [Receive webhooks](/guides/receive-webhooks/) and [Test your integration](/guides/test-integration/).
# Prepare for production
> Review authentication, retries, asynchronous processing, webhooks, errors, and data handling.
Use this checklist before sending production verification traffic.
## Credentials and permissions
[Section titled “Credentials and permissions”](#credentials-and-permissions)
* Call Dockt only from trusted backend code.
* Store API keys, client secrets, access tokens, and webhook secrets in a secret manager.
* Give each application and environment a separate credential.
* Keep development and automated tests in a Workspace separate from production.
* Grant only the permissions the integration uses.
* Confirm credential scope and permissions with `GET /v1/auth/me` during setup.
* Define a credential rotation and disablement procedure.
## Request safety
[Section titled “Request safety”](#request-safety)
* Send a stable `Idempotency-Key` on every supported create, upload, and invitation-resend operation.
* Use a new key for each distinct logical operation.
* Set explicit connection and response timeouts in your HTTP client.
* Parse every non-2xx response as `application/problem+json`.
* Log `x-request-id` and your own correlation ID without logging secrets or document contents.
* Retry only failures that can succeed later, with exponential backoff and jitter.
## Asynchronous workflows
[Section titled “Asynchronous workflows”](#asynchronous-workflows)
* Treat `201 Created` and `202 Accepted` according to each operation’s documented lifecycle.
* Handle every Document and Assessment status.
* Distinguish Document `failed` from Document `invalid`.
* Handle every Document result and every Assessment Decision value.
* Store the exact `decision_id` and `assessment_input_version` your product acts on.
* Re-read canonical resources before delayed or high-impact actions.
* Stop foreground polling at an application-owned deadline without inventing a Dockt result.
* Reconcile unresolved Documents and Assessments with scheduled canonical reads.
## Webhooks
[Section titled “Webhooks”](#webhooks)
* Read the raw request body before parsing JSON.
* Verify the timestamp and HMAC signature before trusting an event.
* Compare signatures in constant time.
* Deduplicate side effects by `X-Dockt-Event-Id`.
* Accept that delivery is at least once and unordered.
* Persist or enqueue the event before returning `2xx`.
* Use API polling as recovery when webhook delivery is unavailable.
* Define how you will replace a webhook endpoint and its one-time signing secret.
## Documents and personal data
[Section titled “Documents and personal data”](#documents-and-personal-data)
* Upload only through trusted backend infrastructure.
* Enforce your own file type and size checks before calling Dockt.
* Avoid placing personal data in filenames, logs, idempotency keys, or external IDs.
* Apply your retention and access-control policies to stored API responses.
* Withdraw Documents that should no longer participate in active use.
* Handle an unfamiliar supported-document classification without rejecting the complete response.
## Contract updates
[Section titled “Contract updates”](#contract-updates)
* Generate or validate client types against [OpenAPI JSON](https://api.dockt.com/openapi.json).
* Review contract changes before updating generated clients.
* Test unknown response fields and enum handling according to your language’s compatibility model.
* Keep integration tests for authentication, uploads, polling, webhooks, and Problem Details errors.
Follow [API compatibility and updates](/reference/api-compatibility/) for the client update workflow. Run [Test your integration](/guides/test-integration/) before sending production traffic.
# Receive webhooks
> Create a webhook endpoint, verify signatures, deduplicate events, and recover from delivery failures.
Use webhooks to react to asynchronous Document and Assessment completion without continuously polling Dockt.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
You need:
* A public HTTPS endpoint that can read the raw request body.
* A workspace credential with `webhooks:manage`.
* A secret manager for the one-time webhook signing secret.
Export your API credentials:
```sh
export DOCKT_API_BASE_URL='https://api.dockt.com'
export DOCKT_API_TOKEN='REPLACE_WITH_BEARER_TOKEN'
```
## 1. Create a webhook endpoint
[Section titled “1. Create a webhook endpoint”](#1-create-a-webhook-endpoint)
Subscribe to the events your integration handles:
```sh
webhook_response="$(
curl --fail-with-body --silent --show-error \
-X POST "$DOCKT_API_BASE_URL/v1/webhooks" \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: webhook-production-events' \
--data '{
"label": "Production events",
"url": "https://example.com/webhooks/dockt",
"events": [
"document.completed",
"document.failed",
"assessment.completed"
]
}'
)"
export DOCKT_WEBHOOK_ID="$(printf '%s' "$webhook_response" | jq -r '.data.id')"
printf '%s' "$webhook_response" | jq '.data | {id, status, events, secret}'
```
Store `data.secret` immediately. Dockt returns it only in the create response.
## 2. Read the raw request
[Section titled “2. Read the raw request”](#2-read-the-raw-request)
Dockt sends an HTTP `POST` with `Content-Type: application/json` and these headers:
* `X-Dockt-Event-Id`
* `X-Dockt-Event-Type`
* `X-Dockt-Timestamp`
* `X-Dockt-Signature`
Read the exact body bytes before parsing JSON. Re-serializing parsed JSON changes the signed message and causes signature verification to fail.
## 3. Verify the signature
[Section titled “3. Verify the signature”](#3-verify-the-signature)
The signed message is `.`. The signature is a lowercase hexadecimal HMAC-SHA256 digest prefixed with `v1=`.
This Web Crypto implementation rejects malformed timestamps, requests outside the five-minute replay window, and invalid signatures:
```ts
export const verifyDocktWebhook = async (input: {
body: string;
secret: string;
signature: string;
timestamp: string;
now?: Date;
}) => {
const signedAt = Date.parse(input.timestamp);
const now = input.now?.getTime() ?? Date.now();
if (!Number.isFinite(signedAt) || Math.abs(now - signedAt) > 5 * 60 * 1000) {
return false;
}
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(input.secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const digest = await crypto.subtle.sign(
'HMAC',
key,
encoder.encode(`${input.timestamp}.${input.body}`)
);
const expected = `v1=${Array.from(new Uint8Array(digest), (byte) =>
byte.toString(16).padStart(2, '0')).join('')}`;
if (expected.length !== input.signature.length) return false;
let mismatch = 0;
for (let index = 0; index < expected.length; index += 1) {
mismatch |= expected.charCodeAt(index) ^ input.signature.charCodeAt(index);
}
return mismatch === 0;
};
```
Use your platform’s constant-time comparison function when it provides one.
## 4. Connect verification to your HTTP handler
[Section titled “4. Connect verification to your HTTP handler”](#4-connect-verification-to-your-http-handler)
Pass verified events to a function that stores the event ID and queues your application work:
```ts
type AcceptEvent = (input: {
eventId: string;
event: unknown;
}) => Promise<'stored' | 'duplicate'>;
export const receiveDocktWebhook = async (
request: Request,
secret: string,
acceptEvent: AcceptEvent
) => {
const eventId = request.headers.get('x-dockt-event-id');
const signature = request.headers.get('x-dockt-signature');
const timestamp = request.headers.get('x-dockt-timestamp');
if (!eventId || !signature || !timestamp) {
return new Response('Missing webhook headers', { status: 400 });
}
const body = await request.text();
const valid = await verifyDocktWebhook({ body, secret, signature, timestamp });
if (!valid) return new Response('Invalid signature', { status: 401 });
let event: unknown;
try {
event = JSON.parse(body);
} catch {
return new Response('Invalid JSON', { status: 400 });
}
await acceptEvent({ eventId, event });
return new Response(null, { status: 204 });
};
```
Implement `acceptEvent` in your application so it inserts `eventId` with a unique constraint and durably stores or queues the event in the same operation. Return `duplicate` when the ID already exists. A duplicate still receives `204` because your application already accepted it.
## 5. Deduplicate before side effects
[Section titled “5. Deduplicate before side effects”](#5-deduplicate-before-side-effects)
Delivery is at least once and unordered. Store `X-Dockt-Event-Id` with a unique constraint before applying a side effect. A retry uses the same event ID and body.
Don’t assume that a `document.completed` event arrives before `assessment.completed`, or that events for separate Documents arrive in upload order. Fetch the canonical resource named by the event before applying an important state change.
## 6. Acknowledge after durable acceptance
[Section titled “6. Acknowledge after durable acceptance”](#6-acknowledge-after-durable-acceptance)
Return any `2xx` response after you have stored the event or queued durable work. Non-2xx responses and timeouts are retried up to 10 total attempts, with exponential delays capped at about 60 seconds. The default delivery timeout is 10 seconds.
Keep request handling short. Move long-running work out of the HTTP request.
## 7. Handle each event
[Section titled “7. Handle each event”](#7-handle-each-event)
* `document.completed`: Fetch `GET /v1/documents/{documentId}` for full facts, checks, issues, and Findings.
* `document.failed`: Fetch the Document, record the failure, and route it to retry or review.
* `assessment.completed`: Fetch the Assessment or the provided `decision_id` before acting on the result.
* `webhook.test`: Confirm signature verification, deduplication, and acknowledgement without changing product state.
## 8. Send a test event
[Section titled “8. Send a test event”](#8-send-a-test-event)
Queue a targeted `webhook.test` delivery:
```sh
curl --fail-with-body --silent --show-error \
-X POST \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/webhooks/$DOCKT_WEBHOOK_ID/test" | jq
```
The webhook must be active. The API returns `202 Accepted` when the test delivery is queued.
## 9. Monitor delivery health
[Section titled “9. Monitor delivery health”](#9-monitor-delivery-health)
Read `GET /v1/webhooks/{webhookId}` and inspect `delivery`:
* `last_event_type`
* `last_delivery_status`
* `last_attempted_at`
* `last_succeeded_at`
These fields are `null` before the first delivery attempt. Alert when the latest status remains `failed`, and use canonical API reads to recover any missed workflow state.
# Report an outcome
> Record what happened after your product acted on a Dockt Decision.
Report an Outcome when you want to record what your product or review team did after receiving a Dockt Decision. An Outcome is separate from the immutable Decision.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
You need the Decision ID your workflow acted on and a workspace credential with `decisions:outcome`.
## Create the Outcome
[Section titled “Create the Outcome”](#create-the-outcome)
Send the observed result, its timestamp, and a stable name for your source system:
```sh
export DOCKT_API_BASE_URL='https://api.dockt.com'
export DOCKT_API_TOKEN='REPLACE_WITH_BEARER_TOKEN'
export DOCKT_DECISION_ID='dec_REPLACE_ME'
curl --fail-with-body --silent --show-error \
-X POST "$DOCKT_API_BASE_URL/v1/decisions/$DOCKT_DECISION_ID/outcome" \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
-H 'Content-Type: application/json' \
--data '{
"outcome": "manual_review",
"observed_at": "2026-08-06T10:15:00Z",
"source_system": "contractor-portal",
"notes": "Reviewer requested an updated document."
}' | jq
```
Supported Outcome values are `accepted`, `rejected`, `follow_up_requested`, `manual_review`, and `unknown`.
Use the time the Outcome happened in `observed_at`, not the time your integration sends the request.
## Read the Outcome
[Section titled “Read the Outcome”](#read-the-outcome)
Fetch the recorded Outcome:
```sh
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/decisions/$DOCKT_DECISION_ID/outcome" | jq
```
A `404 Not Found` means no Outcome has been reported for that Decision.
## Preserve the original Decision
[Section titled “Preserve the original Decision”](#preserve-the-original-decision)
Reporting an Outcome doesn’t change Decision Facts, Findings, requirements, or evidence lineage. If new worker input or evidence changes the Assessment, Dockt creates a separate Decision. Report the Outcome against the exact Decision ID your system handled.
# Complete an assessment
> Create a worker Assessment, provide context and evidence, and interpret the Decision.
Use an Assessment when you need to evaluate one worker’s context and supporting Documents together. Unlike a standalone Document check, an Assessment determines which evidence applies to this worker and produces a Decision across the complete case.
By the end of this guide, your backend will create a worker case, respond to requests for missing context, collect the evidence that applies, wait for evaluation, and interpret the immutable Decision.
## How the workflow works
[Section titled “How the workflow works”](#how-the-workflow-works)
An Assessment is iterative rather than a single request-and-response operation:
1. Create the Assessment with the worker identity and context you already know.
2. Read `input_requests` and provide any additional context Dockt needs.
3. Read `requirements` and ask the user for the evidence that applies to this case.
4. Upload each evidence Document and wait for processing.
5. Read the latest Decision and route the case according to its result and Findings.
Updating context or evidence later may create a new Decision. The Assessment remains the current case, while each Decision preserves one evaluated version.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
You need a workspace credential with these permissions:
* `features:read`
* `assessments:read`
* `assessments:write`
* `documents:read`
* `documents:create`
* `decisions:read`
Export the API base URL and bearer token:
```sh
export DOCKT_API_BASE_URL='https://api.dockt.com'
export DOCKT_API_TOKEN='REPLACE_WITH_BEARER_TOKEN'
```
Your Workspace must have the intended social compliance package enabled in Features. Packages determine the possible requirements Dockt evaluates. Confirm the current configuration:
```sh
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/features" | jq '.data.social_compliance_packages'
```
If the list doesn’t contain the package your workflow needs, configure Workspace Features before creating the Assessment. You don’t send package codes in the Assessment request; Dockt applies the active Workspace configuration automatically.
## 1. Create an Assessment
[Section titled “1. Create an Assessment”](#1-create-an-assessment)
Create one Assessment for one worker case. Keep identity fields in `worker` and employment, assignment, employer, project, and evaluation values in `context`:
```sh
assessment_response="$(
curl --fail-with-body --silent --show-error \
-X POST "$DOCKT_API_BASE_URL/v1/assessments" \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: assessment-worker-case-1042' \
--data '{
"external_id": "worker-case-1042",
"external_profile": "site-electrician",
"worker": {
"first_name": "Amina",
"last_name": "Diallo",
"date_of_birth": "1990-05-17",
"nationality": "FR"
},
"context": [
{ "code": "employment.relationship", "value": "posted_employee" },
{ "code": "worker.origin", "value": "eea_swiss" },
{ "code": "employment.posting_status", "value": "posted" },
{ "code": "employment.employer_country", "value": "FR" },
{ "code": "employment.social_security_regime", "value": "eu_eea_swiss_coordination" },
{ "code": "assignment.duration_days", "value": 30 },
{ "code": "assessment.evaluation_date", "value": "2026-08-06" }
]
}'
)"
export DOCKT_ASSESSMENT_ID="$(printf '%s' "$assessment_response" | jq -r '.data.id')"
printf '%s' "$assessment_response" | jq '.data | {
id,
status,
input_requests,
requirements,
advisories
}'
```
`external_id` connects the Assessment to a stable case ID in your system. `external_profile` is optional and should only be sent when your Workspace has a matching profile configured.
Use context values that describe the real case. The example values illustrate the request shape; they don’t apply to every worker or package. Your create response is the first source of truth for what this Assessment needs next.
The [Create assessment response](/api/operations/assessmentscreateassessment/) shows every returned field. At this stage, `latest_decision` is `null` when Dockt still needs context or has not evaluated the current inputs.
## 2. Satisfy input requests
[Section titled “2. Satisfy input requests”](#2-satisfy-input-requests)
Read `input_requests` before uploading evidence. These are questions Dockt must answer before it can determine the applicable requirements. Each item tells you which context code is missing, why it is needed, its `value_type`, and any accepted options.
If requests remain, collect those values and update the Assessment:
```sh
curl --fail-with-body --silent --show-error \
-X PATCH "$DOCKT_API_BASE_URL/v1/assessments/$DOCKT_ASSESSMENT_ID" \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
-H 'Content-Type: application/json' \
--data '{
"context": [
{ "code": "CODE_FROM_INPUT_REQUEST", "value": "VALID_VALUE" }
]
}' | jq '.data.input_requests'
```
Replace both placeholders from the response; don’t invent context codes or values. Context values merge by code, so the update keeps other context already stored on the Assessment.
Repeat this step until `input_requests` is empty. While requests remain, `latest_decision` is `null`: that means more context is needed, not that the Assessment failed.
## 3. Review required evidence
[Section titled “3. Review required evidence”](#3-review-required-evidence)
Once context is complete, use `requirements` to decide which evidence to request from the user. Each requirement includes:
* `required` and `status`.
* A human-readable `label` and optional `reason`.
* `accepted_evidence_types`.
* Document IDs grouped by supporting, pending, review, or invalid status.
Focus on requirements where `required` is true and `status` is `missing`. Show the returned `label` and `reason` to explain the request to the user. Treat `accepted_evidence_types` as machine-readable evidence-purpose codes, not Document classification codes.
You don’t assign an upload to a requirement. After processing, the grouped Document IDs show whether a Document is still processing, supports the requirement, needs review, or is invalid. [Document uploads and evidence](/reference/supported-documents/) explains the relationship between classifications and evidence types.
Don’t use a package catalog’s possible evidence list as the worker’s checklist. The Assessment response is the source for this worker’s evaluated requirements.
## 4. Upload an evidence document
[Section titled “4. Upload an evidence document”](#4-upload-an-evidence-document)
Upload each PDF or image separately with a stable idempotency key:
```sh
document_response="$(
curl --fail-with-body --silent --show-error \
-X POST "$DOCKT_API_BASE_URL/v1/assessments/$DOCKT_ASSESSMENT_ID/documents" \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
-H 'Idempotency-Key: assessment-worker-case-1042-passport' \
-F 'file=@./passport.pdf;type=application/pdf'
)"
export DOCKT_DOCUMENT_ID="$(printf '%s' "$document_response" | jq -r '.data.id')"
printf '%s' "$document_response" | jq '.data | {id, status, filename}'
```
The `202 Accepted` response means Dockt accepted the upload. It doesn’t mean the Document or Assessment is complete.
Repeat this step for each relevant piece of evidence. Use a different idempotency key for each logical upload, and store the returned Document ID with the evidence record in your system.
Dockt determines how a processed Document contributes to the Assessment. Your upload request only supplies the file; don’t try to assign it to a requirement using fields that aren’t in the contract.
## 5. Wait for processing
[Section titled “5. Wait for processing”](#5-wait-for-processing)
Use the `assessment.completed` webhook in production. During development, poll the Assessment until a current Decision is available:
```sh
while true; do
assessment_response="$(
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/assessments/$DOCKT_ASSESSMENT_ID"
)"
assessment_status="$(printf '%s' "$assessment_response" | jq -r '.data.status')"
decision_id="$(printf '%s' "$assessment_response" | jq -r '.data.latest_decision.decision_id // empty')"
printf 'Assessment status: %s\n' "$assessment_status"
if [ -n "$decision_id" ]; then
export DOCKT_DECISION_ID="$decision_id"
break
fi
if [ "$(printf '%s' "$assessment_response" | jq '.data.input_requests | length')" -gt 0 ]; then
printf 'Assessment needs more context.\n' >&2
exit 1
fi
sleep 5
done
```
Dockt can create a Decision from zero or partial evidence when no linked Document is still processing. That Decision may be `incomplete`; it still gives you a structured explanation of what is missing. A later upload or input update can produce a newer immutable Decision.
An `assessment.completed` event means a Decision was produced for the current input version. It doesn’t prevent the Assessment from changing and producing another Decision later.
Stop foreground polling when your application’s waiting deadline expires. Keep the Assessment unresolved and reconcile it later; elapsed time does not imply `incomplete`, `review_required`, or `non_compliant`.
## 6. Read the complete Decision
[Section titled “6. Read the complete Decision”](#6-read-the-complete-decision)
Fetch the Decision referenced by the Assessment:
```sh
decision_response="$(
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/decisions/$DOCKT_DECISION_ID"
)"
printf '%s' "$decision_response" | jq '.data | {
id,
assessment_input_version,
decision,
signal,
explanation,
findings,
requirements,
documents
}'
```
Handle all four Decision values in your product:
* `compliant`: Continue according to your product policy.
* `incomplete`: Ask for the missing context or evidence identified by the response.
* `review_required`: Route the case to a person with its Findings and evidence links.
* `non_compliant`: Stop or escalate according to your product policy and show the Findings.
Start with `decision`, then read `requirements` and `findings` to understand why. Use Finding `code` and `impact` for routing and program logic. Use the title and explanation for reviewer-facing text. Use the display-oriented `signal` for presentation only.
The [Get decision response](/api/operations/decisionsgetdecision/) shows the complete immutable shape, including Facts, source lineage, Document results, applied package versions, and optional recommendation.
The Decision is Dockt’s evaluation, not the record of what your business eventually did. Keep your acceptance, rejection, follow-up, or manual-review action separate and optionally report it as a Decision Outcome.
## 7. Keep Decision lineage
[Section titled “7. Keep Decision lineage”](#7-keep-decision-lineage)
Store the Assessment ID, Decision ID, and `assessment_input_version` you acted on. Before applying a delayed action, read the Assessment again and decide whether your workflow must use its newer `latest_decision`.
Optionally [report the downstream Outcome](/guides/report-outcome/) after your team or product acts on the Decision.
# Test your integration
> Test Dockt authentication, payload parsing, asynchronous processing, webhooks, and recovery.
Test your Dockt integration in a separate Workspace before you send production worker data. A Workspace isolates credentials, configuration, Documents, Assessments, Decisions, and webhooks.
Dockt uses `https://api.dockt.com` for public API traffic. The API does not expose a request flag that changes an operation into test mode. Use Workspace isolation to keep test data and production data separate.
## Create a test environment
[Section titled “Create a test environment”](#create-a-test-environment)
Set up a Workspace used only by developers and automated tests:
1. Enable the same social compliance packages you plan to use in production.
2. Create a workspace credential with only the permissions your tests need.
3. Register a webhook endpoint owned by your test environment.
4. Use fictional or synthetic personal data that you are authorized to process.
5. Keep every test `external_id` and idempotency key free of personal data.
Do not run integration tests in a production Workspace. Assessments do not have a delete operation, and their immutable Decisions remain part of that Workspace’s history.
## Separate parser tests from processing tests
[Section titled “Separate parser tests from processing tests”](#separate-parser-tests-from-processing-tests)
Use two kinds of tests:
* **Contract tests** verify that your code handles every documented response shape and enum.
* **API workflow tests** verify authentication, uploads, asynchronous state changes, webhooks, and recovery against the public API.
Do not require an arbitrary uploaded file to produce one exact semantic result in every automated test. Use curated OpenAPI examples for deterministic parser tests, and use API workflow tests to verify transport and lifecycle behavior.
## Extract response fixtures from OpenAPI
[Section titled “Extract response fixtures from OpenAPI”](#extract-response-fixtures-from-openapi)
Download the public contract in your test setup:
```sh
curl --fail-with-body --silent --show-error \
https://api.dockt.com/openapi.json \
--output openapi.json
```
Extract a curated example, such as the accepted Document upload response:
```sh
jq '.paths["/v1/documents"].post.responses["202"].content["application/json"].example' \
openapi.json > document-uploaded.json
```
Use the same approach for:
* `POST /v1/assessments` with response `201`.
* `GET /v1/documents/{documentId}` with response `200`.
* `GET /v1/assessments/{assessmentId}` with response `200`.
* `GET /v1/decisions/{decisionId}` with response `200`.
* Any documented Problem Details response.
Store the extracted examples with your tests or regenerate them in a reviewed dependency-update process. Don’t download a changing contract during every production build.
## Test the first API boundary
[Section titled “Test the first API boundary”](#test-the-first-api-boundary)
Cover these cases in your test suite:
| Case | Expected behavior |
| ---------------------------------------- | ----------------------------------------------------------------------- |
| Valid workspace credential | `GET /v1/auth/me` returns the expected Workspace and permissions. |
| Missing bearer token | The API returns `401` with Problem Details. |
| Missing permission | The API returns `403` with Problem Details. |
| Empty or unsupported upload | The API returns the documented invalid-upload Problem Details response. |
| Same idempotency key and request | Dockt replays the successful response. |
| Same idempotency key with a changed body | Dockt returns an idempotency conflict. |
Record `x-request-id` when a test fails. Do not record the credential or uploaded file content.
## Test asynchronous state handling
[Section titled “Test asynchronous state handling”](#test-asynchronous-state-handling)
For Documents, verify that your application:
* Stores the Document ID from the `202 Accepted` response.
* Handles `uploaded`, `processing`, `completed`, `failed`, and `withdrawn`.
* Reads `document_result` only after `status` is `completed`.
* Distinguishes a processing `failed` status from an `invalid` result.
* Handles unknown future issue, Finding, and classification codes safely.
For Assessments, verify that your application:
* Renders returned `input_requests` from their `value_type` and `options`.
* Uses the Assessment’s `requirements` instead of a fixed upload checklist.
* Handles a Decision created from zero or partial evidence.
* Stores the Decision ID and `assessment_input_version` it used.
* Detects when a later update creates a newer Decision.
## Test webhooks
[Section titled “Test webhooks”](#test-webhooks)
Use `POST /v1/webhooks/{webhookId}/test` to send a signed `webhook.test` event. Verify that your receiver:
1. Reads the raw body before parsing JSON.
2. Rejects an invalid signature.
3. Rejects a timestamp outside the five-minute replay window.
4. Stores the event ID before applying side effects.
5. Returns `2xx` after durable acceptance.
6. Treats a repeated event ID as an acknowledged duplicate.
The test event proves delivery and signature handling. It does not create a Document, Assessment, or Decision.
## Test recovery
[Section titled “Test recovery”](#test-recovery)
Simulate failures in your own application:
* Drop one webhook after signature verification, then recover with a canonical API read.
* Deliver the same webhook twice, then confirm that the side effect runs once.
* Deliver events in a different order, then confirm that you fetch current resource state.
* Time out an upload request, then retry with the same idempotency key and body.
* Leave a resource unresolved past your application’s deadline, then confirm that you keep it pending instead of inventing a result.
Finish by reviewing [Prepare for production](/guides/production-checklist/).
# Verify a document
> Upload, monitor, and interpret one standalone document verification.
Use a standalone Document when you need a result for one file without evaluating a worker’s complete compliance case. This is the smallest complete Dockt workflow and a good way to validate your integration before adding Assessments.
By the end of this guide, your backend will upload a file, wait for asynchronous processing, distinguish processing failure from an invalid result, and read the structured details behind the result.
## How the workflow works
[Section titled “How the workflow works”](#how-the-workflow-works)
1. Your backend uploads one PDF or image and receives a Document ID.
2. Dockt processes the file after the upload request has returned.
3. Your backend waits for `completed`, `failed`, or `withdrawn` by polling or receiving a webhook.
4. For a completed Document, your product branches on `document_result` and uses the accompanying details to explain or review it.
The Document ID connects every step. Store it as soon as the upload succeeds.
## Prerequisites
[Section titled “Prerequisites”](#prerequisites)
You need a workspace credential with `documents:create` and `documents:read`. Export its bearer token and the API base URL:
```sh
export DOCKT_API_BASE_URL='https://api.dockt.com'
export DOCKT_API_TOKEN='REPLACE_WITH_BEARER_TOKEN'
```
Prepare one non-empty supported file no larger than 10 MB. This guide uses `./document.pdf`. See [Document uploads and evidence](/reference/supported-documents/) for accepted file formats.
The API call must come from your backend. Don’t send the Dockt bearer token to a browser or mobile client, and don’t place document bytes in application logs.
## 1. Upload the document
[Section titled “1. Upload the document”](#1-upload-the-document)
Send one `file` part and an idempotency key that identifies this upload in your system:
```sh
document_response="$(
curl --fail-with-body --silent --show-error \
-X POST "$DOCKT_API_BASE_URL/v1/documents" \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
-H 'Idempotency-Key: document-case-1042-primary' \
-F 'file=@./document.pdf;type=application/pdf'
)"
export DOCKT_DOCUMENT_ID="$(printf '%s' "$document_response" | jq -r '.data.id')"
printf '%s' "$document_response" | jq '.data | {id, status, filename}'
```
Dockt returns `202 Accepted` with the new Document in a single-resource response envelope. Store `data.id`; it is the canonical Document ID for polling, webhook reconciliation, and later reads.
The initial `status` is normally `uploaded` or `processing`. There is no `document_result` to act on until the status becomes `completed`.
The response fields become useful at different points in the lifecycle:
| Field | Before completion | After `completed` |
| ------------------------------------ | -------------------------- | ---------------------------------------- |
| `status` | `uploaded` or `processing` | `completed` |
| `document_result` | `null` | `valid`, `review_required`, or `invalid` |
| `classified_as` and evidence details | Can be `null` or empty | Contain the available result details |
| `completed_at` | `null` | Completion date and time |
The [Create document response](/api/operations/documentscreatedocument/) shows the complete accepted-upload shape. The [Get document response](/api/operations/documentsgetdocument/) shows the canonical read shape.
## 2. Wait for a terminal status
[Section titled “2. Wait for a terminal status”](#2-wait-for-a-terminal-status)
Poll the Document with backoff while `status` is `uploaded` or `processing`:
```sh
while true; do
document_response="$(
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/documents/$DOCKT_DOCUMENT_ID"
)"
document_status="$(printf '%s' "$document_response" | jq -r '.data.status')"
printf 'Document status: %s\n' "$document_status"
case "$document_status" in
completed|failed|withdrawn) break ;;
uploaded|processing) sleep 5 ;;
*) printf 'Unexpected status: %s\n' "$document_status" >&2; exit 1 ;;
esac
done
```
For production workloads, prefer the `document.completed` and `document.failed` webhooks and use polling as recovery. A webhook is a notification that state changed, so fetch the Document by ID before applying an important action.
Stop polling when your application’s own waiting deadline expires. Keep the Document unresolved and reconcile it later; elapsed time does not change the Document to `failed`.
## 3. Handle processing failure
[Section titled “3. Handle processing failure”](#3-handle-processing-failure)
If `status` is `failed`, processing didn’t produce a document verdict. Inspect `issues`, retain the response `x-request-id`, and decide whether to ask for a clearer file, retry the upload as a new logical operation, or send the case to manual review.
Don’t treat `failed` as `invalid`. A processing failure isn’t a conclusion about the document’s validity.
## 4. Interpret the result
[Section titled “4. Interpret the result”](#4-interpret-the-result)
For a completed Document, inspect the result and actionable fields:
```sh
printf '%s' "$document_response" | jq '.data | {
document_result,
classified_as,
facts,
verifications,
enrichment,
credential_assertions,
issues,
fraud_signals,
findings
}'
```
Use `document_result` as the first branch in your application and handle every value:
* `valid`: Continue the workflow, while still displaying relevant informational Findings.
* `review_required`: Route the Document to a reviewer and show its issues and Findings.
* `invalid`: Stop or escalate the workflow according to your policy and show the reasons.
After choosing the branch, use `findings`, `issues`, and `fraud_signals` to explain why it was selected. `classified_as` and `facts` describe what Dockt detected and extracted. `verifications`, `enrichment`, and `credential_assertions` provide additional supporting detail when applicable.
Not every Document produces every detail. Empty optional collections are normal and shouldn’t be treated as processing errors.
An unsupported document type completes as `review_required` with a `type_unknown` issue. Ask for another document or route the case to review.
Treat `enrichment.status` and its checks separately from `document_result`. Enrichment warnings provide additional context and don’t automatically override an intrinsic valid result.
## 5. Store the integration record
[Section titled “5. Store the integration record”](#5-store-the-integration-record)
Store at least:
* Your source record ID.
* `data.id` as the Dockt Document ID.
* The latest `status` and `document_result`.
* The `x-request-id` for failed API requests.
Keep the full response when your retention policy permits it. Don’t store or log the bearer token with the result.
If your product acts automatically, also store the result and response version it acted on. For a review workflow, keep the Document ID so the reviewer can fetch the current canonical response rather than relying on a webhook payload.
## 6. Optional: withdraw the document
[Section titled “6. Optional: withdraw the document”](#6-optional-withdraw-the-document)
With `documents:delete`, withdraw a Document that should no longer participate in active use:
```sh
curl --fail-with-body --silent --show-error \
-X DELETE \
-H "Authorization: Bearer $DOCKT_API_TOKEN" \
"$DOCKT_API_BASE_URL/v1/documents/$DOCKT_DOCUMENT_ID" | jq
```
The response confirms `deleted: true`. Earlier Assessment Decisions, if any, remain immutable.
# Accounts and workspaces
> Understand Dockt's ownership and authorization boundaries.
Dockt separates administration from day-to-day verification by using Accounts and Workspaces.
An Account is your top-level customer boundary. It owns your Workspaces, account users, and account-scoped API credentials. You normally interact with the Account when setting up access or managing environments.
A Workspace is where verification work happens. Its configuration, credentials, Documents, Assessments, Decisions, and webhooks are isolated from every other Workspace. A resource created in one Workspace cannot be read or used from another Workspace.
For example, you might use separate Workspaces for development and production. Each has its own credentials, configuration, data, and webhook endpoint, which prevents test traffic from mixing with production traffic.
## Choose the right boundary
[Section titled “Choose the right boundary”](#choose-the-right-boundary)
Use account scope to manage:
* Account profile information.
* Account users and invitations.
* Workspaces owned by the Account.
* Account-scoped API credentials.
Use workspace scope to manage:
* Features and enabled social compliance packages.
* Documents and Assessments.
* Decisions and Decision Outcomes.
* Workspace API credentials.
* Webhook endpoints.
Most verification requests require a workspace-scoped credential.
If your product only uploads documents and runs Assessments, you can treat the Account as an administrative container and perform all runtime requests with a workspace credential.
## Scope resolution
[Section titled “Scope resolution”](#scope-resolution)
A workspace API credential always operates in the Workspace assigned when the credential was created. You don’t select or override its Workspace on each request. When you call `POST /v1/documents`, for example, the new Document automatically belongs to that credential’s Workspace.
A browser session can select an available Account or Workspace through `POST /v1/auth/scope`. The selected scope applies to subsequent session requests.
Call `GET /v1/auth/me` to inspect the active scope and permissions for either authentication method.
## Resource ownership
[Section titled “Resource ownership”](#resource-ownership)
Dockt resource IDs have recognizable prefixes, such as `acc_`, `wsp_`, `ast_`, `doc_`, and `dec_`. IDs identify resources, but they don’t grant access. Dockt checks every requested resource against the authenticated scope.
Store Dockt IDs alongside your own record IDs. The Dockt ID is the value you use for later API calls; your own ID keeps the resource connected to the case or environment in your application.
Use `external_id` on Workspaces and Assessments when you need that correlation to appear in Dockt responses. Keep personal data out of external IDs because they may appear in logs and operational tools.
## Account and workspace credentials
[Section titled “Account and workspace credentials”](#account-and-workspace-credentials)
An account credential can manage account resources and Workspaces when its permissions allow those operations. It cannot perform workspace verification operations without workspace scope.
A workspace credential cannot access another Workspace, even if both Workspaces belong to the same Account.
Create separate credentials for separate applications and environments. This gives each integration an independent permission set and lets you rotate or disable one credential without interrupting the others.
[Authentication and permissions reference](/reference/authentication-permissions/)
# Assessments
> Understand the worker verification case, its inputs, evidence, and lifecycle.
An Assessment is the complete verification case for one worker. It brings together who the worker is, the circumstances you are evaluating, the evidence you upload, and the Workspace configuration that determines which requirements apply.
Create one Assessment for one case in your system. Use its `external_id` to link it to your own worker, onboarding, assignment, or access record. As the case changes, update the existing Assessment instead of creating a replacement for every new document.
The Assessment is a live resource: you can add information and evidence over time. Its Decisions are permanent snapshots of what Dockt concluded from each version of that information.
## Assessment inputs
[Section titled “Assessment inputs”](#assessment-inputs)
Create an Assessment with the information your product already knows:
* `worker`: the worker’s legal first name, last name, date of birth, and two-letter nationality code.
* `context`: coded employment, assignment, employer, project, or evaluation values.
* `external_id`: an optional identifier from your system.
* `external_profile`: an optional profile label configured for your Workspace.
The `worker` object answers “who is being assessed?” The `context` array answers “under which circumstances?” Context uses coded values so the applicable requirements can be evaluated consistently.
You don’t need to guess every possible context field before creating the Assessment. If more information is needed, Dockt returns `input_requests`. Each request contains the exact code, value type, reason, and allowed options when applicable. Present those requests in your own workflow, collect the values, and update the Assessment. While input requests remain, `latest_decision` is `null` because Dockt doesn’t yet have enough context to evaluate the case.
Update context with `PATCH /v1/assessments/{assessmentId}`. Context values merge by `code`, so send the codes you want to add or replace.
## Assessment lifecycle
[Section titled “Assessment lifecycle”](#assessment-lifecycle)
Assessment `status` tells you what Dockt is currently doing. It is not the compliance result.
An Assessment has one of these statuses:
* `created`: The Assessment exists and can accept input or evidence.
* `documents_pending`: One or more linked Documents haven’t finished processing.
* `processing`: Dockt is evaluating the current inputs.
* `assessed`: A current Decision is available.
After `assessed`, read `latest_decision.decision` for the semantic result. An assessed case can still be `incomplete`, `review_required`, or `non_compliant`; `assessed` only means a Decision was produced.
## Evidence and evaluation
[Section titled “Evidence and evaluation”](#evidence-and-evaluation)
The Assessment’s `requirements` tell you which evidence applies to this worker. Request evidence from the user based on that list, then upload each PDF or image to `POST /v1/assessments/{assessmentId}/documents`. Each upload creates a normal Document and links it to the Assessment.
The Document is processed first. Its result then contributes to the Assessment requirement it supports. This is why you may briefly see a Document in `processing` while the Assessment is `documents_pending`.
Dockt evaluates the current Assessment whenever no accepted linked Document is still processing. This means an Assessment can receive an `incomplete`, `review_required`, or `non_compliant` Decision with no or partial evidence.
A later upload, withdrawn Document, worker update, context update, or Features change can produce a newer Decision. Earlier Decisions remain unchanged, so you can always identify exactly which result your product acted on.
## Inputs, requirements, and Findings
[Section titled “Inputs, requirements, and Findings”](#inputs-requirements-and-findings)
These three collections answer different questions:
* `input_requests`: What additional facts must your product collect before Dockt can evaluate the case?
* `requirements`: Which evidence applies, and has acceptable evidence satisfied it?
* `review` and Decision `findings`: Why does the current result need attention, and what should a reviewer inspect?
Check them in that order. First collect requested context, then collect required evidence, then interpret the Decision and its Findings.
## Assessment reads
[Section titled “Assessment reads”](#assessment-reads)
The Assessment detail response is the current working view of the case:
* `input_requests` for missing context.
* `requirements` and their evidence status.
* `advisories` for external or planning obligations.
* `documents` as linked Document summaries.
* `review` with machine-readable reason codes.
* `latest_decision` with the current result and Findings.
The compact `latest_decision` is useful for a list or status screen. Follow `latest_decision.decision_id` with `GET /v1/decisions/{decisionId}` when your workflow acts on the result or a reviewer needs the complete immutable record, resolved public Facts, evidence lineage, and Document results.
[Complete an assessment](/guides/run-assessment/)
[Assessment input codes and value types](/reference/assessment-inputs/)
# Decisions and outcomes
> Interpret immutable assessment results and report downstream Outcomes.
An Assessment changes as you add context, upload evidence, or withdraw an incorrect Document. A Decision captures the result at one specific point in that process.
Each Decision is an immutable evaluation of one Assessment input version. It records what Dockt concluded, why it reached that conclusion, and which public evidence supported it. This gives your product a stable result to act on even if the Assessment changes later.
Dockt provides the Decision; your product decides the business action. For example, you may continue automatically, ask for more evidence, route the case to a reviewer, or stop the workflow according to your own policy.
## Decision values
[Section titled “Decision values”](#decision-values)
Use the `decision` field as the semantic result and handle every value:
* `incomplete`: The current inputs don’t yet satisfy everything needed for a complete result. Read requirements and Findings to determine what is missing or unusable.
* `compliant`: The current inputs satisfy the requirements evaluated for this Assessment version.
* `review_required`: Dockt found uncertainty or an issue that needs a person to decide the next action. Present the Findings and supporting evidence to the reviewer.
* `non_compliant`: The current inputs don’t satisfy one or more evaluated requirements. Read the Findings and requirements before explaining or applying your product’s action.
The `signal` field is a simplified display value derived from `decision`:
| `decision` | `signal` |
| ----------------- | -------- |
| `compliant` | `green` |
| `incomplete` | `yellow` |
| `review_required` | `yellow` |
| `non_compliant` | `red` |
Use the signal for status color or sorting when helpful, but don’t infer a Decision or business action from it alone. Use `decision`, Findings, and requirements to control your workflow.
## Read a Decision
[Section titled “Read a Decision”](#read-a-decision)
Assessment detail includes a compact `latest_decision` so you can show current case status without making another request. It contains the Decision ID and summary result.
Fetch `GET /v1/decisions/{decisionId}` before acting on a Decision or opening a detailed review. The complete response includes:
* `assessment_input_version`
* `explanation`
* `findings`
* `facts` and their customer-safe sources
* `documents` and their results
* `requirements`
* `applied_packages`
* An optional `recommendation`
The `explanation` summarizes the overall conclusion. `requirements` show how each evidence obligation contributed. `documents` and `facts` show the supporting record.
Findings identify specific reasons behind the result. They have a stable `code`, a `scope`, and an `impact`. Use the code and impact for routing or program logic. Use the title and explanation for people reviewing the result, rather than translating a code into customer-facing text yourself.
## Decision immutability
[Section titled “Decision immutability”](#decision-immutability)
Dockt never updates a Decision in place. When worker data, context, evidence, or applicable configuration changes, Dockt can create a new Decision for a higher `assessment_input_version`.
Store the `decision_id` and `assessment_input_version` you acted on with your own workflow record. Don’t assume that an Assessment’s current `latest_decision` is the same Decision your system handled earlier.
For delayed or high-impact actions, read the Assessment again and compare its current `latest_decision.decision_id` with the Decision you originally received. Your product can then choose whether to continue with the original result or re-evaluate its action against the newer one.
## Report an Outcome
[Section titled “Report an Outcome”](#report-an-outcome)
An Outcome records what happened in your system after you received a Decision. It closes the feedback loop without modifying Dockt’s evaluation.
For example, a `review_required` Decision may lead your team to `accepted`, `rejected`, `follow_up_requested`, or `manual_review`. The Outcome describes that downstream event; it is not a correction or override of the Decision.
Supported Outcome values are:
* `accepted`
* `rejected`
* `follow_up_requested`
* `manual_review`
* `unknown`
Use `POST /v1/decisions/{decisionId}/outcome` to report an Outcome with the time it happened and the source system that observed it. Report it against the exact Decision your product handled, not whichever Decision is currently latest.
[Report a Decision Outcome](/guides/report-outcome/)
[Decision fact codes and source lineage](/reference/facts-and-assertions/#decision-facts-shape)
# Documents
> Understand standalone and assessment-linked document processing and results.
A Document represents one PDF or image uploaded to a Workspace and everything Dockt learns from processing that file. The resource groups the detected document type, extracted public facts, applicable checks, issues, and final result under one ID.
A Document answers questions about the file itself. It doesn’t, by itself, answer whether a worker satisfies every requirement that applies to their situation. Use an Assessment when you need that broader answer.
## Upload types
[Section titled “Upload types”](#upload-types)
Create a standalone Document with `POST /v1/documents` when one file can be handled independently. Your product might use this to check a document at upload time before deciding what to do next.
Create an assessment-linked Document with `POST /v1/assessments/{assessmentId}/documents` when the file is evidence for a worker Assessment. Dockt processes it as a Document and also makes its result available to the Assessment’s requirements and next Decision.
Don’t upload the same file through both routes for one workflow. Choose the route based on whether the result stands alone or must contribute to an Assessment.
Uploads use `multipart/form-data` with one `file` part. Files must be non-empty PDFs or images no larger than 10 MB. Declare the file’s MIME type when possible.
## Processing lifecycle
[Section titled “Processing lifecycle”](#processing-lifecycle)
Document processing is asynchronous because classification, extraction, and applicable checks may continue after the upload request returns. The `status` field tells you whether that work is still running:
A Document has one of these statuses:
* `uploaded`: Dockt accepted the upload.
* `processing`: Verification is in progress.
* `completed`: Processing finished and `document_result` is available.
* `failed`: Processing couldn’t produce a result.
* `withdrawn`: The Document was deleted from active use.
`POST /v1/documents` and assessment upload return `202 Accepted`. This means Dockt stored the upload and created the Document; it doesn’t mean the file passed verification. Store the returned Document ID, then poll `GET /v1/documents/{documentId}` or receive a Document webhook before interpreting the result.
Treat `failed` and `invalid` differently. `failed` is a processing status: Dockt couldn’t produce a result. `invalid` is a completed result: processing succeeded and the applicable checks concluded that the file was invalid.
## Document result
[Section titled “Document result”](#document-result)
For a completed Document, `document_result` summarizes the file-level conclusion:
* `valid`: The document passed applicable intrinsic checks.
* `review_required`: One or more issues need review.
* `invalid`: The document failed applicable intrinsic checks.
An unsupported document type completes as `review_required` with a `type_unknown` issue. Processing succeeded, but Dockt couldn’t confidently apply a supported document workflow. Ask the user for a supported document or send the file to review.
Your product should branch on `document_result`, then use `issues`, `findings`, and the detailed fields to explain the result. Don’t reconstruct a result by combining individual checks yourself.
## Detailed Document fields
[Section titled “Detailed Document fields”](#detailed-document-fields)
The canonical Document read can include several layers of information:
* `classified_as`: what kind of document Dockt detected, including its family, type, and optional variant.
* `facts`: public values extracted from the file, with confidence where available. Use these as structured observations, not as replacements for the result.
* `verifications`: results from checks that apply to the detected document.
* `enrichment`: additional evidence-discovery checks and warnings that may provide context.
* `credential_assertions`: customer-safe records of verified credentials where available.
* `issues` and `fraud_signals`: machine-readable reasons that the file needs attention.
* `findings`: actionable conclusions produced from this Document, with codes and explanations suitable for workflow and review.
Not every field is populated for every file. The detected type and applicable checks determine which details are available. Write your integration to handle empty optional collections without treating them as errors.
The `classified_as.type` value identifies the Document. It is not the same as an Assessment requirement’s `accepted_evidence_types`, which describe the purpose the evidence must serve. Dockt maps processed Documents to requirements and returns the result through each requirement’s Document ID arrays. See [Document uploads and evidence](/reference/supported-documents/) for upload requirements and evidence handling.
Document enrichment is reported separately from `document_result`. A warning or unavailable enrichment check doesn’t automatically make an otherwise valid Document invalid. Use `document_result` as the top-level conclusion.
## Withdraw a Document
[Section titled “Withdraw a Document”](#withdraw-a-document)
Delete a standalone Document with `DELETE /v1/documents/{documentId}`. For an assessment-linked Document, use `DELETE /v1/assessments/{assessmentId}/documents/{documentId}`.
Withdrawal removes the Document from active processing and later evaluation. Use it when a user uploaded the wrong file or the file should no longer support an active case. It doesn’t erase or change Decisions that were already created from an earlier Assessment input version.
[Verify a standalone document](/guides/verify-document/)
[Document fact keys and credential assertions](/reference/facts-and-assertions/)
# Features and compliance packages
> Understand how workspace configuration determines assessment requirements.
Before Dockt can evaluate an Assessment, it needs to know which compliance rules your Workspace intends to apply. Workspace Features and social compliance packages provide that configuration.
**Features** is the active configuration for one Workspace. **Social compliance packages** are versioned sets of requirements and guidance that Features can enable. You configure them once for the Workspace; you don’t choose packages separately on every Assessment request.
## Social compliance packages
[Section titled “Social compliance packages”](#social-compliance-packages)
A social compliance package describes the possible evidence, requirements, and obligations for a compliance workflow. The package catalog is the list of packages your Workspace can select. Read it with `GET /v1/social-compliance-packages` and use its package codes when updating Features.
Package catalog entries describe what a package may evaluate. They are not a fixed document checklist. The evidence that is actually required can differ between workers because Dockt considers the worker identity and Assessment context together with the enabled packages.
Your integration should therefore use the catalog for Workspace configuration, then use each Assessment’s `requirements` as the checklist for that specific case.
## Workspace Features
[Section titled “Workspace Features”](#workspace-features)
Read the active Workspace configuration with `GET /v1/features`. Enable package codes with `PATCH /v1/features`:
```json
{
"social_compliance_packages": [
"PACKAGE_CODE"
]
}
```
Use package codes returned by the catalog. The API rejects package codes that can’t be enabled.
Configure Features before creating your first Assessment. A standalone Document can still be processed independently, but an Assessment needs Workspace Features to determine what to evaluate.
## Effect on Assessments
[Section titled “Effect on Assessments”](#effect-on-assessments)
When you create an Assessment, Dockt applies the Workspace’s current Features automatically. You don’t send a Features ID or package list in the Assessment request. The response records what was applied so the result remains understandable later:
* `features_id`, which identifies the configuration used.
* `applied_packages`, which records the applied package code and version.
* `input_requests`, which asks for context still needed.
* `requirements`, which reports required evidence and its status.
* `advisories`, which reports external or planning obligations.
`features_id` identifies the exact Workspace configuration used for that Assessment input version. `applied_packages` records the package code and version used by the evaluation. Store these fields with high-impact results when you need to show which configuration produced them.
Changing Features can cause existing Assessments to be evaluated again. A new result creates a new immutable Decision for a newer Assessment input version; it doesn’t rewrite an earlier Decision.
## Requirements and advisories
[Section titled “Requirements and advisories”](#requirements-and-advisories)
A **requirement** represents evidence that the current Assessment may need. Its `status` is:
* `missing`: required evidence has not been satisfied.
* `not_required`: the requirement doesn’t apply to this Assessment.
* `satisfied`: accepted evidence supports the requirement.
Use the requirement’s document ID lists to understand which uploads support it, are still processing, require review, or are invalid. This lets your product distinguish “upload still processing” from “evidence is missing” or “uploaded evidence needs review.”
An **advisory** describes an obligation or consideration that may sit outside the uploaded evidence. Its `severity` and optional `phase` indicate how and when to surface it. An advisory doesn’t satisfy evidence and doesn’t make an evidence requirement incomplete by itself; treat it as separate workflow guidance.
# Assessment inputs
> Worker fields, context value types, and every accepted Assessment context code.
An Assessment starts with a worker identity and optional context about the employment, assignment, project, and payment being evaluated. Dockt uses these inputs to determine which requirements apply.
You can send known context when you create the Assessment. If Dockt needs another value, the response includes an `input_requests` entry that gives you the code, type, reason, and allowed options.
## Request shape
[Section titled “Request shape”](#request-shape)
Create an Assessment with `POST /v1/assessments`:
```json
{
"external_id": "worker-case-1042",
"worker": {
"first_name": "Amina",
"last_name": "Diallo",
"date_of_birth": "1990-05-17",
"nationality": "FR"
},
"context": [
{ "code": "employment.relationship", "value": "posted_employee" },
{ "code": "employment.employer_country", "value": "FR" },
{ "code": "assessment.evaluation_date", "value": "2026-08-23" }
]
}
```
`external_id` and `external_profile` are optional. Use `external_id` to correlate the Assessment with a record in your system. Send `external_profile` only when your Workspace has a matching profile configured.
## Worker fields
[Section titled “Worker fields”](#worker-fields)
| Field | Type | Meaning |
| --------------- | ------------ | ------------------------------------------------------------------------ |
| `first_name` | string | Worker’s legal first name. Must not be empty. |
| `last_name` | string | Worker’s legal last name. Must not be empty. |
| `date_of_birth` | date | Worker’s date of birth as `YYYY-MM-DD`. |
| `nationality` | country code | Worker’s nationality as an ISO 3166-1 alpha-2 code such as `BE` or `FR`. |
The worker object identifies the person being assessed. Don’t place employment, employer, project, or assignment information in these fields; send it as context.
## Context value types
[Section titled “Context value types”](#context-value-types)
Every context item contains a `code` and one scalar `value`. Codes must be unique within one request.
| Value type | JSON value | Rules |
| --------------- | ---------- | ------------------------------------------------------------------------- |
| `string` | string | A non-empty value. Surrounding whitespace is removed. |
| `integer` | number | A whole number greater than or equal to zero. Don’t send a quoted number. |
| `boolean` | boolean | JSON `true` or `false`, not the strings `"true"` or `"false"`. |
| `date` | string | A valid calendar date in `YYYY-MM-DD` format. |
| `country_code` | string | A two-letter ISO 3166-1 alpha-2 code. Dockt normalizes it to uppercase. |
| `single_select` | string | One exact value from the options listed for that code. |
## Worker and employment context
[Section titled “Worker and employment context”](#worker-and-employment-context)
| Code | Type | Meaning and accepted values |
| ------------------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `employment.relationship` | `single_select` | Worker’s relationship to the assignment: `posted_employee`, `local_employee`, or `self_employed`. |
| `worker.origin` | `single_select` | Worker’s origin category for mobility rules: `eea_swiss` or `non_eea`. |
| `employment.posting_status` | `single_select` | Whether the employment is `posted` across borders or `local`. |
| `employment.employer_country` | `country_code` | Country in which the employer is established. |
| `employment.social_security_regime` | `single_select` | Social-security regime that applies: `eu_eea_swiss_coordination`, `uk_withdrawal_or_tca`, `bilateral_agreement`, `third_country_no_agreement`, `belgian_social_security`, or `unknown`. |
| `employment.vander_elst_required` | `boolean` | Whether the case must satisfy Vander Elst conditions. |
| `employment.vander_elst_candidate` | `boolean` | Whether the worker and assignment may qualify for a Vander Elst route and should be evaluated for it. |
| `employment.non_eea_temporary_services_exemption` | `boolean` | Whether a temporary-services exemption is claimed for a non-EEA self-employed worker. |
| `employment.limosa_exemption_claimed` | `boolean` | Whether the case claims an exemption from the Limosa declaration requirement. |
| `employment.limosa_exemption_reason` | `string` | Reason supplied for the claimed Limosa exemption. |
| `employer.has_belgian_vat` | `boolean` | Whether the employer has a Belgian VAT number or KBO registration. |
| `employer.established_in_eea_swiss` | `boolean` | Whether the posting employer is genuinely established in the EEA or Switzerland. |
| `employer.has_belgian_establishment` | `boolean` | Whether the employer has an establishment in Belgium. |
| `employer.is_temporary_agency` | `boolean` | Whether the employer operates as a temporary-employment agency. |
| `assignment.duration_days` | `integer` | Expected total length of the assignment in calendar days. |
| `assignment.residency_duration_days` | `integer` | Relevant period of legal residence, in days. |
| `assignment.be.stay_duration_days` | `integer` | Expected number of days the worker will stay in Belgium. |
| `assignment.be.duration_days_in_180` | `integer` | Number of assignment days that fall within the relevant rolling 180-day window. |
| `assignment.is_cleaning_sector` | `boolean` | Whether the worker’s assignment is in the cleaning sector. |
## Evaluation and project context
[Section titled “Evaluation and project context”](#evaluation-and-project-context)
| Code | Type | Meaning and accepted values |
| ------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------- |
| `assessment.evaluation_date` | `date` | Date on which Dockt should evaluate the case. Use the relevant business date, not automatically the upload date. |
| `project.be.worksite_region` | `single_select` | Belgian region of the worksite: `flanders`, `brussels`, `wallonia`, `german_community`, or `unknown`. |
| `project.is_construction` | `boolean` | Whether the project is a construction project. |
| `project.be.activity_30bis` | `boolean` | Whether the work is immovable work covered by the Belgian 30bis rules. |
| `project.be.activity_ready_mixed_concrete` | `boolean` | Whether the project includes ready-mixed concrete activity. |
| `project.be.activity_meat_30ter` | `boolean` | Whether the activity falls under the meat-sector 30ter rules. |
| `project.be.activity_guarding_30ter` | `boolean` | Whether the activity is guarding or surveillance covered by 30ter rules. |
| `project.is_cleaning_for_third_party` | `boolean` | Whether cleaning work is performed for a third party. |
| `project.temporary_mobile_construction_site` | `boolean` | Whether the worksite is a temporary or mobile construction site. |
| `project.asbestos_hazardous_works` | `boolean` | Whether the project includes asbestos or other hazardous works. |
| `project.contract_value_ex_vat` | `integer` | Contract value excluding VAT, expressed as the whole-number amount used by your workflow. |
| `project.site_total_cost_ex_vat` | `integer` | Total site cost excluding VAT, expressed as a whole-number amount. |
| `project.subcontractor_count` | `integer` | Number of subcontractors involved in the project. |
| `project.subcontract_chain_depth` | `integer` | Number of levels in the subcontracting chain. |
| `project.customer_role` | `single_select` | Your organization’s role: `principal`, `main_contractor`, `intermediate_contractor`, `subcontractor`, or `unknown`. |
| `project.relationship_to_contractor` | `single_select` | Whether the relationship to the relevant contractor is `direct`, `indirect`, or `unknown`. |
| `project.direct_contractor_risk_sector` | `boolean` | Whether the direct contractor operates in a sector subject to the evaluated risk rules. |
| `project.contractor_uses_third_country_nationals` | `boolean` | Whether the contractor uses workers who are nationals of countries outside the applicable free-movement area. |
| `project.include_payment_withholding_checks` | `boolean` | Whether the Assessment should include payment-withholding checks. |
| `project.include_posted_worker_inspection_file` | `boolean` | Whether the workflow should include the posted-worker inspection-file requirement. |
| `project.include_technical_hse_requirements` | `boolean` | Whether technical health, safety, and environment requirements should be included. |
## Payment context
[Section titled “Payment context”](#payment-context)
| Code | Type | Meaning |
| ------------------------------------- | --------- | ----------------------------------------------------------------- |
| `payment.planned` | `boolean` | Whether a payment is planned for the evaluated case. |
| `payment.invoice_amount_ex_vat` | `integer` | Invoice amount excluding VAT, expressed as a whole-number amount. |
| `payment.invoice_payment_date` | `date` | Planned or actual invoice payment date. |
| `payment.contractor_or_subcontractor` | `boolean` | Whether the payment is made to a contractor or subcontractor. |
## Respond to input requests
[Section titled “Respond to input requests”](#respond-to-input-requests)
An Assessment response may ask for a context value:
```json
{
"code": "project.customer_role",
"label": "Customer role",
"reason": "The applicable requirement depends on your role in the contracting chain.",
"value_type": "single_select",
"options": [
{ "value": "principal", "label": "principal" },
{ "value": "main_contractor", "label": "main contractor" }
]
}
```
Use `code` unchanged, render an input appropriate for `value_type`, and restrict selectable values to `options` when the array is non-empty. Show `label` to the user and use `reason` to explain why the information is needed.
Update the Assessment with `PATCH /v1/assessments/{assessmentId}`. Context values merge by code, so send only the values you want to add or replace.
Treat this page as the complete accepted code catalog for the current API contract. Your integration should still render unfamiliar future `input_requests` from their returned `value_type` and `options` instead of rejecting the entire Assessment response.
# Facts and credential assertions
> Document fact keys, credential assertion fields, and Decision fact codes.
Dockt exposes evidence at two levels:
* A Document’s `facts` describe values read from that specific file.
* A Decision’s `facts` contain a smaller allowlisted set of values resolved across the Assessment, with subject and source lineage.
Documents can also include `verifications`, `enrichment.checks`, and `credential_assertions` when Dockt performs applicable authoritative or external checks.
Use this page as the value and code reference after you know which response field you are handling.
## Document facts shape
[Section titled “Document facts shape”](#document-facts-shape)
`Document.facts` is an object keyed by fact name. Every key contains an array because one file can contain the same kind of value more than once.
```json
{
"facts": {
"holder_name": [
{ "value": "Amina Diallo", "confidence": 0.99 }
],
"valid_until": [
{ "value": "2027-05-17", "confidence": 0.96 }
]
}
}
```
Each entry contains:
| Field | Type | Meaning |
| ------------ | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `value` | string, number, boolean, object, or `null` | Extracted value. Dates use `YYYY-MM-DD` strings when a calendar date is available. |
| `confidence` | number or `null` | Extraction confidence from `0` to `1`, or `null` when no confidence is available. Confidence is not a verification result. |
Only facts found on the file are returned. A missing key means Dockt didn’t expose a value for that fact; it doesn’t by itself mean that the document is invalid. Use `document_result`, `issues`, and `findings` for the conclusion.
### Document and issuer facts
[Section titled “Document and issuer facts”](#document-and-issuer-facts)
| Key | Value type | Meaning |
| --------------------------- | ---------- | ------------------------------------------------------------------------ |
| `document_type` | string | Document type stated or identified for the file. |
| `certificate_type` | string | Certificate category or qualification type shown on the document. |
| `document_number` | string | General identifier printed on the document. |
| `authority_check_url` | string | URL printed on the document for an authority or validity check. |
| `authority_check_reference` | string | Reference value used with an authority check, such as a validation code. |
| `diploma_number` | string | Diploma, certificate, or equivalent evidence number. |
| `issue_date` | date | Date on which the document was issued. |
| `valid_from` | date | First date on which the document or authorization is valid. |
| `valid_until` | date | Last date on which the document or authorization is valid. |
| `issuing_body` | string | Organization or authority that issued the document. |
| `issuer_name` | string | Issuer name as shown on the document. |
| `issuing_center` | string | Issuing, examination, or training center. |
| `issuer_country` | string | Country associated with the issuer. |
| `document_country` | string | Country to which the document belongs or in which it was issued. |
| `language` | string | Language identified for the document. |
### Worker identity facts
[Section titled “Worker identity facts”](#worker-identity-facts)
| Key | Value type | Meaning |
| -------------------------- | ---------- | ------------------------------------------------------------- |
| `holder_name` | string | Full name of the person who holds the document or credential. |
| `first_name` | string | Holder’s first or given name. |
| `last_name` | string | Holder’s last or family name. |
| `date_of_birth` | date | Holder’s date of birth. |
| `nationality` | string | Holder’s nationality as printed or encoded on the document. |
| `person_identifier` | string | Person-specific identifier other than a document number. |
| `identity_document_number` | string | Passport, identity card, or other identity-document number. |
### Employer and organization facts
[Section titled “Employer and organization facts”](#employer-and-organization-facts)
| Key | Value type | Meaning |
| --------------------------------------- | ---------- | ---------------------------------------------------------------------- |
| `employer_name` | string | Legal or stated name of the worker’s employer. |
| `employer_registration_type` | string | Type of employer registration identifier shown. |
| `employer_registration_number` | string | Employer registration identifier. |
| `employer_country` | string | Country in which the employer is established or registered. |
| `employer_address` | string | Employer address shown on the document. |
| `employer_vat_number` | string | Employer VAT identifier. |
| `service_recipient_name` | string | Name of the customer or organization receiving the service. |
| `service_recipient_registration_type` | string | Type of registration identifier used for the service recipient. |
| `service_recipient_registration_number` | string | Service recipient’s registration identifier. |
| `kbo_number` | string | Belgian Crossroads Bank for Enterprises registration number. |
| `company_registration_status` | string | Company registration state shown by the evidence. |
| `declaration_of_works_number` | string | Reference for a Declaration of Works or equivalent chain registration. |
### Assignment and employment facts
[Section titled “Assignment and employment facts”](#assignment-and-employment-facts)
| Key | Value type | Meaning |
| -------------------------------- | ---------- | ------------------------------------------------------------------------------ |
| `assignment_start_date` | date | Date on which the assignment starts. |
| `assignment_end_date` | date | Date on which the assignment ends. |
| `assignment_activity` | string | Work or activity described for the assignment. |
| `worksite_name` | string | Name of the site where the work takes place. |
| `worksite_address` | string | Address of the worksite. |
| `project_reference` | string | Project, site, or assignment reference. |
| `social_security_country` | string | Country whose social-security system covers the worker. |
| `posting_declaration_number` | string | Identifier of a worker-posting declaration or notification. |
| `notification_status` | string | Status stated for the posting or other notification. |
| `employment_registration_number` | string | Identifier of an employment registration. |
| `employment_start_date` | date | Employment start date shown in the evidence. |
| `employment_end_date` | date | Employment end date shown in the evidence. |
| `contract_type` | string | Employment or engagement contract type. |
| `wage_amount` | number | Wage amount stated in the evidence. Read `wage_period` before interpreting it. |
| `wage_period` | string | Period or frequency associated with `wage_amount`. |
### Accommodation and access facts
[Section titled “Accommodation and access facts”](#accommodation-and-access-facts)
| Key | Value type | Meaning |
| ------------------------ | ---------- | ---------------------------------------------------------------- |
| `accommodation_address` | string | Address of accommodation provided or declared for the worker. |
| `accommodation_capacity` | number | Number of people the accommodation evidence states it can house. |
| `approval_authority` | string | Person, role, or organization that granted an approval. |
| `approval_date` | date | Date on which the approval was granted. |
| `approval_scope` | string | Work, site, or exception covered by the approval. |
### Permit and Vander Elst facts
[Section titled “Permit and Vander Elst facts”](#permit-and-vander-elst-facts)
| Key | Value type | Meaning |
| -------------------------- | ---------- | ---------------------------------------------------------------------------- |
| `work_permit_number` | string | Work-authorization or work-permit identifier. |
| `residence_permit_number` | string | Residence-permit identifier. |
| `permit_type` | string | Type or category of work or residence permit. |
| `permit_scope` | string | Work, location, employer, or activity covered by the permit. |
| `permit_remarks` | string | Conditions or remarks printed on the permit. |
| `legal_basis` | string | Legal basis stated for the authorization or exemption. |
| `vander_elst_reference` | string | Reference associated with Vander Elst evidence. |
| `prior_residence_days` | number | Number of days of prior legal residence supported by the evidence. |
| `permit_covers_assignment` | boolean | Whether the evidence states that the permit covers the evaluated assignment. |
| `employer_match_status` | string | Result of comparing the employer across relevant evidence. |
### Registration and operational facts
[Section titled “Registration and operational facts”](#registration-and-operational-facts)
| Key | Value type | Meaning |
| --------------------------------------- | ---------- | ----------------------------------------------------------- |
| `debt_check_date` | date | Date on which fiscal or social debt status was checked. |
| `debt_status` | string | Fiscal or social debt state returned by the evidence. |
| `check_in_reference` | string | Reference for a worksite attendance registration. |
| `check_in_at` | string | Check-in timestamp as supplied by the evidence. |
| `check_out_at` | string | Check-out timestamp as supplied by the evidence. |
| `professional_card_number` | string | Professional-card identifier for a self-employed worker. |
| `social_contribution_period` | string | Period covered by social-contribution evidence. |
| `social_contribution_status` | string | Payment or affiliation status for social contributions. |
| `temporary_agency_accreditation_number` | string | Accreditation identifier for a temporary-employment agency. |
### Skill and professional facts
[Section titled “Skill and professional facts”](#skill-and-professional-facts)
| Key | Value type | Meaning |
| ----------------------------------- | ---------- | -------------------------------------------------------------- |
| `skill_scope` | string | Trade, task, role, or competency covered by a certificate. |
| `training_level` | string | Level or category of the completed training. |
| `professional_registration_number` | string | Registration number for a regulated professional. |
| `regulated_profession` | string | Regulated profession named in the evidence. |
| `professional_authorization_status` | string | Status of the authorization to practise the profession. |
| `screening_profile` | string | Role or screening profile against which a check was performed. |
| `screening_result` | string | Outcome stated by the background-screening evidence. |
The tables list all 80 public Document fact keys currently supported by Dockt. Only keys applicable to a particular document appear in its response. The response schema allows new keys, so ignore or store unfamiliar keys rather than failing the entire response. Raw document text is never returned as a fact.
## Document external checks
[Section titled “Document external checks”](#document-external-checks)
`Document.enrichment.checks` summarizes applicable evidence checks that are separate from the file’s intrinsic `document_result`:
```json
{
"code": "organization.vat_registration",
"status": "completed",
"source": "SOURCE_CODE",
"reason": null
}
```
| Field | Values | Meaning |
| -------- | ------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `code` | See the table below | Stable code for the kind of check performed. |
| `status` | `completed`, `not_found`, `ambiguous`, `unsupported`, `unavailable` | Outcome of that check. It is separate from `document_result`. |
| `source` | string or `null` | Customer-safe identifier for the source used. Source identifiers are not a closed enum. |
| `reason` | string or `null` | Explanation supplied for the outcome when available. |
### Enrichment check codes
[Section titled “Enrichment check codes”](#enrichment-check-codes)
| Code | Meaning |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `organization.registry_profile` | Checks an employer’s official registration profile. |
| `organization.vat_registration` | Validates an employer VAT registration. |
| `organization.customs_registration` | Validates an employer customs or EORI registration. |
| `organization.jurisdiction.restrictive_measures` | Looks up restrictive measures associated with an employer jurisdiction. This is a country-level lookup, not a conclusion that the employer is sanctioned. |
| `credential.vca.registry_profile` | Looks up VCA credentials associated with the worker. Confirmed credentials can also appear in `credential_assertions`. |
| `document.issuer.social_security_directory` | Checks whether a social-security document issuer matches the applicable institution directory. |
| `document.a1_authority_verification` | Validates an A1 document through its applicable authority check. |
| `document.limosa_authority_verification` | Validates a Limosa declaration through its applicable authority check. |
| `document.vca_registry_verification` | Validates VCA document evidence through the applicable register. |
An intrinsic authority check may appear in the Document’s `verifications` array instead of `enrichment.checks`. `verifications` uses the statuses `verified`, `not_found`, `invalid`, `expired`, `error`, and `not_checked` and includes an optional source and reason.
## Credential assertions
[Section titled “Credential assertions”](#credential-assertions)
`credential_assertions` contains credentials established through an applicable authoritative check. It is separate from extracted `facts`: a printed certificate label can be a fact, while an assertion records the credential that was actually confirmed.
```json
{
"scheme_code": "SCHEME_CODE",
"definition_code": "QUALIFICATION_CODE",
"source": "registry",
"status": "verified",
"holder_name": "Amina Diallo",
"certificate_number": "CERTIFICATE_NUMBER",
"valid_from": "2022-05-17",
"valid_until": "2032-05-16"
}
```
| Field | Type | Meaning |
| -------------------- | ---------------- | ------------------------------------------------------------------------ |
| `scheme_code` | string | Identifier of the credential scheme. |
| `definition_code` | string | Canonical qualification or credential code confirmed within that scheme. |
| `source` | string | Kind of source that established the assertion. |
| `status` | string | Status returned for the asserted credential. |
| `holder_name` | string or `null` | Confirmed holder name when available. |
| `certificate_number` | string or `null` | Confirmed certificate identifier when available. |
| `valid_from` | string or `null` | Confirmed validity start when available. |
| `valid_until` | string or `null` | Confirmed validity end when available. |
Treat `scheme_code`, `definition_code`, `source`, and `status` as public identifiers rather than closed enums. Available credential definitions can vary with the configured catalog.
## Decision facts shape
[Section titled “Decision facts shape”](#decision-facts-shape)
Decision facts are resolved values included in the immutable result returned by `GET /v1/decisions/{decisionId}`:
```json
{
"id": "fact_example",
"code": "organization.legal_name",
"subject": {
"id": "employer:1",
"type": "employer"
},
"value": "Example Employer NV",
"resolution": "corroborated",
"assurance": "high",
"sources": [
{
"kind": "document",
"code": "assessment_document",
"document_ids": ["doc_example"],
"observed_at": null,
"fresh_until": null
}
]
}
```
| Field | Values | Meaning |
| -------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | string | Identifier of this frozen fact in the Decision. |
| `code` | See the table below | Semantic name of the resolved value. |
| `subject.type` | `worker`, `employer`, `assignment`, `credential` | Kind of entity the fact describes. |
| `subject.id` | string | Subject identifier inside the Decision. Treat it as opaque. |
| `value` | string, number, boolean, or string array | Resolved value. The current fact codes normally return strings. |
| `resolution` | `resolved`, `corroborated` | `corroborated` means more than one consistent statement supports the value. |
| `assurance` | `low`, `medium`, `high` | Source-based assurance: customer input alone is low, document evidence is medium, and external or multiple source kinds produce high assurance. |
| `sources` | array | Lineage for the statements that support the value. |
### Decision fact codes
[Section titled “Decision fact codes”](#decision-fact-codes)
| Code | Subject | Meaning |
| ---------------------------------- | ---------- | -------------------------------------------------------------- |
| `person.name` | worker | Resolved full name of the worker. |
| `person.date_of_birth` | worker | Resolved worker date of birth. |
| `person.nationality` | worker | Resolved worker nationality. |
| `organization.legal_name` | employer | Resolved legal name of the employer. |
| `organization.registration_number` | employer | Resolved organization registration number. |
| `organization.vat_number` | employer | Resolved VAT identifier. |
| `organization.status` | employer | Resolved registration or operating status of the organization. |
| `assignment.worksite_country` | assignment | Country in which the assignment takes place. |
| `assignment.starts_on` | assignment | Resolved assignment start date. |
| `assignment.ends_on` | assignment | Resolved assignment end date. |
| `credential.type` | credential | Resolved credential or qualification code. |
| `credential.status` | credential | Resolved status of the credential. |
| `credential.valid_from` | credential | Resolved start of the credential’s validity period. |
| `credential.valid_until` | credential | Resolved end of the credential’s validity period. |
Only these 14 codes are currently included in public Decision facts. Other evidence can still affect requirements or Findings without appearing in this allowlisted array.
### How external checks become Decision facts
[Section titled “How external checks become Decision facts”](#how-external-checks-become-decision-facts)
Applicable external checks can add evidence to an Assessment. A value appears in public Decision `facts` only when its semantic code is one of the 14 allowlisted codes above, its value has a supported public shape, and the available statements resolve to one consistent value.
For example:
* An organization registration check can contribute `organization.legal_name`, `organization.registration_number`, or `organization.status`.
* A VAT check can contribute `organization.vat_number` or `organization.legal_name`.
* A credential register can contribute `credential.type`, `credential.status`, or `credential.valid_until`.
External checks can establish additional evidence that isn’t part of the public fact allowlist. Those values may support requirements or Findings, but Dockt doesn’t return their internal evidence codes in `Decision.facts`. This keeps the public result stable while still preserving the relevant conclusion and source lineage.
### Decision fact sources
[Section titled “Decision fact sources”](#decision-fact-sources)
| Field | Values | Meaning |
| -------------- | ----------------------------------------------- | --------------------------------------------------------------------------------- |
| `kind` | `customer_input`, `document`, `external_source` | Where the supporting statement originated. |
| `code` | string | Customer-safe source code. Treat it as an identifier and allow unfamiliar values. |
| `document_ids` | string array | Documents used by this source, when applicable. |
| `observed_at` | date-time or `null` | Time at which an external value was observed. |
| `fresh_until` | date-time or `null` | Time until which that observation is considered fresh, when available. |
Use the fact `code` and `subject.type` for application logic. Use `sources` to show evidence lineage or to explain why a resolved value has its assurance level. Don’t infer the Decision result by counting facts; use the top-level `decision`, requirements, and Findings.
## Finding codes
[Section titled “Finding codes”](#finding-codes)
Document and Decision Findings explain why a result needs attention. Use `code` and `impact` for routing; use the returned title and explanation for reviewer-facing text.
| Code | Meaning |
| ------------------------------------ | ----------------------------------------------------------------------------------------------- |
| `document.intrinsic_issue` | The Document itself contains an issue or signal that needs attention. |
| `identity.cross_document_conflict` | Verified evidence identifies a person who differs from the worker being assessed. |
| `evidence.required_unresolved` | Required Assessment information or evidence is missing, unresolved, or invalid. |
| `source.mandatory_unavailable` | Dockt couldn’t complete a required authoritative check. |
| `source.authoritative_contradiction` | An authoritative source contradicts a material claim or couldn’t find the claimed record. |
| `integrity.temporal_impossibility` | Evidence dates or validity periods form a timeline that can’t be reconciled. |
| `agent.unavailable` | A supporting evaluation step was unavailable and the result requires a safe fallback or review. |
The public Finding code set is allowlisted. Always handle an unfamiliar future code by falling back to its returned `impact`, `title`, and `explanation`.
# Document uploads and evidence
> File formats, classification fields, and Assessment evidence handling.
Dockt accepts a file, classifies what the file represents, and extracts public facts from it. In an Assessment, Dockt then decides whether that Document supports one of the worker’s evidence requirements.
These are separate questions:
* `classified_as` describes the uploaded file.
* `facts` contains values Dockt read from that file.
* `accepted_evidence_types` describes the purpose of evidence required by an Assessment.
* Requirement Document ID arrays show whether an uploaded Document actually supports that requirement.
Use `classified_as` to interpret the uploaded file. Use the Assessment’s requirements to determine whether that file satisfies an evidence need.
## Accepted file formats
[Section titled “Accepted file formats”](#accepted-file-formats)
Upload one non-empty file no larger than 10 MB. Dockt accepts these file formats:
| Format | Common media type |
| ------ | ----------------- |
| PDF | `application/pdf` |
| JPEG | `image/jpeg` |
| PNG | `image/png` |
| GIF | `image/gif` |
| WebP | `image/webp` |
Declare the media type when possible. If the upload uses `application/octet-stream` or omits a useful media type, Dockt inspects the file signature for these formats.
## Classification shape
[Section titled “Classification shape”](#classification-shape)
After classification, use the `classified_as` fields as follows:
* `family` groups related documents for display and reporting.
* `type` identifies the supported document behavior Dockt applied.
* `variant` distinguishes a document form when Dockt returns one. It can be `null`.
Treat all three values as open strings. Handle an unfamiliar future value without rejecting the entire Document response.
## Facts are conditional
[Section titled “Facts are conditional”](#facts-are-conditional)
A fact appears only when Dockt exposes a value from that file. A missing fact key does not, by itself, mean the Document is invalid.
Use:
1. `status` to determine whether processing is complete.
2. `document_result` for the top-level Document conclusion.
3. `issues`, `fraud_signals`, and `findings` for actionable reasons.
4. `facts`, `verifications`, `enrichment`, and `credential_assertions` for supporting detail.
The [Facts and credential assertions reference](/reference/facts-and-assertions/) lists every public fact and check code.
## Assessment evidence types
[Section titled “Assessment evidence types”](#assessment-evidence-types)
Social compliance package entries list possible evidence types before Dockt evaluates a specific worker. Read them with `GET /v1/social-compliance-packages`.
After you create an Assessment, use its `requirements` as the case-specific source of truth:
* Show `label` and `reason` to explain what is needed.
* Treat `accepted_evidence_types` as machine-readable evidence-purpose codes.
* Upload the file without assigning it to a requirement.
* After processing, use `supporting_document_ids`, `pending_document_ids`, `review_document_ids`, and `invalid_document_ids` to see how Dockt evaluated it.
Use the returned requirements as the checklist for that worker. Requirements depend on the worker context and the social compliance packages enabled for the Workspace.
## Unsupported documents
[Section titled “Unsupported documents”](#unsupported-documents)
If Dockt can read the file but cannot match a supported document type, the Document completes with:
* `status: "completed"`
* `document_result: "review_required"`
* An issue with `code: "type_unknown"`
This is a completed review outcome, not a processing failure. Ask for a supported document or route the file to a person for review.
# API compatibility and updates
> Version your Dockt client, review OpenAPI changes, and handle compatible contract growth.
Dockt product endpoints use the `/v1` route prefix. The public [OpenAPI JSON](https://api.dockt.com/openapi.json) describes the contract deployed at `https://api.dockt.com`.
Treat OpenAPI as an external dependency. Keep an approved contract or generated client version with your application instead of regenerating it without review during every build.
## Pin the contract you use
[Section titled “Pin the contract you use”](#pin-the-contract-you-use)
Download the contract when you intentionally update your integration:
```sh
curl --fail-with-body --silent --show-error \
https://api.dockt.com/openapi.json \
--output openapi.json
```
Commit the file or the client types generated from it. Review the diff before deployment, then run your contract and workflow tests.
The top-level OpenAPI `info.version` identifies the published contract document. Continue to use the `/v1` path prefix shown by each operation.
## Handle contract growth safely
[Section titled “Handle contract growth safely”](#handle-contract-growth-safely)
Write response parsing so a compatible contract addition does not stop an existing workflow:
* Ignore unknown object fields when your language and validation policy allow it.
* Give unfamiliar enum and code values a safe fallback.
* Use top-level result fields before optional supporting detail.
* Treat resource IDs and pagination cursors as opaque strings.
* Branch on Problem Details `type`, not its human-readable `title` or `detail`.
For a new Document classification, fact, check, or Finding code, preserve the response and use its returned human-readable explanation when available. Do not convert an unfamiliar value into success.
## Review state-machine changes
[Section titled “Review state-machine changes”](#review-state-machine-changes)
When an OpenAPI diff changes a status or result enum, review every branch in your integration:
* Document `status` and `document_result`.
* Assessment `status`, `input_requests`, and `latest_decision`.
* Decision `decision`, `signal`, Finding `impact`, and requirement `status`.
* Webhook event `type` and canonical resource reads.
* Problem Details `type` and HTTP status.
Keep a fallback for an unfamiliar value so your application can retain the response, avoid an unsafe automatic action, and route the case for investigation.
## Update generated clients
[Section titled “Update generated clients”](#update-generated-clients)
Use this update sequence:
1. Download the new OpenAPI document.
2. Diff it against the approved version in your application.
3. Regenerate client types in a review branch.
4. Compile the application and run parser tests against the curated examples.
5. Run authentication, idempotency, upload, webhook, and recovery tests in a test Workspace.
6. Deploy the client update through your normal release process.
Do not make generated client updates an unreviewed production build step.
## Diagnose an unexpected response
[Section titled “Diagnose an unexpected response”](#diagnose-an-unexpected-response)
If a response does not match the contract version you reviewed:
1. Retain the HTTP status, response headers, and redacted response body.
2. Record `x-request-id`.
3. Compare the operation with the deployed OpenAPI document.
4. Contact `hello@dockt.com` with the request ID and operation name.
Never include bearer tokens, webhook secrets, document contents, or raw personal data in a support request.
# Error reference
> Understand Dockt Problem Details responses and resolve each stable error type.
Dockt returns errors as [Problem Details](https://www.rfc-editor.org/rfc/rfc9457) with `Content-Type: application/problem+json`.
## Error shape
[Section titled “Error shape”](#error-shape)
```json
{
"type": "https://docs.dockt.com/errors/invalid-request",
"title": "Bad request",
"status": 400,
"detail": "The supplied cursor is invalid.",
"instance": "req_example1042",
"errors": {
"issues": [
{
"path": ["body", "worker", "nationality"],
"message": "Expected a two-letter country code."
}
]
}
}
```
Use `type` as the stable machine-readable error code. Use `detail` and optional `errors.issues` to explain the specific request failure. `instance` is the Dockt request ID when available.
Don’t branch application logic on `title` or `detail`; those fields are written for people and can become more specific.
## HTTP behavior
[Section titled “HTTP behavior”](#http-behavior)
A `401` response includes `WWW-Authenticate`. A `429` response includes `Retry-After` in seconds. Every API response includes `x-request-id`; log it and include it when contacting `hello@dockt.com`.
## Error types
[Section titled “Error types”](#error-types)
| Error type | Typical status | Action |
| -------------------------------------------------------------------------- | -------------: | ----------------------------------------------------------------- |
| [Account user conflict](/errors/account-user-conflict/) | 409 | Preserve an active owner or resolve membership state. |
| [Conflict](/errors/conflict/) | 409 | Resolve the duplicate or incompatible resource state. |
| [Forbidden](/errors/forbidden/) | 403 | Use a compatible scope and permission. |
| [Forbidden scope](/errors/forbidden-scope/) | 403 | Select a scope available to the session. |
| [Idempotency conflict](/errors/idempotency-conflict/) | 409 | Retry the same operation unchanged or use a new key for new work. |
| [Internal error](/errors/internal/) | 500 | Retry safely and retain the request ID. |
| [Authentication state error](/errors/internal-auth-state/) | 500 | Retry authentication and retain the request ID. |
| [Invalid account user](/errors/invalid-account-user/) | 400 | Correct the account user or invitation input. |
| [Invalid API credential](/errors/invalid-api-credential/) | 400 or 401 | Correct credential input or authentication. |
| [Invalid Assessment context](/errors/invalid-assessment-context/) | 400 | Correct context codes and value types. |
| [Invalid document upload](/errors/invalid-document-upload/) | 400 | Send one supported non-empty file within the size limit. |
| [Invalid Features](/errors/invalid-features/) | 400 | Use supported package codes and values. |
| [Invalid reference](/errors/invalid-reference/) | 422 | Use a referenced resource valid in the current scope. |
| [Invalid request](/errors/invalid-request/) | 400 | Correct the request syntax or cursor. |
| [Invalid session scope](/errors/invalid-session-scope/) | 409 | Select a valid session scope again. |
| [Missing authentication](/errors/missing-auth/) | 401 | Send a valid bearer token or browser session. |
| [Not found](/errors/not-found/) | 404 | Check the ID and authenticated scope. |
| [Operation unavailable](/errors/not-implemented/) | Varies | Use a documented public operation. |
| [Rate limit exceeded](/errors/rate-limit/) | 429 | Wait for `Retry-After` before retrying. |
| [Processing temporarily unavailable](/errors/runtime-routing-unavailable/) | 409 | Retry the idempotent request later. |
| [Scope context required](/errors/scope-context-required/) | 409 | Select an Account or Workspace in the session. |
| [Validation failed](/errors/validation/) | 422 | Correct every structured validation issue. |
| [Webhook inactive](/errors/webhook-inactive/) | 409 | Activate the endpoint before sending a test. |
| [Workspace Features required](/errors/workspace-features-required/) | 409 | Configure Features before creating Assessments. |
# Account user conflict
> Resolve an account membership change that conflicts with ownership or invitation state.
**Type:** `https://docs.dockt.com/errors/account-user-conflict`
**Status:** `409 Conflict`
The requested membership or invitation change conflicts with the Account’s current user state. This includes a change that would leave the Account without an active owner.
Read the current account users and invitations, preserve at least one active owner, then retry the intended change.
# Conflict
> Resolve a duplicate or incompatible resource state.
**Type:** `https://docs.dockt.com/errors/conflict`
**Status:** `409 Conflict`
The request conflicts with an existing resource or its current state. The response `detail` identifies the conflicting value or operation.
Read the existing resource, decide whether to update or reuse it, and retry only after the conflict is resolved.
# Forbidden
> Resolve a request that the authenticated principal isn't allowed to perform.
**Type:** `https://docs.dockt.com/errors/forbidden`
**Status:** `403 Forbidden`
Authentication succeeded, but the active scope, credential type, or granted permissions don’t allow the operation.
Call `GET /v1/auth/me`, confirm the required Account or Workspace scope, and compare `permissions` with the operation’s API reference. Don’t retry unchanged credentials automatically.
# Forbidden scope
> Resolve a browser session scope that isn't available to the signed-in user.
**Type:** `https://docs.dockt.com/errors/forbidden-scope`
**Status:** `403 Forbidden`
The Account or Workspace requested through `POST /v1/auth/scope` isn’t available to the current browser session.
Call `GET /v1/auth/me`, choose a scope from `available_scopes`, and retry the selection. API credentials have fixed scope and can’t select a different one.
# Idempotency conflict
> Resolve reuse of an idempotency key for changed or concurrent work.
**Type:** `https://docs.dockt.com/errors/idempotency-conflict`
**Status:** `409 Conflict`
The same `Idempotency-Key` is already associated with a different request body, or its first request is still running.
If this is a retry, send the exact same method, route, and body after the first request can complete. If this is new work, create and persist a new key. Don’t generate a new key merely to bypass an uncertain first request.
[Idempotency reference](/reference/idempotency/)
# Internal error
> Recover from an unexpected Dockt API failure.
**Type:** `https://docs.dockt.com/errors/internal`
**Status:** `500 Internal Server Error`
Dockt couldn’t complete the request because of an unexpected failure.
Retry only when the operation is safe to retry. Reuse the original idempotency key for supported writes. If the failure continues, contact `hello@dockt.com` with `x-request-id` or `instance` and the request time. Don’t include credentials or document contents.
# Authentication state error
> Recover from an unexpected failure while resolving authentication.
**Type:** `https://docs.dockt.com/errors/internal-auth-state`
**Status:** `500 Internal Server Error`
Dockt couldn’t resolve the request’s authentication state.
Retry the authentication request once. If the failure continues, start a new browser sign-in or obtain a new M2M access token as applicable, then contact `hello@dockt.com` with the request ID.
# Invalid account user
> Correct invalid account user, role, email, or invitation input.
**Type:** `https://docs.dockt.com/errors/invalid-account-user`
**Status:** `400 Bad Request`
The account user or invitation input isn’t valid for the requested operation.
Use the response `detail` to correct the email, role, invitation duration, or membership transition. Read the current Account users and invitations before retrying a state-dependent change.
# Invalid API credential
> Correct API credential configuration or bearer authentication.
**Type:** `https://docs.dockt.com/errors/invalid-api-credential`
**Status:** `400 Bad Request` or `401 Unauthorized`
Credential creation or update input is invalid, or the bearer credential can’t be authenticated.
For create and update requests, verify the credential type, target scope, expiration, and allowed permissions. For API calls, confirm the bearer value is complete, enabled, and unexpired. Obtain a new access token for an M2M credential before retrying an expired token.
# Invalid Assessment context
> Correct Assessment context codes, value types, or allowed values.
**Type:** `https://docs.dockt.com/errors/invalid-assessment-context`
**Status:** `400 Bad Request`
One or more Assessment context entries use an unknown code, incorrect value type, or unsupported value.
Read the response `detail` and the Assessment’s `input_requests`. Send each requested code with the declared `value_type` and one of its `options` when options are provided.
# Invalid document upload
> Correct an unsupported, empty, oversized, or malformed document upload.
**Type:** `https://docs.dockt.com/errors/invalid-document-upload`
**Status:** `400 Bad Request`
The multipart upload doesn’t contain one supported Document file. Files must be non-empty PDFs or images no larger than 10 MB.
Send one `file` part, declare an accurate MIME type when possible, and verify the file can be read before retrying. Use the response `detail` for the specific rejection reason.
# Invalid Features
> Correct unsupported workspace Features configuration.
**Type:** `https://docs.dockt.com/errors/invalid-features`
**Status:** `400 Bad Request`
The requested Features configuration contains an unsupported package code or invalid value.
Read `GET /v1/social-compliance-packages`, use package codes returned by the catalog, and retry `PATCH /v1/features` with the corrected list.
# Invalid reference
> Correct a resource reference that doesn't exist or can't be used in the current scope.
**Type:** `https://docs.dockt.com/errors/invalid-reference`
**Status:** `422 Unprocessable Content`
A referenced resource doesn’t exist, isn’t available in the authenticated scope, or can’t be used for this relationship.
Check the referenced ID, read it with the same credential, and confirm that all related resources belong to the active Workspace or Account before retrying.
# Invalid request
> Correct malformed request syntax, parameters, or pagination cursors.
**Type:** `https://docs.dockt.com/errors/invalid-request`
**Status:** `400 Bad Request`
Dockt couldn’t interpret the request. A common cause is an invalid pagination cursor.
Check the request method, path, query parameters, headers, and JSON syntax. Send cursors exactly as returned by the same collection endpoint and restart pagination when a cursor is no longer valid.
# Invalid session scope
> Replace an invalid or incomplete browser session scope selection.
**Type:** `https://docs.dockt.com/errors/invalid-session-scope`
**Status:** `409 Conflict`
The browser session contains an incomplete or invalid active scope selection.
Call `GET /v1/auth/me`, select an available Account or Workspace with `POST /v1/auth/scope`, and retry the original request. If no valid scope is available, complete Account or Workspace setup first.
# Missing authentication
> Add a valid Dockt bearer token or browser session.
**Type:** `https://docs.dockt.com/errors/missing-auth`
**Status:** `401 Unauthorized`
The endpoint requires authentication, but the request has no valid API credential or browser session.
Backend integrations must send `Authorization: Bearer YOUR_TOKEN`. Confirm that intermediaries preserve the header and that the token isn’t empty. Browser requests must complete sign-in before calling protected routes.
# Not found
> Resolve a resource that isn't available in the authenticated scope.
**Type:** `https://docs.dockt.com/errors/not-found`
**Status:** `404 Not Found`
The requested resource doesn’t exist or isn’t available within the authenticated scope.
Check the resource ID and route, then call `GET /v1/auth/me` to confirm the active Account or Workspace. A deleted, withdrawn, or differently scoped resource might not be readable with the current credential.
# Operation unavailable
> Use a documented public operation supported by the current API.
**Type:** `https://docs.dockt.com/errors/not-implemented`
The requested operation isn’t available through the current public API behavior. The HTTP status and `detail` describe the specific failure.
Check the [API reference](/api/) for the supported method and path. If the operation appears in the reference and still returns this type, contact `hello@dockt.com` with the request ID.
# Rate limit exceeded
> Wait for the retry window and reduce request volume.
**Type:** `https://docs.dockt.com/errors/rate-limit`
**Status:** `429 Too Many Requests`
The authenticated principal exceeded the request limit for the current window.
Wait for the number of seconds in `Retry-After`, add jitter, and retry. Reuse the original idempotency key for a supported write. Reduce polling or use webhooks if the same workflow repeatedly reaches the limit.
[Rate limits reference](/reference/rate-limits/)
# Processing temporarily unavailable
> Retry a verification request when asynchronous processing is temporarily unavailable.
**Type:** `https://docs.dockt.com/errors/runtime-routing-unavailable`
**Status:** `409 Conflict`
Dockt can’t accept asynchronous processing for the active Workspace at this time.
Retry later with exponential backoff. Reuse the original idempotency key and unchanged request body. If the response continues, contact `hello@dockt.com` with the request ID.
# Scope context required
> Select an active Account or Workspace before calling the operation.
**Type:** `https://docs.dockt.com/errors/scope-context-required`
**Status:** `409 Conflict`
The browser session has no valid active Account or Workspace for the requested operation.
Call `GET /v1/auth/me`, choose an entry from `available_scopes`, and send it to `POST /v1/auth/scope`. Complete Account or Workspace setup first if no scope is available.
# Validation failed
> Correct structured request field validation errors.
**Type:** `https://docs.dockt.com/errors/validation`
**Status:** `422 Unprocessable Content`
The request shape was understood, but one or more fields don’t satisfy the public schema.
Read every item in `errors.issues`. Use `path` to locate the field and `message` to correct it. Validate the updated request against [OpenAPI JSON](https://api.dockt.com/openapi.json) before retrying.
# Webhook inactive
> Activate a webhook endpoint before requesting a test delivery.
**Type:** `https://docs.dockt.com/errors/webhook-inactive`
**Status:** `409 Conflict`
`POST /v1/webhooks/{webhookId}/test` requires an active webhook endpoint.
Read the endpoint and confirm its destination URL. If it is disabled, create a new active endpoint or contact `hello@dockt.com`, then send the test to the active endpoint.
# Workspace Features required
> Configure the active Workspace before creating an Assessment.
**Type:** `https://docs.dockt.com/errors/workspace-features-required`
**Status:** `409 Conflict`
The active Workspace doesn’t have the Features configuration required to create an Assessment.
Complete Workspace setup, read `GET /v1/features`, and enable the intended social compliance package before retrying Assessment creation with the original idempotency key.
# API conventions
> Base URL, versioning, envelopes, identifiers, dates, headers, and status codes.
The Dockt API uses consistent HTTP, JSON, and resource conventions across `/v1`.
## Base URL
[Section titled “Base URL”](#base-url)
Send production requests to `https://api.dockt.com`. Product routes begin with `/v1`.
The API accepts and returns JSON for structured requests. Document uploads use `multipart/form-data`. Error responses use `application/problem+json`.
## Choose the relevant API area
[Section titled “Choose the relevant API area”](#choose-the-relevant-api-area)
A backend verification integration normally uses:
* `Auth` to inspect its authenticated Workspace and permissions.
* `Features` and `CompliancePackages` to read Workspace configuration.
* `Documents`, `Assessments`, and `Decisions` for verification workflows.
* `Webhooks` for asynchronous completion events.
The API reference also includes browser-session, Account, Workspace, account-user, and API-credential administration. You don’t need those operations when your backend already has a workspace credential. [Plan your backend integration](/getting-started/plan-integration/) provides a workflow-oriented endpoint map.
## Authentication
[Section titled “Authentication”](#authentication)
Send an API key or M2M access token with every protected request:
```http
Authorization: Bearer YOUR_TOKEN
```
[Authenticate your backend](/getting-started/authentication/)
## Response envelopes
[Section titled “Response envelopes”](#response-envelopes)
A single resource uses an `object: "single"` envelope:
```json
{
"object": "single",
"data": {
"object": "document",
"id": "doc_example"
}
}
```
A collection uses an `object: "list"` envelope:
```json
{
"object": "list",
"data": [],
"has_more": false,
"next_cursor": null
}
```
Deletion operations return a single envelope whose resource includes `deleted: true`.
## Resource identifiers
[Section titled “Resource identifiers”](#resource-identifiers)
Dockt IDs are opaque strings with a resource prefix. Common prefixes include:
* `acc_` for Accounts.
* `wsp_` for Workspaces.
* `feat_` for Features.
* `ast_` for Assessments.
* `doc_` for Documents.
* `dec_` for Decisions.
* `cred_` for API credentials.
* `whk_` for webhook endpoints.
* `evt_` for webhook events.
Treat IDs as case-sensitive opaque values. Don’t derive authorization or business state from a prefix.
## Dates and times
[Section titled “Dates and times”](#dates-and-times)
Date-time fields use ISO 8601 strings with a time-zone offset, such as `2026-08-06T09:30:00Z`.
Calendar-date fields use `YYYY-MM-DD`, such as `1990-05-17`. Don’t apply a time zone to calendar dates.
## Request correlation
[Section titled “Request correlation”](#request-correlation)
Dockt returns these headers:
* `x-request-id`: the request identifier to include in logs and support requests.
* `traceparent`: distributed trace context you can propagate according to your tracing setup.
Dockt accepts an incoming `traceparent` header. Never place personal data or secrets in tracing attributes.
## Status codes
[Section titled “Status codes”](#status-codes)
Common successful responses are:
* `200 OK` for reads, updates, deletions, and accepted invitation resends.
* `201 Created` when a resource or Outcome is created synchronously.
* `202 Accepted` when asynchronous Document processing or webhook test delivery is accepted.
Common error responses are:
* `400 Bad Request` for malformed input or an invalid cursor.
* `401 Unauthorized` for missing or invalid authentication.
* `403 Forbidden` for an incompatible scope or missing permission.
* `404 Not Found` when the resource isn’t available in the authenticated scope.
* `409 Conflict` for state or idempotency conflicts.
* `422 Unprocessable Content` for structured validation or invalid references.
* `429 Too Many Requests` when a rate limit is exceeded.
* `500 Internal Server Error` for an unexpected Dockt failure.
[Handle errors](/errors/)
## OpenAPI contract
[Section titled “OpenAPI contract”](#openapi-contract)
Use [OpenAPI JSON](https://api.dockt.com/openapi.json) for exact operation, parameter, request, response, enum, and permission definitions. The interactive [API reference](/api/) is generated from the same document.
Review [API compatibility and updates](/reference/api-compatibility/) before replacing generated client types.
# Authentication and permissions
> Credential types, fixed scopes, session scope selection, and public API permissions.
Dockt authorizes every request from its authenticated principal, fixed or selected scope, and granted permissions.
## Backend credentials
[Section titled “Backend credentials”](#backend-credentials)
Both `api_key` and `m2m` credentials use bearer authentication for Dockt API requests. An API key is the bearer value. An M2M credential exchanges its client ID and one-time client secret at its returned `token_url`, then uses the short-lived access token as the bearer value.
Secret material is returned only when a credential is created. Later credential reads return metadata and a key hint, not the secret.
## Credential scope
[Section titled “Credential scope”](#credential-scope)
An API credential has exactly one `scope_type`:
* `account`: Manages the Account, its Workspaces, and account-scoped credentials.
* `workspace`: Operates within one Workspace for configuration and verification workflows.
The scope is fixed when the credential is created. The API doesn’t accept a request header or body field that changes it.
## Browser sessions
[Section titled “Browser sessions”](#browser-sessions)
Browser sessions can use `POST /v1/auth/scope` to select an available Account or Workspace. The active selection applies to later session requests. API credentials can’t call this operation to change their fixed scope.
Use `GET /v1/auth/me` to read the current authentication type, scope, permissions, available session scopes, and active scope.
## Account permissions
[Section titled “Account permissions”](#account-permissions)
| Permission | Allows |
| --------------------------------- | ------------------------------------------------------------------ |
| `account:read` | Read the current Account. |
| `account:write` | Update Account metadata and manage account users where applicable. |
| `workspaces:read` | List and read Account Workspaces. |
| `workspaces:write` | Create and update Account Workspaces. |
| `api-credentials:read` | List credential metadata. |
| `api-credentials:manage` | Create, update, disable, and delete credentials. |
| `features:read` | Read Workspace Features where the account operation supports it. |
| `features:write` | Update Workspace Features where the account operation supports it. |
| `social-compliance-packages:read` | Read the social compliance package catalog. |
## Workspace permissions
[Section titled “Workspace permissions”](#workspace-permissions)
| Permission | Allows |
| --------------------------------- | ---------------------------------------------------------- |
| `workspace:read` | Read the active Workspace. |
| `features:read` | Read active Workspace Features. |
| `features:write` | Update active Workspace Features. |
| `social-compliance-packages:read` | Read the social compliance package catalog. |
| `webhooks:manage` | Create, read, update, delete, and test webhook endpoints. |
| `api-credentials:read` | List Workspace credential metadata. |
| `api-credentials:manage` | Create, update, disable, and delete Workspace credentials. |
| `assessments:read` | List and read Assessments. |
| `assessments:write` | Create and update Assessments and manage linked Documents. |
| `documents:read` | List and read Documents. |
| `documents:create` | Upload Documents. |
| `documents:delete` | Withdraw standalone Documents. |
| `decisions:read` | Read Decisions and Outcomes. |
| `decisions:outcome` | Report a Decision Outcome. |
The exact permission required by each operation appears in the [API reference](/api/).
## Authentication failures
[Section titled “Authentication failures”](#authentication-failures)
A `401 Unauthorized` response means the bearer value is missing, invalid, expired, or no longer enabled. The response includes `WWW-Authenticate`.
A `403 Forbidden` response means authentication succeeded, but the credential scope or permission doesn’t allow the operation. Check `GET /v1/auth/me` before changing application logic or credentials.
# Idempotency
> Retry duplicate-sensitive Dockt operations without creating duplicate resources.
Send `Idempotency-Key` on supported create, upload, and invitation-resend requests. The key lets you safely retry when your client doesn’t know whether Dockt completed the first request.
## Supported operations
[Section titled “Supported operations”](#supported-operations)
Dockt applies idempotency to:
* `POST /v1/workspaces`
* `POST /v1/account/invitations`
* `POST /v1/account/invitations/{invitationId}/resend`
* `POST /v1/account/api-credentials`
* `POST /v1/api-credentials`
* `POST /v1/assessments`
* `POST /v1/documents`
* `POST /v1/assessments/{assessmentId}/documents`
* `POST /v1/webhooks`
The OpenAPI operation includes the header when it is supported.
## Choose a key
[Section titled “Choose a key”](#choose-a-key)
Use a stable key that represents one logical operation in your system:
```http
Idempotency-Key: assessment-worker-case-1042-passport
```
Avoid personal data, filenames, bearer tokens, or other secrets in the key. A random value stored with your operation record also works.
Use the same key only when retrying the same method, route, and request body with the same credential or session principal. Use a new key for a different resource or changed request.
## Replay behavior
[Section titled “Replay behavior”](#replay-behavior)
Keys are scoped to the authenticated principal, HTTP method, and route and retained for 24 hours.
* The same key and equivalent request body replay the successful response.
* The replay receives the request ID for the current retry.
* Reusing a key with a different body returns `409 Conflict`.
* Reusing a key while the first request is still running returns `409 Conflict`.
For JSON requests, semantically equivalent object key ordering is treated consistently. For multipart uploads, the file content and part metadata are part of the request identity.
## Retry pattern
[Section titled “Retry pattern”](#retry-pattern)
1. Create and persist the idempotency key before the first request.
2. Send the request with explicit client timeouts.
3. If the connection fails or a retryable server response occurs, retry with the same key and body.
4. If Dockt returns an idempotency conflict, don’t generate a new key automatically. Determine whether the request changed or the first request is still active.
5. Store the returned Dockt resource ID with the key after success.
# Pagination
> Iterate through Dockt collection endpoints with opaque cursors.
Dockt collection endpoints use cursor pagination.
## Request a page
[Section titled “Request a page”](#request-a-page)
Use `limit` to request between 1 and 100 resources. The default is 20:
```http
GET /v1/assessments?limit=50
```
Collection responses include:
```json
{
"object": "list",
"data": [],
"has_more": true,
"next_cursor": "OPAQUE_CURSOR"
}
```
## Request the next page
[Section titled “Request the next page”](#request-the-next-page)
When `has_more` is `true`, send the returned `next_cursor` unchanged:
```http
GET /v1/assessments?limit=50&cursor=OPAQUE_CURSOR
```
Stop when `has_more` is `false` or `next_cursor` is `null`.
## Cursor rules
[Section titled “Cursor rules”](#cursor-rules)
* Treat the cursor as an opaque string.
* Don’t parse, modify, or construct cursors.
* Keep the same filters and page size while walking one collection view.
* Start a new pagination sequence when filters change.
* Handle an invalid or expired cursor as `400 Bad Request` and restart from the first page when appropriate.
* Don’t use a cursor from one endpoint on another endpoint.
Collection-specific filters appear in the [API reference](/api/).
# Rate limits
> Handle per-principal read, write, and upload rate limits.
Dockt applies approximate abuse-protection limits per authenticated principal:
| Request group | Limit |
| --------------------------- | ----------------------------: |
| Reads | 1,000 requests per 60 seconds |
| Writes | 200 requests per 60 seconds |
| Assessment document uploads | 20 requests per 60 seconds |
These limits are request protection, not contracted product quotas.
## Handle a rate-limit response
[Section titled “Handle a rate-limit response”](#handle-a-rate-limit-response)
When a limit is exceeded, Dockt returns `429 Too Many Requests` with an `application/problem+json` body and a `Retry-After` header in seconds.
1. Stop sending requests in the affected workflow.
2. Wait for `Retry-After`.
3. Add random jitter when multiple workers may retry together.
4. Retry with the original idempotency key when the operation supports idempotency.
5. Reduce polling frequency or use webhooks for asynchronous completion.
Don’t retry a `429` immediately. Coordinated immediate retries extend congestion and can delay recovery.
## Reduce request volume
[Section titled “Reduce request volume”](#reduce-request-volume)
* Use `assessment.completed`, `document.completed`, and `document.failed` webhooks.
* Cache stable catalog and configuration reads when appropriate for your application.
* Use collection filters and a larger `limit` instead of many small page requests.
* Avoid polling several views for the same resource state.
# Webhook delivery
> Event schemas, signature headers, retries, acknowledgement, ordering, and delivery health.
Dockt delivers signed JSON events to active webhook endpoints in a Workspace.
## Subscription events
[Section titled “Subscription events”](#subscription-events)
Subscribe an endpoint to any of these event types:
* `assessment.completed`
* `document.completed`
* `document.failed`
`POST /v1/webhooks/{webhookId}/test` sends a targeted `webhook.test` event without adding it to the subscription list.
## Delivery headers
[Section titled “Delivery headers”](#delivery-headers)
| Header | Meaning |
| -------------------- | ------------------------------------------------------------- |
| `X-Dockt-Event-Id` | Stable event ID used across retries. |
| `X-Dockt-Event-Type` | Event type, equal to the JSON body `type`. |
| `X-Dockt-Timestamp` | ISO 8601 timestamp included in the signed message. |
| `X-Dockt-Signature` | `v1=` followed by a lowercase hexadecimal HMAC-SHA256 digest. |
## Event envelope
[Section titled “Event envelope”](#event-envelope)
An Assessment completion event has this shape:
```json
{
"object": "event",
"id": "evt_example1042",
"type": "assessment.completed",
"created_at": "2026-08-06T09:35:00Z",
"data": {
"assessment_id": "ast_example1042",
"decision_id": "dec_example1042",
"decision": "compliant",
"signal": "green"
}
}
```
Document lifecycle events include a compact Document summary in `data.document`. Fetch the canonical Document for complete evidence detail.
The top-level `webhooks` section in [OpenAPI JSON](https://api.dockt.com/openapi.json) defines every receiver schema.
## Signature contract
[Section titled “Signature contract”](#signature-contract)
Verify `X-Dockt-Signature` as HMAC-SHA256 over:
```text
.
```
Use the endpoint’s one-time signing secret, lowercase hexadecimal encoding, the `v1=` prefix, and a constant-time comparison. Reject timestamps outside a five-minute replay window.
[Implement a webhook receiver](/guides/receive-webhooks/)
## Delivery behavior
[Section titled “Delivery behavior”](#delivery-behavior)
* Delivery is at least once.
* Event ordering isn’t guaranteed.
* Retries keep the same event ID and body.
* Any `2xx` response acknowledges the event.
* Non-2xx responses and timeouts retry up to 10 total attempts.
* Retry delays increase exponentially and are capped at about 60 seconds.
* The default request timeout is 10 seconds.
Persist or enqueue a verified event before acknowledging it. Deduplicate side effects by event ID.
## Delivery health
[Section titled “Delivery health”](#delivery-health)
Webhook endpoint reads include a `delivery` summary with the latest event type, result, attempt time, and successful-delivery time. The fields are `null` before the first attempt.
The summary describes only the latest persisted delivery health. Use your receiver’s event log and canonical API reads for complete recovery and reconciliation.