API

Compliance API reference

The live subject-check, analyst correction, re-review and verdict-override, sandbox, account API-key, and webhook signing endpoints in one focused reference.

API token lifecycle

Keep vda_ tokens server-side and treat the plaintext as a one-time credential. Follow this mint → store → rotate → revoke sequence to keep an integration live without leaving the API reference.

1. Mint. Use the account API-key endpoint with an authenticated session. Send Cookie: auth_token=<session> and Content-Type: application/json; the JSON body must include a name.

POST /api/account/api-keys
Cookie: auth_token=<session>
Content-Type: application/json

{"name":"production"}

2. Store. The 201 response’s key field contains the vda_ plaintext exactly once. Copy it immediately into server-side secret storage. Never put it in source control, browser code, request or console logs, or client-visible configuration. It cannot be reconstructed later: the service retains only the SHA-256 hash and token prefix. See the mint reference for the one-time response.

3. Rotate. For zero downtime, mint a second key, deploy the new secret, switch callers to Authorization: Bearer vda_<new-token>, verify traffic, then revoke the old key. For in-place rotation, use the POST /api/v1/compliance/api-keys/rotate endpoint with the current bearer header; its 201 response returns a one-time replacement key and invalidates the old token immediately.

POST /api/v1/compliance/api-keys/rotate
Authorization: Bearer vda_<current-token>

4. Revoke. Get the numeric key ID from the account key list endpoint, then use the account revoke endpoint with DELETE /api/account/api-keys/:id and Cookie: auth_token=<session>. Revocation is immediate; subsequent bearer calls return 401 with invalid_key.

DELETE /api/account/api-keys/<id>
Cookie: auth_token=<session>
Compromised token. If the current secret is available, rotate it immediately, save the one-time replacement, and switch callers. Otherwise, revoke it through the session-authenticated account endpoint, mint a replacement, deploy it, and remove the exposed value from secret storage, logs, and code.

Error handling

Branch on the HTTP status and stable error.code, not the human-readable message. The response body follows the examples below; messages may become more specific as validation and platform behavior evolve.

401 Unauthorized · invalid_key_format
{
  "error": {
    "code": "invalid_key_format",
    "message": "API key required. Use Authorization: Bearer vda_<token>"
  }
}
Remediation. Check for a missing or malformed Authorization: Bearer vda_<token> header, then follow the token lifecycle guidance. For invalid_key or key_expired, rotate or replace the revoked/expired token and see the detailed error-code contract.
400 Bad Request · invalid_payload
{
  "error": {
    "code": "invalid_payload",
    "message": "first_name is required",
    "errors": [
      "first_name is required",
      "last_name is required",
      "At least one location is required",
      "purpose is required",
      "Consent certification is required"
    ]
  }
}
Remediation. Check the request shape in the sandbox guidance and the full schema at POST /subject-checks/sandbox. Correct every named field in error.errors before retrying.
429 Too Many Requests · rate_limited
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded: 60 subject checks/hour. Check Retry-After header."
  }
}
Remediation. Read the accompanying Retry-After response header, whose value is in seconds; it is not part of the JSON body. Follow the rate-limit guidance and sandbox guidance, then wait/back off instead of retrying immediately.
500 Internal Server Error · internal_error
{
  "error": {
    "code": "internal_error",
    "message": "Failed to start subject check"
  }
}
Remediation. Retry the request, then follow the subject-check guidance and contact support if the failure persists.

Live endpoints

Every path below is registered by the current application. This page documents case decision history, analyst correction, re-review, and verdict-override request and response contracts; use the broader Compliance API reference for the other endpoints and full API guidance.

Live route names. Account key minting and listing use POST/GET /api/account/api-keys; revocation uses DELETE /api/account/api-keys/:id. In-place bearer rotation uses POST /api/v1/compliance/api-keys/rotate.

Subject check

Method
POST
Path
/api/v1/compliance/subject-checks
Required request headers
Authorization: Bearer vda_<token>
Content-Type: application/json
Auth model
Account-scoped third-party bearer authentication via authenticateAccountApiKey.
Description
Starts an asynchronous subject check and returns a 201 handoff with a running check ID.
400 Bad Request · invalid_payload
{
  "error": {
    "code": "invalid_payload",
    "message": "first_name is required",
    "errors": [
      "first_name is required",
      "last_name is required",
      "At least one location is required",
      "purpose is required",
      "Consent certification is required"
    ]
  }
}
401 Unauthorized · invalid_key_format
{
  "error": {
    "code": "invalid_key_format",
    "message": "API key required. Use Authorization: Bearer vda_<token>"
  }
}
500 Internal Server Error · internal_error
{
  "error": {
    "code": "internal_error",
    "message": "Failed to start subject check"
  }
}
Remediation. Correct the request payload and its required fields, supply a live vda_ bearer token, or retry and contact support if the server returns 500.

Sandbox subject check

Method
POST
Path
/api/v1/compliance/subject-checks/sandbox
Required request headers
Authorization: Bearer vda_<token>
Content-Type: application/json
Auth model
Account-scoped third-party bearer authentication via authenticateAccountApiKey.
Description
Returns a deterministic completed clear_no_match verdict without a database write, quota decrement, or rate-limit decrement.
400 Bad Request · invalid_payload
{
  "error": {
    "code": "invalid_payload",
    "message": "first_name is required",
    "errors": [
      "first_name is required",
      "last_name is required",
      "At least one location is required",
      "purpose is required",
      "Consent certification is required"
    ]
  }
}
401 Unauthorized · invalid_key_format
{
  "error": {
    "code": "invalid_key_format",
    "message": "API key required. Use Authorization: Bearer vda_<token>"
  }
}
500 Internal Server Error · internal_error
{
  "error": {
    "code": "internal_error",
    "message": "Failed to run sandbox subject check"
  }
}
Remediation. Payload and authentication failures are fixed by the caller: correct the request and supply a live vda_ bearer token. Retry a 500 sandbox failure.

Override a subject-check verdict

Method
POST
Path
/api/v1/compliance/subject-checks/:id/verdict-override
Required request headers
Authorization: Bearer vda_<token>
Content-Type: application/json
Auth model
The vda_ bearer token is scoped to its account. The account user associated with the token must currently have the analyst role.
Required fields
replacement_verdict: accepted, rejected, or escalated, and must differ from the current effective verdict. reason: a trimmed, nonempty string of at most 500 characters.
Path parameter
id must be a positive safe integer identifying a completed subject check in the token's account.
Request JSON
{
  "replacement_verdict": "rejected",
  "reason": "Analyst review confirmed a sanctions match."
}
200 OK
{
  "success": true,
  "subject_check_id": 12345,
  "verdict_override": {
    "id": 905,
    "person_check_id": 12345,
    "previous_disposition": "accepted",
    "corrected_disposition": "rejected",
    "corrected_by": 42,
    "reason": "Analyst review confirmed a sanctions match.",
    "created_at": "2026-10-11T14:02:11.082Z"
  },
  "correction_history": [{ /* newest record first; includes the saved override */ }]
}
Access and errors. The target must be a completed subject check owned by the bearer token's account; unknown and cross-tenant checks both return 404 not_found. Missing or invalid credentials return 401; a user without current analyst access gets 403 analyst_required. 400 invalid_payload covers an invalid ID, missing or unsupported replacement_verdict, or a missing, blank, or over-500-character reason. 409 decision_not_ready means the check is incomplete. 409 verdict_not_overridable means the current effective verdict is unsupported or already equals the replacement; no correction-history record is appended. On success the correction record is returned as verdict_override alongside correction_history, and the API emits the signed subject_check.verdict_overridden event with the persisted reason in override_context.

Correct a subject-check disposition

Method
POST
Path
/api/v1/compliance/subject-checks/:id/disposition-correction
Required request headers
Authorization: Bearer vda_<token>
Content-Type: application/json
Auth model
The vda_ bearer token is scoped to its account. The account user associated with the token must currently have the analyst role.
Required fields
disposition: accepted, rejected, or escalated. reason: a trimmed, nonempty string of at most 500 characters.
Path parameter
id must be a positive safe integer identifying a completed subject check.
Request JSON
{
  "disposition": "rejected",
  "reason": "Analyst review confirmed a sanctions match."
}
200 OK
{
  "success": true,
  "subject_check_id": 12345,
  "analyst_correction": {
    "id": 987,
    "person_check_id": 12345,
    "previous_disposition": "accepted",
    "corrected_disposition": "rejected",
    "corrected_by": 42,
    "actor_name": "Alex Analyst",
    "actor_email": "alex@example.com",
    "reason": "Analyst review confirmed a sanctions match.",
    "created_at": "2026-09-29T12:00:00.000Z"
  },
  "correction_history": [
    {
      "id": 987,
      "person_check_id": 12345,
      "previous_disposition": "accepted",
      "corrected_disposition": "rejected",
      "corrected_by": 42,
      "actor_name": "Alex Analyst",
      "actor_email": "alex@example.com",
      "reason": "Analyst review confirmed a sanctions match.",
      "created_at": "2026-09-29T12:00:00.000Z"
    }
  ]
}
400 Bad Request · invalid_payload
{
  "error": {
    "code": "invalid_payload",
    "message": "reason must be nonempty and at most 500 characters"
  }
}
Access and errors. Missing or invalid bearer credentials return 401 (invalid_key_format, invalid_key, or key_expired); a user without current analyst access gets 403 analyst_required. 400 invalid_payload covers a path ID that is not a positive safe integer, an unsupported disposition, or a missing, blank, or over-500-character reason. 404 not_found means the check is unknown or belongs to another tenant; those cases intentionally look the same. 409 decision_not_ready means the check is not completed.

Request an analyst re-review

Method
POST
Path
/api/v1/compliance/subject-checks/:id/re-review
Required request headers
Authorization: Bearer vda_<token>
Content-Type: application/json
Auth model
Account-scoped third-party bearer authentication. The current account user associated with the token must have the analyst role.
Required fields
outcome: accepted, rejected, or escalated. reason: a string trimmed by the API, nonempty after trimming, and at most 500 characters.
Path parameter
id must be a positive safe integer identifying a completed subject check in the token's account.
Request JSON
{
  "outcome": "accepted",
  "reason": "Updated review found no actionable match."
}
200 OK
{
  "success": true,
  "subject_check_id": 12345,
  "analyst_correction": {
    "id": 988,
    "person_check_id": 12345,
    "previous_disposition": "rejected",
    "corrected_disposition": "accepted",
    "corrected_by": 42,
    "actor_name": "Alex Analyst",
    "actor_email": "alex@example.com",
    "reason": "Updated review found no actionable match.",
    "created_at": "2026-10-07T12:00:00.000Z"
  },
  "correction_history": [
    {
      "id": 988,
      "person_check_id": 12345,
      "previous_disposition": "rejected",
      "corrected_disposition": "accepted",
      "corrected_by": 42,
      "actor_name": "Alex Analyst",
      "actor_email": "alex@example.com",
      "reason": "Updated review found no actionable match.",
      "created_at": "2026-10-07T12:00:00.000Z"
    }
  ]
}
Access and errors. The bearer token can only re-review checks owned by its account. A check owned by another tenant is intentionally hidden with the same 404 not_found response as an unknown check. The correction_history array is ordered newest first, so analyst_correction matches its first entry. Missing or invalid credentials return 401 (invalid_key_format, invalid_key, or key_expired); a user without current analyst access gets 403 analyst_required. 400 invalid_payload covers an ID that is not a positive safe integer, a missing or unsupported outcome, or a missing, non-string, blank-after-trimming, or over-500-character reason. 409 decision_not_ready means the check is not completed.
400 Bad Request · invalid_payload
{
  "error": {
    "code": "invalid_payload",
    "message": "reason must be nonempty and at most 500 characters"
  }
}

Case decision history

Method
GET
Path
/api/v1/compliance/cases/:caseId/decision-history
Required request headers
Authorization: Bearer vda_<token>
Auth model
Account-scoped third-party bearer authentication. Both the case and its associated person check must belong to the bearer key's account.
Path parameter
caseId must be a UUID.
History event fields
automated_verdict and enhanced_review entries include id, type, timestamp, verdict, actor_id, actor_name, actor_email, and reason. re_review entries include id, type, timestamp, previous_disposition, resulting_disposition, the actor fields, reason, and is_override. Timestamps, actor fields, reasons, and previous dispositions may be null. Events are returned oldest first; fields specific to other event types are omitted.
curl
curl -i -X GET https://veridact.solutions/api/v1/compliance/cases/550e8400-e29b-41d4-a716-446655440000/decision-history \
  -H "Authorization: Bearer vda_<token>"
200 OK · oldest first
{
  "success": true,
  "case_id": "550e8400-e29b-41d4-a716-446655440000",
  "decision_history": [
    {
      "id": "automated:12345",
      "type": "automated_verdict",
      "timestamp": "2026-09-28T13:00:00.000Z",
      "verdict": "concerning",
      "actor_id": null,
      "actor_name": null,
      "actor_email": null,
      "reason": null
    },
    {
      "id": "enhanced_review:550e8400-e29b-41d4-a716-446655440000",
      "type": "enhanced_review",
      "timestamp": "2026-09-28T13:30:00.000Z",
      "verdict": "escalate",
      "actor_id": 44,
      "actor_name": "Analyst Name",
      "actor_email": "analyst@example.com",
      "reason": "Additional review required"
    },
    {
      "id": "correction:901",
      "type": "re_review",
      "timestamp": "2026-09-28T14:02:11.082Z",
      "previous_disposition": "accepted",
      "resulting_disposition": "rejected",
      "actor_id": 44,
      "actor_name": "Analyst Name",
      "actor_email": "analyst@example.com",
      "reason": "Disposition overridden",
      "is_override": true
    }
  ]
}
Access and errors. A malformed UUID returns 400 invalid_payload. A missing case, a case owned by another account, or a case whose associated person check belongs to another account returns the same 404 not_found response. Missing or invalid bearer credentials return 401.

Mint an account API key

Method
POST
Path
/api/account/api-keys
Required request headers
Cookie: auth_token=<session>
Content-Type: application/json
Auth model
Authenticated browser/session JWT via authenticateToken, scoped to the signed-in account.
Description
Mints a vda_ integration key and returns its plaintext exactly once.
401 Unauthorized
{
  "success": false,
  "message": "Authentication required"
}
400 Bad Request
{
  "success": false,
  "message": "name is required (1-100 characters)"
}
201 Created
{
  "success": true,
  "id": 42,
  "name": "production",
  "token_prefix": "vda_a1b2",
  "created_at": "2026-09-12T12:00:00.000Z",
  "key": "vda_<token>"
}
500 Internal Server Error
{
  "success": false,
  "message": "Failed to mint API key"
}
Remediation. Sign in again or refresh the session, provide a key name from 1–100 characters, or retry and report a server failure to support.

List account API keys

Method
GET
Path
/api/account/api-keys
Required request headers
Cookie: auth_token=<session>
Auth model
Authenticated browser/session JWT via authenticateToken, scoped to the signed-in account.
Description
Lists key IDs and masked metadata for the signed-in account. Plaintext secrets are never returned.
200 OK
{
  "success": true,
  "keys": [
    {
      "id": 42,
      "name": "production",
      "token_prefix": "vda_a1b2",
      "created_at": "2026-09-12T12:00:00.000Z",
      "last_used_at": null,
      "revoked_at": null
    }
  ]
}
Secret handling. Use id from this response for revocation. token_prefix is prefix-only metadata; the plaintext key is not listed or recoverable.

Rotate an account API key in place

Method
POST
Path
/api/v1/compliance/api-keys/rotate
Required request headers
Authorization: Bearer vda_<current-token>
Auth model
Account-scoped third-party bearer authentication via authenticateAccountApiKey.
Description
Replaces the current token on the same key row, invalidates the old token immediately, and returns the replacement plaintext exactly once. The request has no body.
201 Created
{
  "success": true,
  "id": 42,
  "account_id": 7,
  "token_prefix": "vda_c3d4",
  "created_at": "2026-09-12T12:05:00.000Z",
  "key": "vda_<replacement-token>"
}
One-time response. Store the replacement immediately; the old bearer token returns 401 with invalid_key after rotation.

Revoke an account API key

Method
DELETE
Path
/api/account/api-keys/:id
Required request headers
Cookie: auth_token=<session>
No body header required.
Auth model
Authenticated browser/session JWT via authenticateToken, scoped to the signed-in account.
Description
Immediately revokes the selected API key when it belongs to the authenticated account.
401 Unauthorized
{
  "success": false,
  "message": "Authentication required"
}
400 Bad Request
{
  "success": false,
  "message": "Invalid key id"
}
404 Not Found
{
  "success": false,
  "message": "API key not found"
}
500 Internal Server Error
{
  "success": false,
  "message": "Failed to revoke API key"
}
Remediation. Sign in again or refresh the session, send a valid numeric key ID, or retry and report a server failure to support.

Webhook signing

Method
POST setup
POST delivery
Path
/api/v1/compliance/webhooks setup
<registered callback URL> delivery
Required request headers
Setup: Authorization: Bearer vda_<token> Content-Type: application/json
Delivery: Content-Type: application/json X-Veridact-Signature: sha256=<hex>
Auth model
The setup call uses the account-scoped bearer token; your callback verifies delivery with the returned signing_secret.
Description
Registers or updates a callback URL, then signs each outbound JSON POST with HMAC-SHA256 over the exact raw JSON bytes.
Verify the exact body bytes
const crypto = require('crypto');
const signature = req.get('X-Veridact-Signature');
const expected = 'sha256=' + crypto
  .createHmac('sha256', signingSecret)
  .update(rawBody)
  .digest('hex');
const valid = signature === expected;
Signing note. Store the signing_secret returned by the setup call and preserve the raw request bytes before parsing the JSON body.
Was this page helpful?