HHeros Developers

Version 1.0.0

Heros ANCHOR PRO

Identity resolution for Nigeria. One real person, not several accounts.

Base URLhttps://sandbox.droomwork.io
AuthenticationDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…
Events it sends8 webhooks →
Signed in: requests you run on this page use a key made for your account. Your keys →

ANCHOR answers one question for you: is this a real person, and is it the same person you saw before.

What this API gives you, and what it will not

You get decisions, never raw personal data. You learn the assurance level reached, whether a disqualifying flag exists, and what evidence was consulted. You don't receive the identity number, the biometric template or the underlying government record. There is no setting that changes this.

Identity numbers are never returned in full. Where a number has to be shown at all, it's masked. If you need to show a subject something, show them the mask.

No consent, no query. Before any source is touched, you need a verifiable consent token bound to the subject, your organisation, the purpose and the specific checks. This holds however your organisation is configured and whatever anyone has attested to. It's data protection law, not vetting policy.

A source that doesn't answer returns unresolved, naming the source. Never a rejection and never a silent pass. Silence isn't evidence.

A probable duplicate goes to a person. A strong identifier match merges on its own. Anything weaker is raised for you to adjudicate.

Assurance levels

An identity carries the level it reached and how. This release delivers up to DAL-2.

LevelMeans
DAL-0Claimed. Someone said so
DAL-1Contactable. A channel was proved
DAL-2Matched against a government identifier on name and date of birth
DAL-3Biometric binding. Not in this release
DAL-4Adverse screening cleared. Not in this release

Getting started

Collect consent. Submit a verification. Read the Passport. Everything downstream of identity, in every module, checks that the Passport is live before it acts.

Every operation below has two samples: Direct HTTP, with nothing but your language's own client, and Client library, with ours. Same request, same answer; the switch above each sample picks one and remembers it. Which should I use?

GET/v1/identity/consent_tokens#

See what people agreed to, and withdrew

identity.consent_tokens.list

This reads back the consents your organisation has recorded, and a withdrawn one stays in the list. So you can show both what somebody agreed to and that they took it back.

You get each consent with the purpose it covers, the checks it names and whether it still stands. There is nothing to filter on.

Read one in full at GET /v1/identity/consent_tokens/{consent_token_id}.

Returns

A list of consent tokens.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of ConsentToken required

The records on this page, in the order the list promises. Empty when nothing matched.

12 fields of ConsentToken
id string required

The token's identifier, starting with anchor_pro_consent_. POST /v1/identity/consent_tokens returns it and it never changes; send it as consent_token_id at POST /v1/identity/verifications.

object always "consent_token" required

Always consent_token. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

subject_ref string required

Your opaque reference to the person, exactly as you sent subject_ref at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

What the subject agreed their data may be used for, as you stated it, for example employment_verification. This token covers no other purpose.

checks array of CheckKind required

The checks the subject agreed to, from government_identifier, bank_identifier, date_of_birth_match, name_match and contactability. A check not listed here is not authorised by this token.

status string required

Whether the token still authorises anything: active does, withdrawn means you or the subject took it back, expired means expires_at has passed. Only an active token authorises a check.

activewithdrawnexpired
wording_version string optional

The exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.

collected_channel string optional

How the consent was collected, as you sent it at POST /v1/identity/consent_tokens: api through your integration, self_serve by the subject themselves, assisted by someone enrolling them, or whatsapp over WhatsApp.

apiself_serveassistedwhatsapp
collected_at string · date-time optional

When the subject gave this consent, as an RFC 3339 timestamp in UTC.

expires_at string · date-time required

When this consent lapses, as an RFC 3339 timestamp in UTC. After that moment the token's status is expired and it authorises nothing.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/consent_tokens" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityConsentTokensList({});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/consent_tokens', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_consent_tokens_list()
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/consent_tokens',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityConsentTokensList();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/consent_tokens');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityConsentTokensList();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/consent_tokens"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityConsentTokensList();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/consent_tokens");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityConsentTokensList(ctx).Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/consent_tokens", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "consent_token",
      "livemode": false,
      "mocked": true,
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "purpose": "employment_verification",
      "checks": [
        "government_identifier"
      ],
      "status": "active",
      "expires_at": "2026-09-01T09:00:00Z",
      "wording_version": "2026.08.1",
      "collected_channel": "api",
      "collected_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true
}
POST/v1/identity/consent_tokens#

Record consent before anything is checked

identity.consent_tokens.create

This records that one person agreed to a stated purpose and a named set of checks, in the wording they were shown. So one agreement can cover several checks on the same person, and your product collects it once.

Send your own reference for the person, the purpose, at least one check and the wording version they saw. You get the token back, with an id starting anchor_pro_consent_ to keep.

Nothing has to exist first. You choose subject_ref yourself, so keep it: POST /v1/identity/verifications asks for the same value for the same person.

One token is one purpose and the checks it names, and neither can be changed afterwards. A second purpose for the same person is another call here.

Carry the id forward as consent_token_id, to POST /v1/identity/verifications and to POST /v1/identity/disclosures.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

subject_ref string required

Your opaque reference to the person whose consent you are recording. Choose it yourself and send the same value as subject_ref at POST /v1/identity/verifications; not a name and not an identifier.

purpose string required

What you will use the subject's data for, as they were told, for example employment_verification. The token covers this purpose and no other, so state it exactly.

checks array of CheckKind required

The checks this consent covers, at least one, from government_identifier, bank_identifier, date_of_birth_match, name_match and contactability. Ask for the ones you need and no more.

wording_version string required

The version of the consent wording the subject saw, for example 2026.08.1. Keep it exact: it's how you later show what they agreed to.

collected_channel string optional

How you collected the consent: api through your integration, self_serve by the subject themselves, assisted by someone enrolling them, or whatsapp over WhatsApp. It comes back unchanged on the token.

apiself_serveassistedwhatsapp
expires_at string · date-time optional

When this consent should lapse, as an RFC 3339 timestamp in UTC. After that moment the token authorises nothing.

Returns

The consent token as stored, carrying an end date even when you sent none.

id string required

The token's identifier, starting with anchor_pro_consent_. POST /v1/identity/consent_tokens returns it and it never changes; send it as consent_token_id at POST /v1/identity/verifications.

object always "consent_token" required

Always consent_token. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

subject_ref string required

Your opaque reference to the person, exactly as you sent subject_ref at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

What the subject agreed their data may be used for, as you stated it, for example employment_verification. This token covers no other purpose.

checks array of CheckKind required

The checks the subject agreed to, from government_identifier, bank_identifier, date_of_birth_match, name_match and contactability. A check not listed here is not authorised by this token.

status string required

Whether the token still authorises anything: active does, withdrawn means you or the subject took it back, expired means expires_at has passed. Only an active token authorises a check.

activewithdrawnexpired
wording_version string optional

The exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.

collected_channel string optional

How the consent was collected, as you sent it at POST /v1/identity/consent_tokens: api through your integration, self_serve by the subject themselves, assisted by someone enrolling them, or whatsapp over WhatsApp.

apiself_serveassistedwhatsapp
collected_at string · date-time optional

When the subject gave this consent, as an RFC 3339 timestamp in UTC.

expires_at string · date-time required

When this consent lapses, as an RFC 3339 timestamp in UTC. After that moment the token's status is expired and it authorises nothing.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/consent_tokens" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","checks":["government_identifier"],"wording_version":"2026.08.1","collected_channel":"api","expires_at":"2026-09-01T09:00:00Z"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityConsentTokensCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorConsentTokenCreateRequest: {"subjectRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","checks":["government_identifier"],"wordingVersion":"2026.08.1","collectedChannel":"api","expiresAt":"2026-09-01T09:00:00Z"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/consent_tokens', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "purpose": "employment_verification",
    "checks": [
      "government_identifier"
    ],
    "wording_version": "2026.08.1",
    "collected_channel": "api",
    "expires_at": "2026-09-01T09:00:00Z"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_consent_tokens_create(body={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "purpose": "employment_verification", "checks": ["government_identifier"], "wording_version": "2026.08.1", "collected_channel": "api", "expires_at": "2026-09-01T09:00:00Z"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/consent_tokens',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "purpose": "employment_verification", "checks": ["government_identifier"], "wording_version": "2026.08.1", "collected_channel": "api", "expires_at": "2026-09-01T09:00:00Z"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityConsentTokensCreate($idempotencyKey, json_decode('{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","checks":["government_identifier"],"wording_version":"2026.08.1","collected_channel":"api","expires_at":"2026-09-01T09:00:00Z"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/consent_tokens');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","checks":["government_identifier"],"wording_version":"2026.08.1","collected_channel":"api","expires_at":"2026-09-01T09:00:00Z"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityConsentTokensCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/consent_tokens"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "purpose": "employment_verification",
          "checks": [
            "government_identifier"
          ],
          "wording_version": "2026.08.1",
          "collected_channel": "api",
          "expires_at": "2026-09-01T09:00:00Z"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityConsentTokensCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/consent_tokens");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "purpose": "employment_verification",
      "checks": [
        "government_identifier"
      ],
      "wording_version": "2026.08.1",
      "collected_channel": "api",
      "expires_at": "2026-09-01T09:00:00Z"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityConsentTokensCreate(ctx).IdempotencyKey(key).AnchorConsentTokenCreateRequest(body).Execute()
body := strings.NewReader(`{
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "checks": [
    "government_identifier"
  ],
  "wording_version": "2026.08.1",
  "collected_channel": "api",
  "expires_at": "2026-09-01T09:00:00Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/consent_tokens", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "consent_token",
  "livemode": false,
  "mocked": true,
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "checks": [
    "government_identifier"
  ],
  "status": "active",
  "expires_at": "2026-09-01T09:00:00Z",
  "wording_version": "2026.08.1",
  "collected_channel": "api",
  "collected_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/consent_tokens/{consent_token_id}#

Check one consent before you rely on it

identity.consent_tokens.retrieve

This reads one consent back as it was given: one person's agreement to one purpose and the checks it names. So before your product relies on a consent it recorded weeks ago, it can check what that consent covered and that nobody has taken it back.

Send the token's id in the path. You get the record as it was written: the purpose it covers, the checks it names, the wording_version the subject saw, how it was collected and when they gave it.

To end this consent, call POST /v1/identity/consent_tokens/{consent_token_id}/revoke with the same id.

Path parameters

consent_token_id string required

The consent token's identifier, starting with anchor_pro_consent_: the id returned by POST /v1/identity/consent_tokens, or of any token listed by GET /v1/identity/consent_tokens.

Returns

The consent token.

id string required

The token's identifier, starting with anchor_pro_consent_. POST /v1/identity/consent_tokens returns it and it never changes; send it as consent_token_id at POST /v1/identity/verifications.

object always "consent_token" required

Always consent_token. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

subject_ref string required

Your opaque reference to the person, exactly as you sent subject_ref at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

What the subject agreed their data may be used for, as you stated it, for example employment_verification. This token covers no other purpose.

checks array of CheckKind required

The checks the subject agreed to, from government_identifier, bank_identifier, date_of_birth_match, name_match and contactability. A check not listed here is not authorised by this token.

status string required

Whether the token still authorises anything: active does, withdrawn means you or the subject took it back, expired means expires_at has passed. Only an active token authorises a check.

activewithdrawnexpired
wording_version string optional

The exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.

collected_channel string optional

How the consent was collected, as you sent it at POST /v1/identity/consent_tokens: api through your integration, self_serve by the subject themselves, assisted by someone enrolling them, or whatsapp over WhatsApp.

apiself_serveassistedwhatsapp
collected_at string · date-time optional

When the subject gave this consent, as an RFC 3339 timestamp in UTC.

expires_at string · date-time required

When this consent lapses, as an RFC 3339 timestamp in UTC. After that moment the token's status is expired and it authorises nothing.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityConsentTokensRetrieve({ consentTokenId: 'anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_consent_tokens_retrieve(consent_token_id='anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityConsentTokensRetrieve(consent_token_id: 'anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityConsentTokensRetrieve("anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityConsentTokensRetrieve(consentTokenId: "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityConsentTokensRetrieve(ctx, "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "consent_token",
  "livemode": false,
  "mocked": true,
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "checks": [
    "government_identifier"
  ],
  "status": "active",
  "expires_at": "2026-09-01T09:00:00Z",
  "wording_version": "2026.08.1",
  "collected_channel": "api",
  "collected_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/consent_tokens/{consent_token_id}/revoke#

Withdraw a consent you recorded

identity.consent_tokens.revoke

This withdraws a consent somebody gave you. So your product can offer a person a way to take their agreement back, and consent.withdrawn tells the rest of your own systems to stop acting on it.

Send the token's id in the path. Send no request body. You get the token back, with the subject_ref, purpose and checks it was written with.

The consent has to exist, from POST /v1/identity/consent_tokens.

Withdrawal only goes one way: nothing here puts a consent back, and a subject who agrees again needs a new token.

Withdrawing a consent you have already withdrawn answers conflict. A withdrawn consent stays readable at GET /v1/identity/consent_tokens/{consent_token_id}.

Path parameters

consent_token_id string required

The consent token's identifier, starting with anchor_pro_consent_: the id returned by POST /v1/identity/consent_tokens, or of any token listed by GET /v1/identity/consent_tokens.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The same consent record, so a decision taken under it earlier can still be explained.

id string required

The token's identifier, starting with anchor_pro_consent_. POST /v1/identity/consent_tokens returns it and it never changes; send it as consent_token_id at POST /v1/identity/verifications.

object always "consent_token" required

Always consent_token. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

subject_ref string required

Your opaque reference to the person, exactly as you sent subject_ref at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

What the subject agreed their data may be used for, as you stated it, for example employment_verification. This token covers no other purpose.

checks array of CheckKind required

The checks the subject agreed to, from government_identifier, bank_identifier, date_of_birth_match, name_match and contactability. A check not listed here is not authorised by this token.

status string required

Whether the token still authorises anything: active does, withdrawn means you or the subject took it back, expired means expires_at has passed. Only an active token authorises a check.

activewithdrawnexpired
wording_version string optional

The exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.

collected_channel string optional

How the consent was collected, as you sent it at POST /v1/identity/consent_tokens: api through your integration, self_serve by the subject themselves, assisted by someone enrolling them, or whatsapp over WhatsApp.

apiself_serveassistedwhatsapp
collected_at string · date-time optional

When the subject gave this consent, as an RFC 3339 timestamp in UTC.

expires_at string · date-time required

When this consent lapses, as an RFC 3339 timestamp in UTC. After that moment the token's status is expired and it authorises nothing.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityConsentTokensRevoke({ consentTokenId: 'anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Idempotency-Key': crypto.randomUUID(),
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_consent_tokens_revoke(consent_token_id='anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityConsentTokensRevoke(consent_token_id: 'anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityConsentTokensRevoke("anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityConsentTokensRevoke(consentTokenId: "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityConsentTokensRevoke(ctx, "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/consent_tokens/anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_consent_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "consent_token",
  "livemode": false,
  "mocked": true,
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "checks": [
    "government_identifier"
  ],
  "status": "active",
  "expires_at": "2026-09-01T09:00:00Z",
  "wording_version": "2026.08.1",
  "collected_channel": "api",
  "collected_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/verifications#

See the identity checks you have run

identity.verifications.list

Every check your organisation has opened is here, so one screen in your product can show where each person stands without keeping a second copy of the answer in your own system. A verification is one check of one person. outcome says where that check stands, and assurance_reached says what the evidence established, which can be lower than the target_assurance you asked for.

You get back one record for each check, and each carries its own id starting anchor_pro_verification_, which is what you store. Read one in full at GET /v1/identity/verifications/{verification_id}.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

outcome string optional

Return only verifications whose outcome is this value: pending (in progress), resolved (subject identified), unresolved (a source didn't answer), blocked (consent or the blocklist stopped it). Leave it out to get every outcome.

pendingresolvedunresolvedblocked

Returns

A page of verifications.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of Verification required

The records on this page, in the order the list promises. Empty when nothing matched.

16 fields of Verification
id string required

The verification's identifier, starting with anchor_pro_verification_. POST /v1/identity/verifications returns it and it never changes; pass it as verification_id wherever a call names this verification.

object always "identity_verification" required

Always identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
origin string required

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
subject_ref string required

Your opaque reference to the person being verified, exactly as you sent subject_ref at POST /v1/identity/verifications. Not a name and not an identifier.

consent_token_id string optional

The consent token this verification ran under, as you sent it at POST /v1/identity/verifications: the id starting with anchor_pro_consent_ that POST /v1/identity/consent_tokens returned for this subject, purpose and checks.

target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance_reached one of optional

The level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.

AssuranceLevelor
passport_id string · nullable optional

The id of the Passport this verification resolved to, starting with anchor_pro_passport_: what every module checks before it acts. Read it at GET /v1/identity/passports/{passport_id}; null until outcome is resolved.

evidence_bundle_id string · nullable optional

The id of the bundle listing every source consulted and what each said. Read it at GET /v1/identity/verifications/{verification_id}/evidence_bundle using this verification's id; null while there is nothing to show yet.

unresolved_source string · nullable optional

Named when the outcome is unresolved. Silence always names its source.

retry_after string · date-time · nullable optional

When to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.

blocked_because string · nullable optional

Why outcome is blocked: no_consent (no active token), consent_scope_insufficient (the token doesn't cover what was asked) or subject_blocklisted. The subject is entitled to know which; null for any other outcome.

no_consentconsent_scope_insufficientsubject_blocklistednull
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), starting_after (optional), outcome (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/verifications?limit=25&outcome=pending" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: limit (optional), starting_after (optional), outcome (optional)
const result = await api.identityVerificationsList({ limit: 25, outcome: 'pending' });
// query parameters: limit (optional), starting_after (optional), outcome (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verifications?limit=25&outcome=pending', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: limit (optional), starting_after (optional), outcome (optional)
result = api.identity_verifications_list(limit=25, outcome='pending')
import os

import requests

# query parameters: limit (optional), starting_after (optional), outcome (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/verifications?limit=25&outcome=pending',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), outcome (optional)
$result = $api->identityVerificationsList(limit: 25, outcome: 'pending');
<?php
// query parameters: limit (optional), starting_after (optional), outcome (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verifications?limit=25&outcome=pending');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;
import com.useheros.sdk.model.*;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: limit (optional), starting_after (optional), outcome (optional)
var result = api.identityVerificationsList(25, null, AnchorVerificationOutcome.fromValue("pending"));
// query parameters: limit (optional), starting_after (optional), outcome (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verifications?limit=25&outcome=pending"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: limit (optional), starting_after (optional), outcome (optional)
var result = api.IdentityVerificationsList(limit: 25, outcome: AnchorVerificationOutcome.Pending);
// query parameters: limit (optional), starting_after (optional), outcome (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/verifications?limit=25&outcome=pending");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: limit (optional), starting_after (optional), outcome (optional)
result, _, err := client.ANCHORAPI.IdentityVerificationsList(ctx).Limit(25).Outcome(heros.AnchorVerificationOutcome("pending")).Execute()
// query parameters: limit (optional), starting_after (optional), outcome (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/verifications?limit=25&outcome=pending", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "identity_verification",
      "livemode": false,
      "mocked": true,
      "outcome": "pending",
      "origin": "partner_api",
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "target_assurance": "dal_0",
      "assurance_reached": "dal_0",
      "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "evidence_bundle_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "unresolved_source": "example",
      "retry_after": "2026-09-01T09:00:00Z",
      "blocked_because": "no_consent",
      "created_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true
}
POST/v1/identity/verifications#

Verify an identity

identity.verifications.create

This opens one check on one person, and the identifier it hands back is what every later call about this check names. However the person reached you, the check runs the same way, so your product keeps one identity path instead of one for each way in.

Send your own reference for the person, the consent token you collected and what they have told you about themselves. You get the verification back with an id starting anchor_pro_verification_ and an outcome.

A consent token has to exist first, from POST /v1/identity/consent_tokens.

Send the same subject_ref you used when you recorded that consent. Leave target_assurance out and your policy pack sets the minimum the purpose needs; asking for more costs more.

A 201 does not mean the person was identified. Read outcome, and read it again at GET /v1/identity/verifications/{verification_id} using the id as verification_id.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

subject_ref string required

Your opaque reference to the person: the same subject_ref you chose and sent at POST /v1/identity/consent_tokens when you recorded their consent. Not a name and not an identifier.

consent_token_id string required

The id of an active consent token for this subject, starting with anchor_pro_consent_, as returned by POST /v1/identity/consent_tokens. Its purpose and checks must cover what you are asking; without one, nothing is queried.

origin string optional

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
claims object required

What the person says about themselves. Identifiers you submit here never come back in full.

5 fields
full_name string optional

The person's full name as they give it. For dal_2 it's matched on name against the government identifier, so send it as it appears on their record.

date_of_birth string · date optional

The person's date of birth as they give it, as YYYY-MM-DD. For dal_2 it's matched against the government identifier alongside the name.

phone string optional

The phone number the person says is theirs. Proving it reaches them is the contactability check, and reaching dal_1 means it did.

government_identifier string · write only optional

Write only. Accepted here, never returned anywhere.

bank_identifier string · write only optional

Write only. Accepted here, never returned anywhere.

Returns

The check as it stands on creation, enough to tell whether you must wait for an answer.

id string required

The verification's identifier, starting with anchor_pro_verification_. POST /v1/identity/verifications returns it and it never changes; pass it as verification_id wherever a call names this verification.

object always "identity_verification" required

Always identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
origin string required

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
subject_ref string required

Your opaque reference to the person being verified, exactly as you sent subject_ref at POST /v1/identity/verifications. Not a name and not an identifier.

consent_token_id string optional

The consent token this verification ran under, as you sent it at POST /v1/identity/verifications: the id starting with anchor_pro_consent_ that POST /v1/identity/consent_tokens returned for this subject, purpose and checks.

target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance_reached one of optional

The level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.

AssuranceLevelor
passport_id string · nullable optional

The id of the Passport this verification resolved to, starting with anchor_pro_passport_: what every module checks before it acts. Read it at GET /v1/identity/passports/{passport_id}; null until outcome is resolved.

evidence_bundle_id string · nullable optional

The id of the bundle listing every source consulted and what each said. Read it at GET /v1/identity/verifications/{verification_id}/evidence_bundle using this verification's id; null while there is nothing to show yet.

unresolved_source string · nullable optional

Named when the outcome is unresolved. Silence always names its source.

retry_after string · date-time · nullable optional

When to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.

blocked_because string · nullable optional

Why outcome is blocked: no_consent (no active token), consent_scope_insufficient (the token doesn't cover what was asked) or subject_blocklisted. The subject is entitled to know which; null for any other outcome.

no_consentconsent_scope_insufficientsubject_blocklistednull
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/verifications" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"full_name":"example","date_of_birth":"2026-09-01","phone":"example","government_identifier":"example","bank_identifier":"example"},"origin":"partner_api","target_assurance":"dal_0"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityVerificationsCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorVerificationCreateRequest: {"subjectRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consentTokenId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"fullName":"example","dateOfBirth":"2026-09-01","phone":"example","governmentIdentifier":"example","bankIdentifier":"example"},"origin":"partner_api","targetAssurance":"dal_0"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verifications', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "claims": {
      "full_name": "example",
      "date_of_birth": "2026-09-01",
      "phone": "example",
      "government_identifier": "example",
      "bank_identifier": "example"
    },
    "origin": "partner_api",
    "target_assurance": "dal_0"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_verifications_create(body={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "claims": {"full_name": "example", "date_of_birth": "2026-09-01", "phone": "example", "government_identifier": "example", "bank_identifier": "example"}, "origin": "partner_api", "target_assurance": "dal_0"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/verifications',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "claims": {"full_name": "example", "date_of_birth": "2026-09-01", "phone": "example", "government_identifier": "example", "bank_identifier": "example"}, "origin": "partner_api", "target_assurance": "dal_0"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityVerificationsCreate($idempotencyKey, json_decode('{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"full_name":"example","date_of_birth":"2026-09-01","phone":"example","government_identifier":"example","bank_identifier":"example"},"origin":"partner_api","target_assurance":"dal_0"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verifications');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"full_name":"example","date_of_birth":"2026-09-01","phone":"example","government_identifier":"example","bank_identifier":"example"},"origin":"partner_api","target_assurance":"dal_0"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityVerificationsCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verifications"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "claims": {
            "full_name": "example",
            "date_of_birth": "2026-09-01",
            "phone": "example",
            "government_identifier": "example",
            "bank_identifier": "example"
          },
          "origin": "partner_api",
          "target_assurance": "dal_0"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityVerificationsCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/verifications");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "claims": {
        "full_name": "example",
        "date_of_birth": "2026-09-01",
        "phone": "example",
        "government_identifier": "example",
        "bank_identifier": "example"
      },
      "origin": "partner_api",
      "target_assurance": "dal_0"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityVerificationsCreate(ctx).IdempotencyKey(key).AnchorVerificationCreateRequest(body).Execute()
body := strings.NewReader(`{
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "claims": {
    "full_name": "example",
    "date_of_birth": "2026-09-01",
    "phone": "example",
    "government_identifier": "example",
    "bank_identifier": "example"
  },
  "origin": "partner_api",
  "target_assurance": "dal_0"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/verifications", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "identity_verification",
  "livemode": false,
  "mocked": true,
  "outcome": "pending",
  "origin": "partner_api",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "target_assurance": "dal_0",
  "assurance_reached": "dal_0",
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "evidence_bundle_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "unresolved_source": "example",
  "retry_after": "2026-09-01T09:00:00Z",
  "blocked_because": "no_consent",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/verifications/{verification_id}#

Check whether a person has been identified

identity.verifications.retrieve

This reads one check back as it stands now. A create can answer pending, so this is the call you come back to before you treat the person as identified.

Send the verification's id in the path. You get that verification back, with outcome for where the check stands and target_assurance for the level you asked for.

The evidence behind the answer is at GET /v1/identity/verifications/{verification_id}/evidence_bundle. An identifier that names no verification answers not_found.

Path parameters

verification_id string required

The verification's identifier, starting with anchor_pro_verification_: the id returned by POST /v1/identity/verifications or listed by GET /v1/identity/verifications, or a batch result's verification_id.

Returns

The verification.

id string required

The verification's identifier, starting with anchor_pro_verification_. POST /v1/identity/verifications returns it and it never changes; pass it as verification_id wherever a call names this verification.

object always "identity_verification" required

Always identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
origin string required

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
subject_ref string required

Your opaque reference to the person being verified, exactly as you sent subject_ref at POST /v1/identity/verifications. Not a name and not an identifier.

consent_token_id string optional

The consent token this verification ran under, as you sent it at POST /v1/identity/verifications: the id starting with anchor_pro_consent_ that POST /v1/identity/consent_tokens returned for this subject, purpose and checks.

target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance_reached one of optional

The level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.

AssuranceLevelor
passport_id string · nullable optional

The id of the Passport this verification resolved to, starting with anchor_pro_passport_: what every module checks before it acts. Read it at GET /v1/identity/passports/{passport_id}; null until outcome is resolved.

evidence_bundle_id string · nullable optional

The id of the bundle listing every source consulted and what each said. Read it at GET /v1/identity/verifications/{verification_id}/evidence_bundle using this verification's id; null while there is nothing to show yet.

unresolved_source string · nullable optional

Named when the outcome is unresolved. Silence always names its source.

retry_after string · date-time · nullable optional

When to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.

blocked_because string · nullable optional

Why outcome is blocked: no_consent (no active token), consent_scope_insufficient (the token doesn't cover what was asked) or subject_blocklisted. The subject is entitled to know which; null for any other outcome.

no_consentconsent_scope_insufficientsubject_blocklistednull
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityVerificationsRetrieve({ verificationId: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_verifications_retrieve(verification_id='anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityVerificationsRetrieve(verification_id: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityVerificationsRetrieve("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityVerificationsRetrieve(verificationId: "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityVerificationsRetrieve(ctx, "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "identity_verification",
  "livemode": false,
  "mocked": true,
  "outcome": "pending",
  "origin": "partner_api",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "target_assurance": "dal_0",
  "assurance_reached": "dal_0",
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "evidence_bundle_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "unresolved_source": "example",
  "retry_after": "2026-09-01T09:00:00Z",
  "blocked_because": "no_consent",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/verifications/{verification_id}/evidence_bundle#

See the sources behind one identity decision

identity.evidence_bundles.retrieve

An evidence bundle is the record of how one check was decided, and it does not change once written. It names the version of your policy pack that was in force when the check was judged, so a decision stays readable against the rules it was made under.

Send the verification's id in the path. You get one entry for every source that was asked, with what each one answered.

An identifier that names no verification answers not_found.

Path parameters

verification_id string required

The verification's identifier, starting with anchor_pro_verification_: the id returned by POST /v1/identity/verifications or listed by GET /v1/identity/verifications, or a batch result's verification_id.

Returns

The bundle, with queried_at on every entry, so you can tell how old each source's answer is.

id string required

The bundle's identifier. It never changes, and it is the value carried as evidence_bundle_id on the verification at GET /v1/identity/verifications/{verification_id} and in evidence_bundle_ids on the Passport.

object always "evidence_bundle" required

Always evidence_bundle. Tells you which kind of record you are looking at, so one handler can read any response.

verification_id string required

The id of the verification this bundle was written for, starting with anchor_pro_verification_, as returned by POST /v1/identity/verifications. One bundle per verification, and it never changes once written.

entries array of object required

One entry per source consulted: which source, what it answered, when, through which provider, and whether evidence still inside its validity period was reused. Never the record behind the answer.

5 fields
source string required

Which source was consulted, for example national_identity_register. Names the source, never what it holds.

result string required

What the source answered: match, no_match, partial_match when only part of the claim matched, or unresolved when it didn't answer. unresolved proves nothing either way.

matchno_matchpartial_matchunresolved
queried_at string · date-time required

When the source was consulted, as an RFC 3339 timestamp in UTC.

provider string required

Which provider answered for this source. You can show an auditor who said what, not only which source was asked.

cached boolean optional

True when evidence inside its validity period was reused rather than re-charged.

policy_pack_version string required

The version of your policy pack in force when this verification was judged, for example 2026.08.1. Read the bundle against that version, not the current one.

adjudicator string · nullable optional

Named where a person decided rather than a rule.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/evidence_bundle" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityEvidenceBundlesRetrieve({ verificationId: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/evidence_bundle', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_evidence_bundles_retrieve(verification_id='anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/evidence_bundle',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityEvidenceBundlesRetrieve(verification_id: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/evidence_bundle');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityEvidenceBundlesRetrieve("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/evidence_bundle"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityEvidenceBundlesRetrieve(verificationId: "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/evidence_bundle");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityEvidenceBundlesRetrieve(ctx, "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/evidence_bundle", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "evidence_bundle",
  "verification_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "entries": [
    {
      "source": "national_identity_register",
      "result": "match",
      "queried_at": "2026-09-01T09:00:00Z",
      "provider": "example",
      "cached": true
    }
  ],
  "policy_pack_version": "2026.08.1",
  "adjudicator": "example",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/verification_batches#

Verify many identities at once

identity.verification_batches.create

This verifies a list of people in one call, where POST /v1/identity/verifications takes one person at a time. You get one identifier covering however many subjects you sent, so your system has one string to come back to for the whole intake.

Send subjects, one entry per person, each the same body you would send to verify that person alone. You get the batch back with an id and a subject_count of how many you sent.

Each entry carries its own consent_token_id, so record every subject's consent at POST /v1/identity/consent_tokens first.

The id starts anchor_pro_verification_, the same prefix a single verification carries, so keep a batch's identifier apart from a verification's in your own store.

Carry the id as batch_id to GET /v1/identity/verification_batches/{batch_id}, the only call that reads a batch back.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

subjects array of VerificationCreateRequest required

The subjects to verify, at least one, each in the shape of a single verification request. Each resolves on its own, so one that can't proceed never holds up the rest.

5 fields of VerificationCreateRequest
subject_ref string required

Your opaque reference to the person: the same subject_ref you chose and sent at POST /v1/identity/consent_tokens when you recorded their consent. Not a name and not an identifier.

consent_token_id string required

The id of an active consent token for this subject, starting with anchor_pro_consent_, as returned by POST /v1/identity/consent_tokens. Its purpose and checks must cover what you are asking; without one, nothing is queried.

origin string optional

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
claims object required

What the person says about themselves. Identifiers you submit here never come back in full.

5 fields
full_name string optional

The person's full name as they give it. For dal_2 it's matched on name against the government identifier, so send it as it appears on their record.

date_of_birth string · date optional

The person's date of birth as they give it, as YYYY-MM-DD. For dal_2 it's matched against the government identifier alongside the name.

phone string optional

The phone number the person says is theirs. Proving it reaches them is the contactability check, and reaching dal_1 means it did.

government_identifier string · write only optional

Write only. Accepted here, never returned anywhere.

bank_identifier string · write only optional

Write only. Accepted here, never returned anywhere.

Returns

The batch, resolving per subject.

id string required

The batch's identifier, starting with anchor_pro_verification_. POST /v1/identity/verification_batches returns it and it never changes; pass it as batch_id to GET /v1/identity/verification_batches/{batch_id}.

object always "verification_batch" required

Always verification_batch. Tells you which kind of record you are looking at, so one handler can read any response.

status string required

Where the batch stands: running while any subject's outcome is still pending, completed once none is. Read results for each subject's outcome.

runningcompleted
subject_count integer required

How many subjects you sent in this batch. results carries one entry for each of them.

results array of object required

One entry per subject, resolved independently. A subject that could not proceed carries the reason and does not stall the others.

4 fields
subject_ref string required

The subject this entry is for: your opaque reference, exactly as you sent it in subjects[].subject_ref at POST /v1/identity/verification_batches. Not a name and not an identifier.

verification_id string · nullable optional

The id of the verification opened for this subject, starting with anchor_pro_verification_; read it in full at GET /v1/identity/verifications/{verification_id}. null when none was opened.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
blocked_because string · nullable optional

Why this subject could not proceed, such as no_consent, when outcome is blocked. null for a subject that went ahead.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/verification_batches" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"subjects":[{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"full_name":"example","date_of_birth":"2026-09-01","phone":"example","government_identifier":"example","bank_identifier":"example"},"origin":"partner_api","target_assurance":"dal_0"}]}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityVerificationBatchesCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorVerificationBatchCreateRequest: {"subjects":[{"subjectRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consentTokenId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"fullName":"example","dateOfBirth":"2026-09-01","phone":"example","governmentIdentifier":"example","bankIdentifier":"example"},"origin":"partner_api","targetAssurance":"dal_0"}]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verification_batches', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "subjects": [
      {
        "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
        "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
        "claims": {
          "full_name": "example",
          "date_of_birth": "2026-09-01",
          "phone": "example",
          "government_identifier": "example",
          "bank_identifier": "example"
        },
        "origin": "partner_api",
        "target_assurance": "dal_0"
      }
    ]
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_verification_batches_create(body={"subjects": [{"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "claims": {"full_name": "example", "date_of_birth": "2026-09-01", "phone": "example", "government_identifier": "example", "bank_identifier": "example"}, "origin": "partner_api", "target_assurance": "dal_0"}]})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/verification_batches',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"subjects": [{"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "claims": {"full_name": "example", "date_of_birth": "2026-09-01", "phone": "example", "government_identifier": "example", "bank_identifier": "example"}, "origin": "partner_api", "target_assurance": "dal_0"}]},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityVerificationBatchesCreate($idempotencyKey, json_decode('{"subjects":[{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"full_name":"example","date_of_birth":"2026-09-01","phone":"example","government_identifier":"example","bank_identifier":"example"},"origin":"partner_api","target_assurance":"dal_0"}]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verification_batches');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"subjects":[{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","claims":{"full_name":"example","date_of_birth":"2026-09-01","phone":"example","government_identifier":"example","bank_identifier":"example"},"origin":"partner_api","target_assurance":"dal_0"}]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityVerificationBatchesCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verification_batches"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "subjects": [
            {
              "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
              "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
              "claims": {
                "full_name": "example",
                "date_of_birth": "2026-09-01",
                "phone": "example",
                "government_identifier": "example",
                "bank_identifier": "example"
              },
              "origin": "partner_api",
              "target_assurance": "dal_0"
            }
          ]
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityVerificationBatchesCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/verification_batches");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "subjects": [
        {
          "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "claims": {
            "full_name": "example",
            "date_of_birth": "2026-09-01",
            "phone": "example",
            "government_identifier": "example",
            "bank_identifier": "example"
          },
          "origin": "partner_api",
          "target_assurance": "dal_0"
        }
      ]
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityVerificationBatchesCreate(ctx).IdempotencyKey(key).AnchorVerificationBatchCreateRequest(body).Execute()
body := strings.NewReader(`{
  "subjects": [
    {
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "claims": {
        "full_name": "example",
        "date_of_birth": "2026-09-01",
        "phone": "example",
        "government_identifier": "example",
        "bank_identifier": "example"
      },
      "origin": "partner_api",
      "target_assurance": "dal_0"
    }
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/verification_batches", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "verification_batch",
  "status": "running",
  "subject_count": 1,
  "results": [
    {
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "outcome": "pending",
      "verification_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "blocked_because": "no_consent"
    }
  ]
}
GET/v1/identity/verification_batches/{batch_id}#

Come back to people you verified in bulk

identity.verification_batches.retrieve

This looks up one batch you sent. It is the only call that reads a batch back, so the identifier you kept is both enough on its own and the only way in.

A verification batch is one submission covering a list of people. Put the batch_id in the path and you get that record back, carrying the subject_count you sent it with.

not_found here means no batch of yours has that identifier, so check it against what you stored when you created the batch.

Path parameters

batch_id string required

The batch's identifier, starting with anchor_pro_verification_: the id returned by POST /v1/identity/verification_batches when you created the batch.

Returns

The batch.

id string required

The batch's identifier, starting with anchor_pro_verification_. POST /v1/identity/verification_batches returns it and it never changes; pass it as batch_id to GET /v1/identity/verification_batches/{batch_id}.

object always "verification_batch" required

Always verification_batch. Tells you which kind of record you are looking at, so one handler can read any response.

status string required

Where the batch stands: running while any subject's outcome is still pending, completed once none is. Read results for each subject's outcome.

runningcompleted
subject_count integer required

How many subjects you sent in this batch. results carries one entry for each of them.

results array of object required

One entry per subject, resolved independently. A subject that could not proceed carries the reason and does not stall the others.

4 fields
subject_ref string required

The subject this entry is for: your opaque reference, exactly as you sent it in subjects[].subject_ref at POST /v1/identity/verification_batches. Not a name and not an identifier.

verification_id string · nullable optional

The id of the verification opened for this subject, starting with anchor_pro_verification_; read it in full at GET /v1/identity/verifications/{verification_id}. null when none was opened.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
blocked_because string · nullable optional

Why this subject could not proceed, such as no_consent, when outcome is blocked. null for a subject that went ahead.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/verification_batches/%7Bbatch_id%7D" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityVerificationBatchesRetrieve({ batchId: '{batch_id}' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verification_batches/%7Bbatch_id%7D', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_verification_batches_retrieve(batch_id='{batch_id}')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/verification_batches/%7Bbatch_id%7D',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityVerificationBatchesRetrieve(batch_id: '{batch_id}');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verification_batches/%7Bbatch_id%7D');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityVerificationBatchesRetrieve("{batch_id}");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verification_batches/%7Bbatch_id%7D"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityVerificationBatchesRetrieve(batchId: "{batch_id}");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/verification_batches/%7Bbatch_id%7D");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityVerificationBatchesRetrieve(ctx, "{batch_id}").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/verification_batches/%7Bbatch_id%7D", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "verification_batch",
  "status": "running",
  "subject_count": 1,
  "results": [
    {
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "outcome": "pending",
      "verification_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "blocked_because": "no_consent"
    }
  ]
}
GET/v1/identity/sources#

What we can check a person against

identity.sources.list

This is the list of registers and services ANCHOR can put a claim to. You never choose one, so this is where you see how far a check can be taken, before you ask a verification for a level no source behind it can reach.

You get each source named for a person to read, with establishes for the checks it can settle, max_assurance for the highest level its evidence supports and health for whether it is answering right now.

Nothing here is carried forward: no operation takes a source identifier. Read the list before you submit a verification at POST /v1/identity/verifications.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

Returns

A page of sources.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of Source required

The records on this page, in the order the list promises. Empty when nothing matched.

7 fields of Source
id string required

The source's identifier, starting with anchor_pro_verification_. It never changes, and GET /v1/identity/sources is the only call that returns it; you never send it, because we pick the source for each check.

object always "identity_source" required

Always identity_source. Tells you which kind of record you are looking at, so one handler can read any response.

name string required

The source's name, for people to read. You never choose a source by name; we pick one for each check.

establishes array of CheckKind required

The checks this source can establish, such as government_identifier or contactability. One source may establish several.

max_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
health string required

Whether the source is answering right now: healthy, degraded (answering, but not reliably) or unavailable (not answering). A source that doesn't answer never fails a verification; the outcome is unresolved, naming it.

healthydegradedunavailable
evidence_validity_days integer optional

How long evidence from this source stays usable before it must be re-queried.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/sources?limit=25" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: limit (optional)
const result = await api.identitySourcesList({ limit: 25 });
// query parameters: limit (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/sources?limit=25', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: limit (optional)
result = api.identity_sources_list(limit=25)
import os

import requests

# query parameters: limit (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/sources?limit=25',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional)
$result = $api->identitySourcesList(limit: 25);
<?php
// query parameters: limit (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/sources?limit=25');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: limit (optional)
var result = api.identitySourcesList(25);
// query parameters: limit (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/sources?limit=25"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: limit (optional)
var result = api.IdentitySourcesList(limit: 25);
// query parameters: limit (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/sources?limit=25");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: limit (optional)
result, _, err := client.ANCHORAPI.IdentitySourcesList(ctx).Limit(25).Execute()
// query parameters: limit (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/sources?limit=25", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "identity_source",
      "name": "example",
      "establishes": [
        "government_identifier"
      ],
      "health": "healthy",
      "max_assurance": "dal_0",
      "evidence_validity_days": 1
    }
  ],
  "has_more": true
}
GET/v1/identity/passports#

See what level each Passport reached

identity.passports.list

This reads Passports together, so one screen in your product can show what level each subject has reached without opening them one at a time.

A Passport is one subject's assurance level, reached on the evidence held for them. Each one carries an id starting anchor_pro_passport_, which is what you store.

Read one in full at GET /v1/identity/passports/{passport_id}.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

assurance_level string optional

Return only Passports whose assurance_level is this value: dal_0 claimed, dal_1 contactable, dal_2 matched against a government identifier, dal_3 and dal_4 not in this release. Leave it out to list every level.

dal_0dal_1dal_2dal_3dal_4

Returns

A page of Passports, each with a valid_until you never sent, so you can re-check before that date arrives.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of Passport required

The records on this page, in the order the list promises. Empty when nothing matched.

14 fields of Passport
id string required

The Passport's identifier, starting with anchor_pro_passport_. It never changes: it's the passport_id on a resolved verification or the id POST /v1/identity/passports returns; pass it to GET /v1/identity/passports/{passport_id}.

object always "passport" required

Always passport. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

subject_ref string required

Your opaque reference to the person this Passport belongs to: the subject_ref you sent at POST /v1/identity/verifications on the verification that resolved into it. Not a name and not an identifier.

assurance_level string required

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance string optional

How a fact was established. Recorded on the fact rather than in configuration, so an attested identity and a verified one stay distinguishable a year later, which is the distinction that matters when something is disputed.

attestedverified
status string required
livedowngradedrevoked
identifiers_held array of object optional

Which identifiers are on file, masked. Enough to tell a subject what you hold, never enough to use it.

2 fields
kind string required

What a consent token may authorise. Ask for what you need and no more.

government_identifierbank_identifierdate_of_birth_matchname_matchcontactability
masked string required

An identity number, masked. This is the only form in which any identifier leaves ANCHOR. The full value is never returned to you, on any endpoint, at any assurance level, to any credential. There is no parameter that widens this and no scope that reveals it. If you need to show a subject which of their identifiers you hold, show them this.

disqualifying_flag boolean optional

Whether something disqualifying exists. Whether, not what. The detail is not yours to see.

evidence_bundle_ids array of string optional

The evidence bundles behind this level, one per verification that contributed. Each names the sources consulted and what they returned, never the records behind them.

monitoring string optional

Whether this subject is still watched after verification: active means yes, paused means watching has stopped. A change that lowers or revokes the Passport reaches you as a passport.downgraded or passport.revoked event.

activepaused
valid_until string · date-time required

When the supporting evidence lapses and the level falls on its own.

issued_at string · date-time optional

When the Passport was first issued, as an RFC 3339 timestamp in UTC. Normally the moment the first verification resolved.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), starting_after (optional), assurance_level (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/passports?limit=25&assurance_level=dal_0" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
const result = await api.identityPassportsList({ limit: 25, assuranceLevel: 'dal_0' });
// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/passports?limit=25&assurance_level=dal_0', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: limit (optional), starting_after (optional), assurance_level (optional)
result = api.identity_passports_list(limit=25, assurance_level='dal_0')
import os

import requests

# query parameters: limit (optional), starting_after (optional), assurance_level (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/passports?limit=25&assurance_level=dal_0',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), assurance_level (optional)
$result = $api->identityPassportsList(limit: 25, assurance_level: 'dal_0');
<?php
// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/passports?limit=25&assurance_level=dal_0');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;
import com.useheros.sdk.model.*;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
var result = api.identityPassportsList(25, null, AnchorAssuranceLevel.fromValue("dal_0"));
// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/passports?limit=25&assurance_level=dal_0"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
var result = api.IdentityPassportsList(limit: 25, assuranceLevel: AnchorAssuranceLevel.Dal_0);
// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/passports?limit=25&assurance_level=dal_0");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
result, _, err := client.ANCHORAPI.IdentityPassportsList(ctx).Limit(25).AssuranceLevel(heros.AnchorAssuranceLevel("dal_0")).Execute()
// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/passports?limit=25&assurance_level=dal_0", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "passport",
      "livemode": false,
      "mocked": true,
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "assurance_level": "dal_0",
      "status": "live",
      "valid_until": "2026-09-01T09:00:00Z",
      "assurance": "attested",
      "identifiers_held": [
        {
          "kind": "government_identifier",
          "masked": "*******4821"
        }
      ],
      "disqualifying_flag": true,
      "evidence_bundle_ids": [
        "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
      ],
      "monitoring": "active",
      "issued_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true
}
POST/v1/identity/passports#

Record a Passport from your own evidence

identity.passports.create

A Passport normally appears when a verification resolves. This one you write yourself, so a subject ANCHOR has never verified can be anchored on evidence you already hold.

Send the assurance level your own evidence supports. You get the Passport back, with an id starting anchor_pro_passport_ to keep.

Nothing has to exist first.

Carry the id forward as passport_id to GET /v1/identity/passports/{passport_id}.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body optional

assurance_level string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4

Returns

The new Passport, carrying a date you never sent for when its level stops being supported.

id string required

The Passport's identifier, starting with anchor_pro_passport_. It never changes: it's the passport_id on a resolved verification or the id POST /v1/identity/passports returns; pass it to GET /v1/identity/passports/{passport_id}.

object always "passport" required

Always passport. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

subject_ref string required

Your opaque reference to the person this Passport belongs to: the subject_ref you sent at POST /v1/identity/verifications on the verification that resolved into it. Not a name and not an identifier.

assurance_level string required

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance string optional

How a fact was established. Recorded on the fact rather than in configuration, so an attested identity and a verified one stay distinguishable a year later, which is the distinction that matters when something is disputed.

attestedverified
status string required
livedowngradedrevoked
identifiers_held array of object optional

Which identifiers are on file, masked. Enough to tell a subject what you hold, never enough to use it.

2 fields
kind string required

What a consent token may authorise. Ask for what you need and no more.

government_identifierbank_identifierdate_of_birth_matchname_matchcontactability
masked string required

An identity number, masked. This is the only form in which any identifier leaves ANCHOR. The full value is never returned to you, on any endpoint, at any assurance level, to any credential. There is no parameter that widens this and no scope that reveals it. If you need to show a subject which of their identifiers you hold, show them this.

disqualifying_flag boolean optional

Whether something disqualifying exists. Whether, not what. The detail is not yours to see.

evidence_bundle_ids array of string optional

The evidence bundles behind this level, one per verification that contributed. Each names the sources consulted and what they returned, never the records behind them.

monitoring string optional

Whether this subject is still watched after verification: active means yes, paused means watching has stopped. A change that lowers or revokes the Passport reaches you as a passport.downgraded or passport.revoked event.

activepaused
valid_until string · date-time required

When the supporting evidence lapses and the level falls on its own.

issued_at string · date-time optional

When the Passport was first issued, as an RFC 3339 timestamp in UTC. Normally the moment the first verification resolved.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/passports" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"assurance_level":"dal_0"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityPassportsCreate({});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/passports', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "assurance_level": "dal_0"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_passports_create()
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/passports',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"assurance_level": "dal_0"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityPassportsCreate();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/passports');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"assurance_level":"dal_0"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityPassportsCreate();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/passports"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "assurance_level": "dal_0"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityPassportsCreate();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/passports");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "assurance_level": "dal_0"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityPassportsCreate(ctx).Execute()
body := strings.NewReader(`{
  "assurance_level": "dal_0"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/passports", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "passport",
  "livemode": false,
  "mocked": true,
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "assurance_level": "dal_0",
  "status": "live",
  "valid_until": "2026-09-01T09:00:00Z",
  "assurance": "attested",
  "identifiers_held": [
    {
      "kind": "government_identifier",
      "masked": "*******4821"
    }
  ],
  "disqualifying_flag": true,
  "evidence_bundle_ids": [
    "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  ],
  "monitoring": "active",
  "issued_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/passports/{passport_id}#

Check one Passport before you act

identity.passports.retrieve

This reads one Passport back as it stands now. PROOF, RAIL, RUN and MATCH each refuse with subject_not_anchored for a subject who has no Passport, so your product can read the record those refusals turn on before it calls them.

Send the Passport's id in the path. You get that Passport back, with assurance_level for the level the subject reached, status for whether you may rely on it now, and valid_until for when the evidence behind it lapses.

The list of them is GET /v1/identity/passports. An id you hold no record for is refused.

Path parameters

passport_id string required

The id of the Passport, from passport_id on a resolved verification at GET /v1/identity/verifications/{verification_id} or from the response to POST /v1/identity/passports. It starts with anchor_pro_passport_.

Returns

The Passport.

id string required

The Passport's identifier, starting with anchor_pro_passport_. It never changes: it's the passport_id on a resolved verification or the id POST /v1/identity/passports returns; pass it to GET /v1/identity/passports/{passport_id}.

object always "passport" required

Always passport. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

subject_ref string required

Your opaque reference to the person this Passport belongs to: the subject_ref you sent at POST /v1/identity/verifications on the verification that resolved into it. Not a name and not an identifier.

assurance_level string required

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance string optional

How a fact was established. Recorded on the fact rather than in configuration, so an attested identity and a verified one stay distinguishable a year later, which is the distinction that matters when something is disputed.

attestedverified
status string required
livedowngradedrevoked
identifiers_held array of object optional

Which identifiers are on file, masked. Enough to tell a subject what you hold, never enough to use it.

2 fields
kind string required

What a consent token may authorise. Ask for what you need and no more.

government_identifierbank_identifierdate_of_birth_matchname_matchcontactability
masked string required

An identity number, masked. This is the only form in which any identifier leaves ANCHOR. The full value is never returned to you, on any endpoint, at any assurance level, to any credential. There is no parameter that widens this and no scope that reveals it. If you need to show a subject which of their identifiers you hold, show them this.

disqualifying_flag boolean optional

Whether something disqualifying exists. Whether, not what. The detail is not yours to see.

evidence_bundle_ids array of string optional

The evidence bundles behind this level, one per verification that contributed. Each names the sources consulted and what they returned, never the records behind them.

monitoring string optional

Whether this subject is still watched after verification: active means yes, paused means watching has stopped. A change that lowers or revokes the Passport reaches you as a passport.downgraded or passport.revoked event.

activepaused
valid_until string · date-time required

When the supporting evidence lapses and the level falls on its own.

issued_at string · date-time optional

When the Passport was first issued, as an RFC 3339 timestamp in UTC. Normally the moment the first verification resolved.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/passports/anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityPassportsRetrieve({ passportId: 'anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/passports/anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_passports_retrieve(passport_id='anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/passports/anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityPassportsRetrieve(passport_id: 'anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/passports/anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityPassportsRetrieve("anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/passports/anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityPassportsRetrieve(passportId: "anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/passports/anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityPassportsRetrieve(ctx, "anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/passports/anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_passport_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "passport",
  "livemode": false,
  "mocked": true,
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "assurance_level": "dal_0",
  "status": "live",
  "valid_until": "2026-09-01T09:00:00Z",
  "assurance": "attested",
  "identifiers_held": [
    {
      "kind": "government_identifier",
      "masked": "*******4821"
    }
  ],
  "disqualifying_flag": true,
  "evidence_bundle_ids": [
    "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  ],
  "monitoring": "active",
  "issued_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/merge_candidates#

Duplicates waiting on your decision

identity.merge_candidates.list

A merge candidate is two of your own references that may belong to one person, raised for you and not by you. Deciding is a separate call, so your product can hold a review queue where somebody weighs each pair before you send the merge.

You send nothing that narrows the list. Each candidate names the two references in left_ref and right_ref, how close the match is in strength, and in signals both the attributes that agree and the ones that do not.

Read one on its own at GET /v1/identity/merge_candidates/{merge_candidate_id}, passing a row's id as merge_candidate_id. When you decide two references are one person, call POST /v1/identity/merges and send that id as merge_candidate_id.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

Returns

A page of candidates.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of MergeCandidate required

The records on this page, in the order the list promises. Empty when nothing matched.

8 fields of MergeCandidate
id string required

The candidate's identifier. It starts with sub_ and never changes; pass it as merge_candidate_id at GET /v1/identity/merge_candidates/{merge_candidate_id} or on POST /v1/identity/merges when you decide.

object always "merge_candidate" required

Always merge_candidate. Tells you which kind of record you are looking at, so one handler can read any response.

left_ref string required

One of the two subject_ref values that may belong to the same person, as you first sent them at POST /v1/identity/consent_tokens. Pass it as surviving_ref or merged_ref on POST /v1/identity/merges when you decide.

right_ref string required

The other subject_ref that may belong to the same person, as you first sent it at POST /v1/identity/consent_tokens. Pass it as surviving_ref or merged_ref on POST /v1/identity/merges when you decide.

strength string required

Only a strong match merges automatically. Probable is raised for you to decide.

strongprobableweak
signals array of object required

What matched and what did not. Both, so you see the case against as well as the case for.

3 fields
signal string required

Which attribute was compared, such as date_of_birth. One entry per attribute, whichever way it came out.

agrees boolean required

true when this attribute matched across the two records, false when it didn't. Read the false entries as carefully as the true ones.

detail string optional

A plain-words note on how this attribute compared, when there's more to say than agrees. Left out otherwise.

status string optional

Where the candidate stands: open awaits your decision, merged means you merged the two records at POST /v1/identity/merges, rejected means they were judged to be different people.

openmergedrejected
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/merge_candidates?limit=25" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: limit (optional), starting_after (optional)
const result = await api.identityMergeCandidatesList({ limit: 25 });
// query parameters: limit (optional), starting_after (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/merge_candidates?limit=25', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: limit (optional), starting_after (optional)
result = api.identity_merge_candidates_list(limit=25)
import os

import requests

# query parameters: limit (optional), starting_after (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/merge_candidates?limit=25',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional)
$result = $api->identityMergeCandidatesList(limit: 25);
<?php
// query parameters: limit (optional), starting_after (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/merge_candidates?limit=25');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: limit (optional), starting_after (optional)
var result = api.identityMergeCandidatesList(25, null);
// query parameters: limit (optional), starting_after (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/merge_candidates?limit=25"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: limit (optional), starting_after (optional)
var result = api.IdentityMergeCandidatesList(limit: 25);
// query parameters: limit (optional), starting_after (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/merge_candidates?limit=25");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: limit (optional), starting_after (optional)
result, _, err := client.ANCHORAPI.IdentityMergeCandidatesList(ctx).Limit(25).Execute()
// query parameters: limit (optional), starting_after (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/merge_candidates?limit=25", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "merge_candidate",
      "left_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "right_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "strength": "strong",
      "signals": [
        {
          "signal": "date_of_birth",
          "agrees": true,
          "detail": "The payee has no verified destination, so this line cannot be paid."
        }
      ],
      "status": "open",
      "created_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true
}
GET/v1/identity/merge_candidates/{merge_candidate_id}#

Weigh up one possible duplicate

identity.merge_candidates.retrieve

This opens one merge candidate, two of your own references that may belong to one person, which is what a reviewer clicks through to from a queue or from the event that raised the pair. So they decide on the case against the match as well as the case for it.

Send the candidate's id in the path. You get the pair back with strength for how close the match is, and one signals entry for each attribute compared, each saying whether it agreed and sometimes adding a note on how.

Carry the id forward as merge_candidate_id on POST /v1/identity/merges when you decide the two are one person. An identifier with no candidate behind it answers not_found.

Path parameters

merge_candidate_id string required

The id of the candidate, from an entry in GET /v1/identity/merge_candidates or from data.id on the merge_candidate.raised event that raised it. It starts with sub_.

Returns

One candidate in full, naming the two references you send when you decide which survives.

id string required

The candidate's identifier. It starts with sub_ and never changes; pass it as merge_candidate_id at GET /v1/identity/merge_candidates/{merge_candidate_id} or on POST /v1/identity/merges when you decide.

object always "merge_candidate" required

Always merge_candidate. Tells you which kind of record you are looking at, so one handler can read any response.

left_ref string required

One of the two subject_ref values that may belong to the same person, as you first sent them at POST /v1/identity/consent_tokens. Pass it as surviving_ref or merged_ref on POST /v1/identity/merges when you decide.

right_ref string required

The other subject_ref that may belong to the same person, as you first sent it at POST /v1/identity/consent_tokens. Pass it as surviving_ref or merged_ref on POST /v1/identity/merges when you decide.

strength string required

Only a strong match merges automatically. Probable is raised for you to decide.

strongprobableweak
signals array of object required

What matched and what did not. Both, so you see the case against as well as the case for.

3 fields
signal string required

Which attribute was compared, such as date_of_birth. One entry per attribute, whichever way it came out.

agrees boolean required

true when this attribute matched across the two records, false when it didn't. Read the false entries as carefully as the true ones.

detail string optional

A plain-words note on how this attribute compared, when there's more to say than agrees. Left out otherwise.

status string optional

Where the candidate stands: open awaits your decision, merged means you merged the two records at POST /v1/identity/merges, rejected means they were judged to be different people.

openmergedrejected
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/merge_candidates/%7Bmerge_candidate_id%7D" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityMergeCandidatesRetrieve({ mergeCandidateId: '{merge_candidate_id}' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/merge_candidates/%7Bmerge_candidate_id%7D', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_merge_candidates_retrieve(merge_candidate_id='{merge_candidate_id}')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/merge_candidates/%7Bmerge_candidate_id%7D',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityMergeCandidatesRetrieve(merge_candidate_id: '{merge_candidate_id}');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/merge_candidates/%7Bmerge_candidate_id%7D');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityMergeCandidatesRetrieve("{merge_candidate_id}");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/merge_candidates/%7Bmerge_candidate_id%7D"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityMergeCandidatesRetrieve(mergeCandidateId: "{merge_candidate_id}");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/merge_candidates/%7Bmerge_candidate_id%7D");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityMergeCandidatesRetrieve(ctx, "{merge_candidate_id}").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/merge_candidates/%7Bmerge_candidate_id%7D", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "merge_candidate",
  "left_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "right_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "strength": "strong",
  "signals": [
    {
      "signal": "date_of_birth",
      "agrees": true,
      "detail": "The payee has no verified destination, so this line cannot be paid."
    }
  ],
  "status": "open",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/merges#

Decide that two records are one person

identity.merges.create

This records your decision that two of your references are one person: one survives and the other is folded into it. So the page in your product that showed two people can show one, and still carry the reason whoever decided it gave.

Send the two references, saying which survives, why you are satisfied they are one person, and a second actor, distinct from you, in approved_by. You get the merge back with its own id.

Nothing has to exist first, and you leave merge_candidate_id out when no candidate was raised.

Keep the id: it is the merge_id at POST /v1/identity/merges/{merge_id}/reverse. Use surviving_ref for the person from then on.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

surviving_ref string required

The subject_ref that remains after the merge: the candidate's left_ref or right_ref, or a reference you first sent at POST /v1/identity/consent_tokens. Use it for the person from then on.

merged_ref string required

The subject_ref absorbed into surviving_ref: the other of the candidate's left_ref and right_ref, or a reference you first sent at POST /v1/identity/consent_tokens. Reversing the merge brings it back as it was.

merge_candidate_id string optional

The candidate this merge answers: its id, from an entry in GET /v1/identity/merge_candidates or from data.id on the merge_candidate.raised event; it starts with sub_. Leave it out when no candidate was raised for these two records.

reason string required

Why you're satisfied the two records are one person, in plain words. It stays on the merge for whoever later asks why.

approved_by string required

A second actor, distinct from the caller.

Returns

The merge.

id string required

The merge's identifier, returned by POST /v1/identity/merges. It never changes; pass it as merge_id at POST /v1/identity/merges/{merge_id}/reverse to reverse the merge.

object always "identity_merge" required

Always identity_merge. Tells you which kind of record you are looking at, so one handler can read any response.

surviving_ref string required

The subject_ref that remains after the merge, as you sent it in surviving_ref on POST /v1/identity/merges. Use it for the person from then on.

merged_ref string required

The subject_ref absorbed into surviving_ref, as you sent it in merged_ref on POST /v1/identity/merges. After a reversal it stands on its own again.

merge_candidate_id string · nullable optional

The candidate this merge answered, as you sent it in merge_candidate_id on POST /v1/identity/merges: the id of a merge candidate, starting with sub_. null when the merge was made without a raised candidate.

four_eyes FourEyes required

Two distinct actors. The same credential twice is refused.

2 fields of FourEyes
requested_by string required

The actor who requested the action: the credential that made the call. Never the same as approved_by.

approved_by string required

The second actor who approved the action, as you named them in approved_by on the request. Never the same credential as requested_by.

reason string optional

Why the two records were merged, as you stated it on the request. It stays on the record so the decision can be explained later.

reversible always true required

Always true. Every merge can be reversed at POST /v1/identity/merges/{merge_id}/reverse, and both records go back to their state before it.

before_state_ref string optional

A reference to the complete state of both records before the merge, kept so the merge can be undone at POST /v1/identity/merges/{merge_id}/reverse.

reversed_at string · date-time · nullable optional

When the merge was reversed, as an RFC 3339 timestamp in UTC. null while the merge still stands.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/merges" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"surviving_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","merged_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","merge_candidate_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityMergesCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorMergeCreateRequest: {"survivingRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","mergedRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approvedBy":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","mergeCandidateId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/merges', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "reason": "The requester confirmed the work in person.",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_merges_create(body={"surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/merges',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityMergesCreate($idempotencyKey, json_decode('{"surviving_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","merged_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","merge_candidate_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/merges');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"surviving_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","merged_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","merge_candidate_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityMergesCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/merges"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "reason": "The requester confirmed the work in person.",
          "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityMergesCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/merges");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "reason": "The requester confirmed the work in person.",
      "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityMergesCreate(ctx).IdempotencyKey(key).AnchorMergeCreateRequest(body).Execute()
body := strings.NewReader(`{
  "surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/merges", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "identity_merge",
  "surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "four_eyes": {
    "requested_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  },
  "reversible": true,
  "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "before_state_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reversed_at": "2026-09-01T09:00:00Z",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/merges/{merge_id}/reverse#

Undo a merge you got wrong

identity.merges.reverse

This takes a merge back. Both references stand on their own again, from the record of how they looked before it, so a mistake here is something your product can correct and not something it lives with.

Send the merge's id in the path as merge_id, with why you are undoing it in reason and who approved it in approved_by. You get the merge back with reversed_at set.

A merge has to exist first, from POST /v1/identity/merges, and the id it returned is what you send here.

Undoing a decision is a decision, and the reversal is held to the same standard as the merge it undoes.

Use each reference on its own from here. A merge_id with no merge behind it answers not_found.

Path parameters

merge_id string required

The id of the merge you want to reverse, from the response to POST /v1/identity/merges.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

reason string required

Why you are undoing this, in your words. It is recorded with the reversal, so write what a reviewer would need to read.

approved_by string required

A second actor who approved the reversal, distinct from you. The same credential twice is refused.

Returns

The reversed merge.

id string required

The merge's identifier, returned by POST /v1/identity/merges. It never changes; pass it as merge_id at POST /v1/identity/merges/{merge_id}/reverse to reverse the merge.

object always "identity_merge" required

Always identity_merge. Tells you which kind of record you are looking at, so one handler can read any response.

surviving_ref string required

The subject_ref that remains after the merge, as you sent it in surviving_ref on POST /v1/identity/merges. Use it for the person from then on.

merged_ref string required

The subject_ref absorbed into surviving_ref, as you sent it in merged_ref on POST /v1/identity/merges. After a reversal it stands on its own again.

merge_candidate_id string · nullable optional

The candidate this merge answered, as you sent it in merge_candidate_id on POST /v1/identity/merges: the id of a merge candidate, starting with sub_. null when the merge was made without a raised candidate.

four_eyes FourEyes required

Two distinct actors. The same credential twice is refused.

2 fields of FourEyes
requested_by string required

The actor who requested the action: the credential that made the call. Never the same as approved_by.

approved_by string required

The second actor who approved the action, as you named them in approved_by on the request. Never the same credential as requested_by.

reason string optional

Why the two records were merged, as you stated it on the request. It stays on the record so the decision can be explained later.

reversible always true required

Always true. Every merge can be reversed at POST /v1/identity/merges/{merge_id}/reverse, and both records go back to their state before it.

before_state_ref string optional

A reference to the complete state of both records before the merge, kept so the merge can be undone at POST /v1/identity/merges/{merge_id}/reverse.

reversed_at string · date-time · nullable optional

When the merge was reversed, as an RFC 3339 timestamp in UTC. null while the merge still stands.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/merges/%7Bmerge_id%7D/reverse" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityMergesReverse({
  mergeId: '{merge_id}',
  idempotencyKey: crypto.randomUUID(),
  anchorReversalRequest: {"reason":"The requester confirmed the work in person.","approvedBy":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/merges/%7Bmerge_id%7D/reverse', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "reason": "The requester confirmed the work in person.",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_merges_reverse(merge_id='{merge_id}', body={"reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/merges/%7Bmerge_id%7D/reverse',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityMergesReverse($idempotencyKey, json_decode('{"reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/merges/%7Bmerge_id%7D/reverse');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityMergesReverse("{merge_id}", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/merges/%7Bmerge_id%7D/reverse"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "reason": "The requester confirmed the work in person.",
          "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityMergesReverse(mergeId: "{merge_id}", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/merges/%7Bmerge_id%7D/reverse");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "reason": "The requester confirmed the work in person.",
      "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityMergesReverse(ctx, "{merge_id}").IdempotencyKey(key).AnchorReversalRequest(body).Execute()
body := strings.NewReader(`{
  "reason": "The requester confirmed the work in person.",
  "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/merges/%7Bmerge_id%7D/reverse", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "identity_merge",
  "surviving_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "merged_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "four_eyes": {
    "requested_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  },
  "reversible": true,
  "merge_candidate_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "before_state_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reversed_at": "2026-09-01T09:00:00Z",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/blocklist_entries#

Blocklist an identity

identity.blocklist_entries.create

This stops one person transacting anywhere on Heros. The reason you give stays on the entry, so whoever later asks why this person was stopped reads your own words off the record.

Send your subject_ref for the person, why you are stopping them, and a second actor in approved_by. You get the entry back with the id you will need to lift it.

Nothing has to exist first.

Send the same subject_ref you used on their consent token. A new string here makes an entry about somebody you hold no other record of.

No list and no retrieve exists for these entries, so keep the id you get back: it is the blocklist_entry_id at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

subject_ref string required

Your opaque reference to the person you are blocklisting: the same subject_ref you first sent at POST /v1/identity/consent_tokens and again at POST /v1/identity/verifications. Not a name and not an identifier.

reason string required

Why this person is being blocklisted, in your words. It stays on the entry, so write what a reviewer would need to read.

approved_by string required

A second actor, distinct from the caller.

Returns

The blocklist entry.

id string required

The entry's identifier, returned by POST /v1/identity/blocklist_entries. It never changes; pass it as blocklist_entry_id at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse to lift the blocklisting.

object always "blocklist_entry" required

Always blocklist_entry. Tells you which kind of record you are looking at, so one handler can read any response.

subject_ref string required

Your opaque reference to the blocklisted person, as you sent it in subject_ref on POST /v1/identity/blocklist_entries and first at POST /v1/identity/consent_tokens. Not a name and not an identifier.

reason string required

Why this person was blocklisted, as you stated it on the request.

four_eyes FourEyes required

Two distinct actors. The same credential twice is refused.

2 fields of FourEyes
requested_by string required

The actor who requested the action: the credential that made the call. Never the same as approved_by.

approved_by string required

The second actor who approved the action, as you named them in approved_by on the request. Never the same credential as requested_by.

status string required

active while the person is stopped from transacting anywhere on Heros; lifted once you reversed the blocklisting at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse.

activelifted
before_state_ref string optional

A reference to the person's standing before the blocklisting, kept so it can be restored when you lift the entry at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/blocklist_entries" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityBlocklistEntriesCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorBlocklistCreateRequest: {"subjectRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approvedBy":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/blocklist_entries', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "reason": "The requester confirmed the work in person.",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_blocklist_entries_create(body={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/blocklist_entries',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityBlocklistEntriesCreate($idempotencyKey, json_decode('{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/blocklist_entries');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityBlocklistEntriesCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/blocklist_entries"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "reason": "The requester confirmed the work in person.",
          "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityBlocklistEntriesCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/blocklist_entries");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "reason": "The requester confirmed the work in person.",
      "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityBlocklistEntriesCreate(ctx).IdempotencyKey(key).AnchorBlocklistCreateRequest(body).Execute()
body := strings.NewReader(`{
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/blocklist_entries", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "blocklist_entry",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "four_eyes": {
    "requested_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  },
  "status": "active",
  "before_state_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/blocklist_entries/{blocklist_entry_id}/reverse#

Lift a blocklisting

identity.blocklist_entries.reverse

This lifts a blocklisting and puts back the standing the person held before it. An entry carries no end date of its own, so a stop you later find was wrong stays in force until this call takes it off.

Send the entry's id in the path, with why you are undoing it and a second actor in approved_by. You get the entry back.

The entry has to exist first, from POST /v1/identity/blocklist_entries.

An id you hold no record for is refused.

Path parameters

blocklist_entry_id string required

The id of the entry you want to lift, from the response to POST /v1/identity/blocklist_entries.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

reason string required

Why you are undoing this, in your words. It is recorded with the reversal, so write what a reviewer would need to read.

approved_by string required

A second actor who approved the reversal, distinct from you. The same credential twice is refused.

Returns

The same entry, still carrying why the person was stopped, not the reason you gave for undoing it.

id string required

The entry's identifier, returned by POST /v1/identity/blocklist_entries. It never changes; pass it as blocklist_entry_id at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse to lift the blocklisting.

object always "blocklist_entry" required

Always blocklist_entry. Tells you which kind of record you are looking at, so one handler can read any response.

subject_ref string required

Your opaque reference to the blocklisted person, as you sent it in subject_ref on POST /v1/identity/blocklist_entries and first at POST /v1/identity/consent_tokens. Not a name and not an identifier.

reason string required

Why this person was blocklisted, as you stated it on the request.

four_eyes FourEyes required

Two distinct actors. The same credential twice is refused.

2 fields of FourEyes
requested_by string required

The actor who requested the action: the credential that made the call. Never the same as approved_by.

approved_by string required

The second actor who approved the action, as you named them in approved_by on the request. Never the same credential as requested_by.

status string required

active while the person is stopped from transacting anywhere on Heros; lifted once you reversed the blocklisting at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse.

activelifted
before_state_ref string optional

A reference to the person's standing before the blocklisting, kept so it can be restored when you lift the entry at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/blocklist_entries/%7Bblocklist_entry_id%7D/reverse" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityBlocklistEntriesReverse({
  blocklistEntryId: '{blocklist_entry_id}',
  idempotencyKey: crypto.randomUUID(),
  anchorReversalRequest: {"reason":"The requester confirmed the work in person.","approvedBy":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/blocklist_entries/%7Bblocklist_entry_id%7D/reverse', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "reason": "The requester confirmed the work in person.",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_blocklist_entries_reverse(blocklist_entry_id='{blocklist_entry_id}', body={"reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/blocklist_entries/%7Bblocklist_entry_id%7D/reverse',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"reason": "The requester confirmed the work in person.", "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityBlocklistEntriesReverse($idempotencyKey, json_decode('{"reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/blocklist_entries/%7Bblocklist_entry_id%7D/reverse');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"reason":"The requester confirmed the work in person.","approved_by":"usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityBlocklistEntriesReverse("{blocklist_entry_id}", idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/blocklist_entries/%7Bblocklist_entry_id%7D/reverse"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "reason": "The requester confirmed the work in person.",
          "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityBlocklistEntriesReverse(blocklistEntryId: "{blocklist_entry_id}", idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/blocklist_entries/%7Bblocklist_entry_id%7D/reverse");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "reason": "The requester confirmed the work in person.",
      "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityBlocklistEntriesReverse(ctx, "{blocklist_entry_id}").IdempotencyKey(key).AnchorReversalRequest(body).Execute()
body := strings.NewReader(`{
  "reason": "The requester confirmed the work in person.",
  "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/blocklist_entries/%7Bblocklist_entry_id%7D/reverse", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "blocklist_entry",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "four_eyes": {
    "requested_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "approved_by": "usr_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
  },
  "status": "active",
  "before_state_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/disclosures#

Which disclosures you hold, and until when

identity.disclosures.list

This answers which people your organisation holds a disclosure for, and what each one was granted for. So a screen in your product can show every disclosure you hold, whatever standing it is in, and when each one ends.

A disclosure is one person's consented permission for you to see named facts drawn from their Passport. Each record carries the Passport, the purpose, what it covers, when it ends and status.

Read status on every record before you act on one. A disclosure can be revoked while you hold it, so a record on this page is not proof you may still see what it names.

Open one in full at GET /v1/identity/disclosures/{disclosure_id}, or end one at POST /v1/identity/disclosures/{disclosure_id}/revoke, passing a record's id as disclosure_id.

Query parameters

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

status string optional

Returns only disclosures in one standing: active (you may still see the fields), revoked (you or the subject ended it), expired (past expires_at) or suspended (consent or the Passport fell away). Leave it out to get every standing.

activerevokedexpiredsuspended

Returns

A page of disclosures.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of Disclosure required

The records on this page, in the order the list promises. Empty when nothing matched.

13 fields of Disclosure
id string required

The disclosure's identifier, returned by POST /v1/identity/disclosures. It starts with anchor_pro_disclosure_ and never changes; pass it as disclosure_id to retrieve or revoke the disclosure.

object always "disclosure" required

Always disclosure. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

passport_id string required

The Passport this disclosure is drawn from, as you sent it in passport_id on POST /v1/identity/disclosures. It is the id of a Passport and starts with anchor_pro_passport_.

subject_ref string optional

Your opaque reference to the person the Passport belongs to: the subject_ref you first sent at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

The purpose you gave on the request, such as employment_verification. What you may see is scoped to it.

consent_token_id string optional

The consent token this disclosure rests on: the id of a token from POST /v1/identity/consent_tokens, starting with anchor_pro_consent_, as you sent it on the request. If the subject withdraws that consent, the disclosure suspends within 24 hours.

fields_disclosed array of string optional

What you may see. Identifiers appear masked and never in full.

status string required
activerevokedexpiredsuspended
suspended_because string · nullable optional

Why the disclosure is suspended: consent_withdrawn when the subject withdrew consent, passport_revoked when the Passport was revoked, passport_downgraded when its level fell. null unless status is suspended.

consent_withdrawnpassport_revokedpassport_downgradednull
expires_at string · date-time required

Time boxed. There is no open ended disclosure.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: limit (optional), starting_after (optional), status (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/disclosures?limit=25&status=active" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: limit (optional), starting_after (optional), status (optional)
const result = await api.identityDisclosuresList({ limit: 25, status: 'active' });
// query parameters: limit (optional), starting_after (optional), status (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/disclosures?limit=25&status=active', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: limit (optional), starting_after (optional), status (optional)
result = api.identity_disclosures_list(limit=25, status='active')
import os

import requests

# query parameters: limit (optional), starting_after (optional), status (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/disclosures?limit=25&status=active',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: limit (optional), starting_after (optional), status (optional)
$result = $api->identityDisclosuresList(limit: 25, status: 'active');
<?php
// query parameters: limit (optional), starting_after (optional), status (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/disclosures?limit=25&status=active');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;
import com.useheros.sdk.model.*;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: limit (optional), starting_after (optional), status (optional)
var result = api.identityDisclosuresList(25, null, AnchorDisclosureStatus.fromValue("active"));
// query parameters: limit (optional), starting_after (optional), status (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/disclosures?limit=25&status=active"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: limit (optional), starting_after (optional), status (optional)
var result = api.IdentityDisclosuresList(limit: 25, status: AnchorDisclosureStatus.Active);
// query parameters: limit (optional), starting_after (optional), status (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/disclosures?limit=25&status=active");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: limit (optional), starting_after (optional), status (optional)
result, _, err := client.ANCHORAPI.IdentityDisclosuresList(ctx).Limit(25).Status(heros.AnchorDisclosureStatus("active")).Execute()
// query parameters: limit (optional), starting_after (optional), status (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/disclosures?limit=25&status=active", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "disclosure",
      "livemode": false,
      "mocked": true,
      "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "purpose": "employment_verification",
      "status": "active",
      "expires_at": "2026-09-01T09:00:00Z",
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "fields_disclosed": [
        "example"
      ],
      "suspended_because": "consent_withdrawn",
      "created_at": "2026-09-01T09:00:00Z"
    }
  ],
  "has_more": true
}
POST/v1/identity/disclosures#

Ask to see what a Passport holds

identity.disclosures.create

This asks for permission to see named facts about one person, drawn from the Passport held for them.

Send the passport_id, the purpose you need it for, the consent token it rests on and the date it ends, with the fields you want in fields_requested. You get the disclosure back, with an id starting anchor_pro_disclosure_.

A Passport has to exist first, from POST /v1/identity/verifications/{verification_id}/resolve or from POST /v1/identity/passports, and a consent token from POST /v1/identity/consent_tokens.

Key your screen on fields_disclosed from the response, not on the fields_requested you sent. The two are not always the same list.

Carry the id as disclosure_id to GET /v1/identity/disclosures/{disclosure_id} and to POST /v1/identity/disclosures/{disclosure_id}/revoke.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

passport_id string required

The Passport you want a disclosure from: its id, from passport_id on a resolved verification at GET /v1/identity/verifications/{verification_id} or from the response to POST /v1/identity/passports. It starts with anchor_pro_passport_.

purpose string required

What you'll use the disclosure for, such as employment_verification. It scopes what you may see, and there is no disclosure without one.

consent_token_id string required

The id of a consent token from POST /v1/identity/consent_tokens or GET /v1/identity/consent_tokens that covers this subject and purpose; it starts with anchor_pro_consent_. One that doesn't is refused with consent_token_missing.

fields_requested array of string optional

The fields you want to see, by name. What you actually get is listed in fields_disclosed on the response, and identifiers appear masked and never in full.

expires_at string · date-time required

When the disclosure ends, as an RFC 3339 timestamp in UTC. Required: there is no open ended disclosure.

Returns

The disclosure.

id string required

The disclosure's identifier, returned by POST /v1/identity/disclosures. It starts with anchor_pro_disclosure_ and never changes; pass it as disclosure_id to retrieve or revoke the disclosure.

object always "disclosure" required

Always disclosure. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

passport_id string required

The Passport this disclosure is drawn from, as you sent it in passport_id on POST /v1/identity/disclosures. It is the id of a Passport and starts with anchor_pro_passport_.

subject_ref string optional

Your opaque reference to the person the Passport belongs to: the subject_ref you first sent at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

The purpose you gave on the request, such as employment_verification. What you may see is scoped to it.

consent_token_id string optional

The consent token this disclosure rests on: the id of a token from POST /v1/identity/consent_tokens, starting with anchor_pro_consent_, as you sent it on the request. If the subject withdraws that consent, the disclosure suspends within 24 hours.

fields_disclosed array of string optional

What you may see. Identifiers appear masked and never in full.

status string required
activerevokedexpiredsuspended
suspended_because string · nullable optional

Why the disclosure is suspended: consent_withdrawn when the subject withdrew consent, passport_revoked when the Passport was revoked, passport_downgraded when its level fell. null unless status is suspended.

consent_withdrawnpassport_revokedpassport_downgradednull
expires_at string · date-time required

Time boxed. There is no open ended disclosure.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/disclosures" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"passport_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expires_at":"2026-09-01T09:00:00Z","fields_requested":["example"]}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityDisclosuresCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorDisclosureCreateRequest: {"passportId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","consentTokenId":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expiresAt":"2026-09-01T09:00:00Z","fieldsRequested":["example"]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/disclosures', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "purpose": "employment_verification",
    "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "expires_at": "2026-09-01T09:00:00Z",
    "fields_requested": [
      "example"
    ]
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_disclosures_create(body={"passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "purpose": "employment_verification", "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "expires_at": "2026-09-01T09:00:00Z", "fields_requested": ["example"]})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/disclosures',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "purpose": "employment_verification", "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "expires_at": "2026-09-01T09:00:00Z", "fields_requested": ["example"]},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityDisclosuresCreate($idempotencyKey, json_decode('{"passport_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expires_at":"2026-09-01T09:00:00Z","fields_requested":["example"]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/disclosures');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"passport_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","purpose":"employment_verification","consent_token_id":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","expires_at":"2026-09-01T09:00:00Z","fields_requested":["example"]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityDisclosuresCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/disclosures"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "purpose": "employment_verification",
          "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "expires_at": "2026-09-01T09:00:00Z",
          "fields_requested": [
            "example"
          ]
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityDisclosuresCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/disclosures");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "purpose": "employment_verification",
      "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "expires_at": "2026-09-01T09:00:00Z",
      "fields_requested": [
        "example"
      ]
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityDisclosuresCreate(ctx).IdempotencyKey(key).AnchorDisclosureCreateRequest(body).Execute()
body := strings.NewReader(`{
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "expires_at": "2026-09-01T09:00:00Z",
  "fields_requested": [
    "example"
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/disclosures", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "disclosure",
  "livemode": false,
  "mocked": true,
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "status": "active",
  "expires_at": "2026-09-01T09:00:00Z",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "fields_disclosed": [
    "example"
  ],
  "suspended_because": "consent_withdrawn",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/disclosures/reconcile#

Recover from missed revocation events

identity.disclosures.reconcile

This answers where every disclosure you hold stands right now. A revocation reaches every holder within 15 minutes, so when your consumer was down or dropped the event, this is how you find the one you missed before you act on a Passport that is no longer live.

Each row names a disclosure you hold, its standing, and the sequence number of its latest change; as_of_sequence gives the point the report was taken at.

Read a row in full at GET /v1/identity/disclosures/{disclosure_id}, passing its disclosure_id, and drop what you cached for one you may no longer see.

Query parameters

since_sequence integer optional

The as_of_sequence from your last reconcile response. You get only the disclosures that changed after it; leave it out to get the current standing of every disclosure you hold.

Returns

The current standing of every disclosure you hold.

object always "disclosure_reconciliation" required

Always disclosure_reconciliation. Tells you which kind of record you are looking at, so one handler can read any response.

as_of_sequence integer · minimum 0 required

The sequence number this report is current to. Keep it and pass it as since_sequence next time to get only what changed after it.

generated_at string · date-time optional

When this report was produced, as an RFC 3339 timestamp in UTC.

disclosures array of object required

One row per disclosure your organisation holds, giving its current standing and the sequence number of its latest change. Only the changes after since_sequence when you passed one.

5 fields
disclosure_id string required

The id of the disclosure this row is about, as POST /v1/identity/disclosures returned it or GET /v1/identity/disclosures lists it; it starts with anchor_pro_disclosure_. Pass it as disclosure_id to retrieve or revoke the disclosure.

passport_id string optional

The id of the Passport the disclosure is drawn from, as GET /v1/identity/passports lists it; it starts with anchor_pro_passport_ and is absent when there is none. Use it to find what you hold against a Passport that is no longer live.

status string required
activerevokedexpiredsuspended
sequence integer required

The sequence number of this disclosure's latest change. Higher means later; compare it with the last event you handled to see what you missed.

changed_at string · date-time optional

When the disclosure last changed standing, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: since_sequence (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/disclosures/reconcile?since_sequence=0" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: since_sequence (optional)
const result = await api.identityDisclosuresReconcile({ sinceSequence: 0 });
// query parameters: since_sequence (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/disclosures/reconcile?since_sequence=0', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: since_sequence (optional)
result = api.identity_disclosures_reconcile(since_sequence=0)
import os

import requests

# query parameters: since_sequence (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/disclosures/reconcile?since_sequence=0',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: since_sequence (optional)
$result = $api->identityDisclosuresReconcile(since_sequence: 0);
<?php
// query parameters: since_sequence (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/disclosures/reconcile?since_sequence=0');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: since_sequence (optional)
var result = api.identityDisclosuresReconcile(0);
// query parameters: since_sequence (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/disclosures/reconcile?since_sequence=0"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: since_sequence (optional)
var result = api.IdentityDisclosuresReconcile(sinceSequence: 0);
// query parameters: since_sequence (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/disclosures/reconcile?since_sequence=0");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: since_sequence (optional)
result, _, err := client.ANCHORAPI.IdentityDisclosuresReconcile(ctx).SinceSequence(0).Execute()
// query parameters: since_sequence (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/disclosures/reconcile?since_sequence=0", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "disclosure_reconciliation",
  "as_of_sequence": 0,
  "disclosures": [
    {
      "disclosure_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "status": "active",
      "sequence": 1,
      "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "changed_at": "2026-09-01T09:00:00Z"
    }
  ],
  "generated_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/disclosures/{disclosure_id}#

Check what you may still see

identity.disclosures.retrieve

This reads one disclosure back as it stands now. A disclosure is one person's consented permission for you to see named facts drawn from their Passport, so this is where you check that a permission you stored days ago is one you still hold.

Send the disclosure's id in the path. You get the purpose it was granted for, the consent token it rests on, what it covers in fields_disclosed, and status for where it stands.

A 200 here is not permission: a revoked disclosure still reads back, so read status before you act on what the record names. An id you hold no record for is refused.

Path parameters

disclosure_id string required

The disclosure's id, as POST /v1/identity/disclosures returned it when you created it or as GET /v1/identity/disclosures lists it. It starts with anchor_pro_disclosure_.

Returns

The disclosure, with the expires_at it runs to, so your own system can drop it before that moment.

id string required

The disclosure's identifier, returned by POST /v1/identity/disclosures. It starts with anchor_pro_disclosure_ and never changes; pass it as disclosure_id to retrieve or revoke the disclosure.

object always "disclosure" required

Always disclosure. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

passport_id string required

The Passport this disclosure is drawn from, as you sent it in passport_id on POST /v1/identity/disclosures. It is the id of a Passport and starts with anchor_pro_passport_.

subject_ref string optional

Your opaque reference to the person the Passport belongs to: the subject_ref you first sent at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

The purpose you gave on the request, such as employment_verification. What you may see is scoped to it.

consent_token_id string optional

The consent token this disclosure rests on: the id of a token from POST /v1/identity/consent_tokens, starting with anchor_pro_consent_, as you sent it on the request. If the subject withdraws that consent, the disclosure suspends within 24 hours.

fields_disclosed array of string optional

What you may see. Identifiers appear masked and never in full.

status string required
activerevokedexpiredsuspended
suspended_because string · nullable optional

Why the disclosure is suspended: consent_withdrawn when the subject withdrew consent, passport_revoked when the Passport was revoked, passport_downgraded when its level fell. null unless status is suspended.

consent_withdrawnpassport_revokedpassport_downgradednull
expires_at string · date-time required

Time boxed. There is no open ended disclosure.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityDisclosuresRetrieve({ disclosureId: 'anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_disclosures_retrieve(disclosure_id='anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityDisclosuresRetrieve(disclosure_id: 'anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityDisclosuresRetrieve("anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityDisclosuresRetrieve(disclosureId: "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityDisclosuresRetrieve(ctx, "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "disclosure",
  "livemode": false,
  "mocked": true,
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "status": "active",
  "expires_at": "2026-09-01T09:00:00Z",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "fields_disclosed": [
    "example"
  ],
  "suspended_because": "consent_withdrawn",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/disclosures/{disclosure_id}/revoke#

Hand back access you no longer need

identity.disclosures.revoke

This ends one disclosure straight away, before the date it would have ended on. So you can give access back as soon as you are done with it, and a permission you hold is always one the person it is about can end themselves.

Send the disclosure's id in the path. Send no request body; you get the disclosure back, the same record GET /v1/identity/disclosures/{disclosure_id} reads.

The disclosure has to exist first, from POST /v1/identity/disclosures.

A disclosure.revoked event follows, so the rest of your system can drop what it cached. A disclosure that is no longer live is refused with conflict. Nothing here puts one back: ask again at POST /v1/identity/disclosures.

Path parameters

disclosure_id string required

The disclosure's id, as POST /v1/identity/disclosures returned it when you created it or as GET /v1/identity/disclosures lists it. It starts with anchor_pro_disclosure_.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The revoked disclosure.

id string required

The disclosure's identifier, returned by POST /v1/identity/disclosures. It starts with anchor_pro_disclosure_ and never changes; pass it as disclosure_id to retrieve or revoke the disclosure.

object always "disclosure" required

Always disclosure. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

passport_id string required

The Passport this disclosure is drawn from, as you sent it in passport_id on POST /v1/identity/disclosures. It is the id of a Passport and starts with anchor_pro_passport_.

subject_ref string optional

Your opaque reference to the person the Passport belongs to: the subject_ref you first sent at POST /v1/identity/consent_tokens. Not a name and not an identifier.

purpose string required

The purpose you gave on the request, such as employment_verification. What you may see is scoped to it.

consent_token_id string optional

The consent token this disclosure rests on: the id of a token from POST /v1/identity/consent_tokens, starting with anchor_pro_consent_, as you sent it on the request. If the subject withdraws that consent, the disclosure suspends within 24 hours.

fields_disclosed array of string optional

What you may see. Identifiers appear masked and never in full.

status string required
activerevokedexpiredsuspended
suspended_because string · nullable optional

Why the disclosure is suspended: consent_withdrawn when the subject withdrew consent, passport_revoked when the Passport was revoked, passport_downgraded when its level fell. null unless status is suspended.

consent_withdrawnpassport_revokedpassport_downgradednull
expires_at string · date-time required

Time boxed. There is no open ended disclosure.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityDisclosuresRevoke({ disclosureId: 'anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Idempotency-Key': crypto.randomUUID(),
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_disclosures_revoke(disclosure_id='anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityDisclosuresRevoke(disclosure_id: 'anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityDisclosuresRevoke("anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityDisclosuresRevoke(disclosureId: "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityDisclosuresRevoke(ctx, "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/disclosures/anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/revoke", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_disclosure_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "disclosure",
  "livemode": false,
  "mocked": true,
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "purpose": "employment_verification",
  "status": "revoked",
  "expires_at": "2026-09-01T09:00:00Z",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "fields_disclosed": [
    "example"
  ],
  "suspended_because": "consent_withdrawn",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/subject_requests#

Exercise a subject data right

identity.subject_requests.create

A person whose data you hold can ask to see it, correct it, erase it, restrict what you do with it, or take a copy elsewhere. Raising it here gives that ask a record with a due date, so the day you owe them an answer is a field your own system can act on and not a date somebody has to remember.

Send the right as kind and the person as subject_ref. You get the request back with its due_by date and an id to read it with later.

Nothing has to exist first. Use the subject_ref you already chose for this person.

Read it back at GET /v1/identity/subject_requests/{subject_request_id}, sending the id as subject_request_id.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

kind string required
accesscorrectionerasurerestrict_processingportability
subject_ref string required

The opaque reference you chose for the person exercising the right: the same subject_ref you sent when you recorded their consent at POST /v1/identity/consent_tokens and verified them. Not a name and not an identifier.

note string optional

Free text you attach to the request, such as what the subject asked for and how they asked. Optional.

Returns

The request, with the date it is due.

id string required

The record's identifier, returned by POST /v1/identity/subject_requests when you raise the request. It starts with sub_ and never changes; pass it as subject_request_id to GET /v1/identity/subject_requests/{subject_request_id}.

object always "subject_request" required

Always subject_request. Tells you which kind of record you are looking at, so one handler can read any response.

kind string required
accesscorrectionerasurerestrict_processingportability
subject_ref string required

The opaque reference to the person exercising the right, exactly as you sent it in subject_ref at POST /v1/identity/subject_requests. Not a name and not an identifier.

status string required

Where the request stands: received when you raise it, in_progress while it's handled, completed once done with completed_at set, or refused when it's turned down. Kept records never make it refused: they're listed in retained.

receivedin_progresscompletedrefused
due_by string · date required

The date the request is due to be answered by, as a calendar date in YYYY-MM-DD. Set when you raise it, so you can chase before it passes.

retained array of object optional

What was kept and under which obligation. Erasure removes personal data while keeping signed records verifiable and honouring statutory retention.

3 fields
category string required

The kind of record that was kept, in words you can pass on to the subject. Read it with lawful_basis to say what remains and why.

lawful_basis string required

Why this category was kept: the obligation that stops it being erased, in words you can quote to the subject when you answer them.

until string · date optional

The date the retention ends, as a calendar date in YYYY-MM-DD. Absent when no end date applies.

completed_at string · date-time · nullable optional

When the request was completed, as an RFC 3339 timestamp in UTC. null while the request is still open.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/subject_requests" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"kind":"access","subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","note":"example"}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identitySubjectRequestsCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorSubjectRequestCreateRequest: {"kind":"access","subjectRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","note":"example"},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/subject_requests', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "kind": "access",
    "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "note": "example"
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_subject_requests_create(body={"kind": "access", "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "note": "example"})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/subject_requests',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"kind": "access", "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "note": "example"},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identitySubjectRequestsCreate($idempotencyKey, json_decode('{"kind":"access","subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","note":"example"}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/subject_requests');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"kind":"access","subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","note":"example"}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identitySubjectRequestsCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/subject_requests"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "kind": "access",
          "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "note": "example"
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentitySubjectRequestsCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/subject_requests");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "kind": "access",
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "note": "example"
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentitySubjectRequestsCreate(ctx).IdempotencyKey(key).AnchorSubjectRequestCreateRequest(body).Execute()
body := strings.NewReader(`{
  "kind": "access",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "note": "example"
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/subject_requests", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "subject_request",
  "kind": "access",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "received",
  "due_by": "2026-09-01",
  "retained": [
    {
      "category": "example",
      "lawful_basis": "example",
      "until": "2026-09-01"
    }
  ],
  "completed_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/subject_requests/{subject_request_id}#

What a subject asked for

identity.subject_requests.retrieve

A subject request is one person's exercise of a data right over the data you hold, and you read one back here: the right they used, when you owe them an answer, and anything that was kept. So the words you give the person who asked come from the record and not from somebody's memory of the case.

Send the id you were given as subject_request_id, and you get that one request back with its due_by date and anything in retained.

Nothing else waits on this call.

Path parameters

subject_request_id string required

The subject request's id, as POST /v1/identity/subject_requests returned it when you raised the request. It starts with sub_.

Returns

The subject request.

id string required

The record's identifier, returned by POST /v1/identity/subject_requests when you raise the request. It starts with sub_ and never changes; pass it as subject_request_id to GET /v1/identity/subject_requests/{subject_request_id}.

object always "subject_request" required

Always subject_request. Tells you which kind of record you are looking at, so one handler can read any response.

kind string required
accesscorrectionerasurerestrict_processingportability
subject_ref string required

The opaque reference to the person exercising the right, exactly as you sent it in subject_ref at POST /v1/identity/subject_requests. Not a name and not an identifier.

status string required

Where the request stands: received when you raise it, in_progress while it's handled, completed once done with completed_at set, or refused when it's turned down. Kept records never make it refused: they're listed in retained.

receivedin_progresscompletedrefused
due_by string · date required

The date the request is due to be answered by, as a calendar date in YYYY-MM-DD. Set when you raise it, so you can chase before it passes.

retained array of object optional

What was kept and under which obligation. Erasure removes personal data while keeping signed records verifiable and honouring statutory retention.

3 fields
category string required

The kind of record that was kept, in words you can pass on to the subject. Read it with lawful_basis to say what remains and why.

lawful_basis string required

Why this category was kept: the obligation that stops it being erased, in words you can quote to the subject when you answer them.

until string · date optional

The date the retention ends, as a calendar date in YYYY-MM-DD. Absent when no end date applies.

completed_at string · date-time · nullable optional

When the request was completed, as an RFC 3339 timestamp in UTC. null while the request is still open.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/subject_requests/%7Bsubject_request_id%7D" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identitySubjectRequestsRetrieve({ subjectRequestId: '{subject_request_id}' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/subject_requests/%7Bsubject_request_id%7D', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_subject_requests_retrieve(subject_request_id='{subject_request_id}')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/subject_requests/%7Bsubject_request_id%7D',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identitySubjectRequestsRetrieve(subject_request_id: '{subject_request_id}');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/subject_requests/%7Bsubject_request_id%7D');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identitySubjectRequestsRetrieve("{subject_request_id}");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/subject_requests/%7Bsubject_request_id%7D"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentitySubjectRequestsRetrieve(subjectRequestId: "{subject_request_id}");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/subject_requests/%7Bsubject_request_id%7D");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentitySubjectRequestsRetrieve(ctx, "{subject_request_id}").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/subject_requests/%7Bsubject_request_id%7D", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "subject_request",
  "kind": "access",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "status": "received",
  "due_by": "2026-09-01",
  "retained": [
    {
      "category": "example",
      "lawful_basis": "example",
      "until": "2026-09-01"
    }
  ],
  "completed_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/access_grants#

Record a reason before viewing unmasked personal data

identity.access_grants.create

Before anyone looks at a person's unmasked data, this is where you say why. The grant reaches one person and only the fields you name, and it lapses, so a look is a record created for a stated purpose and never a standing permission.

Send subject_ref, reason and at least one entry in fields. You get the grant back with an id and the expires_at that ends it.

Nothing has to exist first. Use the subject_ref you already chose for this person.

Nothing else waits on this call.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Body

subject_ref string required

The opaque reference you chose for the person whose data you're about to view unmasked: the same subject_ref you sent when you recorded their consent at POST /v1/identity/consent_tokens and verified them. Not a name and not an identifier.

reason string required

Why you need to see the data unmasked, in your words. It's recorded before anything is shown and logged to each field, so write what a reviewer would need to read.

fields array of string required

The fields you'll view unmasked, by name, such as gross.amount. At least one, and each view is logged to the field, so name only what you need.

Returns

The access grant.

id string required

The record's identifier, returned by POST /v1/identity/access_grants when you create the grant. It never changes and no other call takes it, so keep it if you may need to cite this look later.

object always "access_grant" required

Always access_grant. Tells you which kind of record you are looking at, so one handler can read any response.

subject_ref string required

The opaque reference to the person whose data the grant covers, exactly as you sent it in subject_ref at POST /v1/identity/access_grants. Not a name and not an identifier.

reason string required

Stated before anything is seen, and logged to the field.

granted_to string required

Who the grant names, by their identifier: the one person recorded as looking. It covers that person alone and doesn't widen what your organisation may see.

fields array of string optional

The fields the grant covers, as you named them on the request, such as gross.amount. Each view is logged to the field.

expires_at string · date-time required

When the grant lapses, as an RFC 3339 timestamp in UTC. Grants are short lived; after this, state a reason again before viewing anything unmasked.

created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/access_grants" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","fields":["gross.amount"]}'
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityAccessGrantsCreate({
  idempotencyKey: crypto.randomUUID(),
  anchorAccessGrantCreateRequest: {"subjectRef":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","fields":["gross.amount"]},
});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/access_grants', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Content-Type': 'application/json',
    'Idempotency-Key': crypto.randomUUID(),
  },
  body: JSON.stringify({
    "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
    "reason": "The requester confirmed the work in person.",
    "fields": [
      "gross.amount"
    ]
  }),
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_access_grants_create(body={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "reason": "The requester confirmed the work in person.", "fields": ["gross.amount"]})
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/access_grants',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
    json={"subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "reason": "The requester confirmed the work in person.", "fields": ["gross.amount"]},
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityAccessGrantsCreate($idempotencyKey, json_decode('{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","fields":["gross.amount"]}', true));
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/access_grants');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
  CURLOPT_POSTFIELDS => '{"subject_ref":"sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z","reason":"The requester confirmed the work in person.","fields":["gross.amount"]}',
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityAccessGrantsCreate(idempotencyKey, body);
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/access_grants"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Content-Type", "application/json")
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.ofString("""
        {
          "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
          "reason": "The requester confirmed the work in person.",
          "fields": [
            "gross.amount"
          ]
        }
        """))
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityAccessGrantsCreate(idempotencyKey, body);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/access_grants");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
request.Content = new StringContent("""
    {
      "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "reason": "The requester confirmed the work in person.",
      "fields": [
        "gross.amount"
      ]
    }
    """, Encoding.UTF8, "application/json");
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityAccessGrantsCreate(ctx).IdempotencyKey(key).AnchorAccessGrantCreateRequest(body).Execute()
body := strings.NewReader(`{
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "fields": [
    "gross.amount"
  ]
}`)
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/access_grants", body)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "access_grant",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "reason": "The requester confirmed the work in person.",
  "granted_to": "example",
  "expires_at": "2026-09-01T09:00:00Z",
  "fields": [
    "gross.amount"
  ],
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/gating_contract#

Know what will refuse you before you build

identity.gating_contract.retrieve

The conditions other modules put on an identity are published here, module by module. So you can build each module's requirement into your own flow before PROOF, RAIL, MATCH or RUN refuses a subject with subject_not_anchored.

Send no parameters. You get one entry per module, naming the conditions it insists on and the lowest assurance it accepts for each, and the gates no attestation can lift.

If you cache the contract, read version before you rely on it again: when it moves, a requirement has moved with it. Nothing else waits on this call.

Returns

The gating contract.

object always "gating_contract" required

Always gating_contract. Tells you which kind of record you are looking at, so one handler can read any response.

version string required

Which edition of the contract you're reading, such as 2026.08.1. Build against one and compare it on each read: a new version means a requirement changed.

modules array of object required

One entry per module, stating what it requires of an identity before it will act: the conditions, and the lowest assurance it accepts for each.

2 fields
module string required

Which module these requirements are for: proof (credentials), rail (working relationships), flow (sourcing), match (work allocation), run (payroll), remit (statutory remittance) or route (payouts).

proofrailflowmatchrunremitroute
requires array of object required

What this module demands of an identity before it acts. Every condition listed must hold at its minimum_assurance or higher, or the module refuses.

3 fields
condition string required

The fact that must hold, in words, such as live unrevoked Passport.

minimum_assurance string required

How a fact was established. Recorded on the fact rather than in configuration, so an attested identity and a verified one stay distinguishable a year later, which is the distinction that matters when something is disputed.

attestedverified
contract_row string optional

The row of the contract this requirement is taken from, such as 6.1.13, so you can cite it. Absent when no row states it.

never_attestable array of string optional

Gates that hold in every configuration, whatever anyone has attested to.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/gating_contract" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityGatingContractRetrieve({});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/gating_contract', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_gating_contract_retrieve()
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/gating_contract',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityGatingContractRetrieve();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/gating_contract');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityGatingContractRetrieve();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/gating_contract"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityGatingContractRetrieve();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/gating_contract");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityGatingContractRetrieve(ctx).Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/gating_contract", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "gating_contract",
  "version": "2026.08.1",
  "modules": [
    {
      "module": "proof",
      "requires": [
        {
          "condition": "live unrevoked Passport",
          "minimum_assurance": "attested",
          "contract_row": "6.1.13"
        }
      ]
    }
  ],
  "never_attestable": [
    "example"
  ]
}
GET/v1/identity/readiness#

What ANCHOR needs before it verifies anybody

identity.readiness.retrieve

ANCHOR holds some requirements no attestation can lift, and it names them here whichever mode you run in. So before you write a line against identity you can tell which checks you will not get past by supplying the fact yourself.

Send no parameters. You get always_enforced, which names them, and mode, which tells you whether a required fact comes from the Heros module that owns it or from an attestation of your own.

Nothing else waits on this call.

Returns

The readiness report.

object always "readiness_report" required

Always readiness_report. Tells you which kind of record you are looking at, so one handler can read any response.

module string required

The module whose readiness endpoint you called, such as GET /v1/payroll/readiness: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance) or route (payouts).

anchorproofrailflowmatchrunremitroute
mode string required

integrated means a required fact comes from the Heros module that owns it; standalone means you supply it yourself under an attestation. The checks are the same in both, and what each fact is worth is recorded on its row as held.

integratedstandalone
ready boolean required

true when every row is satisfied and the module has what it needs from you. false when a required fact is missing or held at too low an assurance; rows says which.

rows array of ReadinessRow required

One row per fact the module requires: who owns it, the assurance it needs, what you hold and whether that satisfies it. The rows that are not satisfied are what to bring.

8 fields of ReadinessRow
fact string required

Named for what it is, not for who supplies it.

owner string required

The sibling module that owns this fact when running integrated.

contract_row string optional
required string required

How a fact was established. Recorded on the fact rather than in configuration, so an attested identity and a verified one stay distinguishable a year later, which is the distinction that matters when something is disputed.

attestedverified
held one of required
Assuranceor
source string · nullable optional
integratedstandalonenull
satisfied boolean required
missing_because string · nullable optional
not_suppliedassurance_too_lownull
always_enforced array of string optional

Gates that hold in every mode and cannot be attested away. Consent before any source is queried, bank account validation, the tax identifiers returns are filed under, and duplicate detection within the organisation's own population.

sources_without_credentials array of string optional

The sources you hold no credentials for, by name. No check that needs one of them can run until you supply them.

subjects_without_consent integer optional

How many subjects on file have no active consent covering the checks you use.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/readiness" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityReadinessRetrieve({});
const response = await fetch('https://sandbox.droomwork.io/v1/identity/readiness', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_readiness_retrieve()
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/readiness',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityReadinessRetrieve();
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/readiness');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityReadinessRetrieve();
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/readiness"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityReadinessRetrieve();
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/readiness");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityReadinessRetrieve(ctx).Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/readiness", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "readiness_report",
  "module": "anchor",
  "mode": "integrated",
  "ready": true,
  "rows": [],
  "always_enforced": [
    "consent_token",
    "bank_account_validation",
    "tax_identifier_validation",
    "duplicate_detection"
  ],
  "sources_without_credentials": [],
  "subjects_without_consent": 0
}
POST/v1/identity/verifications/{verification_id}/resolve#

Record that the subject was identified

identity.verifications.resolve

You have decided the subject is who they claimed to be, and this records it. A Passport appears when a verification resolves, and a Passport is what the other modules check before they act for you, so one call closes the check and the Passport it issued is listed at GET /v1/identity/passports.

Name the verification in the path and send no request body. You get the verification back with outcome now resolved.

The verification must exist, from POST /v1/identity/verifications, and must still be waiting on an outcome.

Resolving publishes verification.resolved. Read what each source said at GET /v1/identity/verifications/{verification_id}/evidence_bundle. A verification somebody has already decided is refused with conflict.

Path parameters

verification_id string required

The verification's identifier, starting with anchor_pro_verification_: the id returned by POST /v1/identity/verifications or listed by GET /v1/identity/verifications, or a batch result's verification_id.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The verification in its new state.

id string required

The verification's identifier, starting with anchor_pro_verification_. POST /v1/identity/verifications returns it and it never changes; pass it as verification_id wherever a call names this verification.

object always "identity_verification" required

Always identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
origin string required

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
subject_ref string required

Your opaque reference to the person being verified, exactly as you sent subject_ref at POST /v1/identity/verifications. Not a name and not an identifier.

consent_token_id string optional

The consent token this verification ran under, as you sent it at POST /v1/identity/verifications: the id starting with anchor_pro_consent_ that POST /v1/identity/consent_tokens returned for this subject, purpose and checks.

target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance_reached one of optional

The level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.

AssuranceLevelor
passport_id string · nullable optional

The id of the Passport this verification resolved to, starting with anchor_pro_passport_: what every module checks before it acts. Read it at GET /v1/identity/passports/{passport_id}; null until outcome is resolved.

evidence_bundle_id string · nullable optional

The id of the bundle listing every source consulted and what each said. Read it at GET /v1/identity/verifications/{verification_id}/evidence_bundle using this verification's id; null while there is nothing to show yet.

unresolved_source string · nullable optional

Named when the outcome is unresolved. Silence always names its source.

retry_after string · date-time · nullable optional

When to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.

blocked_because string · nullable optional

Why outcome is blocked: no_consent (no active token), consent_scope_insufficient (the token doesn't cover what was asked) or subject_blocklisted. The subject is entitled to know which; null for any other outcome.

no_consentconsent_scope_insufficientsubject_blocklistednull
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/resolve" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityVerificationsResolve({ verificationId: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/resolve', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Idempotency-Key': crypto.randomUUID(),
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_verifications_resolve(verification_id='anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/resolve',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityVerificationsResolve(verification_id: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/resolve');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityVerificationsResolve("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/resolve"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityVerificationsResolve(verificationId: "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/resolve");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityVerificationsResolve(ctx, "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/resolve", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "identity_verification",
  "livemode": false,
  "mocked": true,
  "outcome": "resolved",
  "origin": "partner_api",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "target_assurance": "dal_0",
  "assurance_reached": "dal_0",
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "evidence_bundle_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "unresolved_source": "example",
  "retry_after": "2026-09-01T09:00:00Z",
  "blocked_because": "no_consent",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/verifications/{verification_id}/mark_unresolved#

Close a check a source could not answer

identity.verifications.mark_unresolved

This is the outcome for a check that cannot be decided, because a source did not answer. No Passport is issued, so your own system can keep the subject in its queue and come back to them later instead of turning them away.

Name the verification in the path and send no request body. You get the verification back with outcome now unresolved.

The verification must exist, from POST /v1/identity/verifications, and must still be waiting on an outcome.

Marking a verification unresolved publishes verification.unresolved. Trying the subject again means a fresh POST /v1/identity/verifications, and GET /v1/identity/sources tells you which sources are answering right now. A verification somebody has already decided is refused with conflict.

Path parameters

verification_id string required

The verification's identifier, starting with anchor_pro_verification_: the id returned by POST /v1/identity/verifications or listed by GET /v1/identity/verifications, or a batch result's verification_id.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The verification in its new state.

id string required

The verification's identifier, starting with anchor_pro_verification_. POST /v1/identity/verifications returns it and it never changes; pass it as verification_id wherever a call names this verification.

object always "identity_verification" required

Always identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
origin string required

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
subject_ref string required

Your opaque reference to the person being verified, exactly as you sent subject_ref at POST /v1/identity/verifications. Not a name and not an identifier.

consent_token_id string optional

The consent token this verification ran under, as you sent it at POST /v1/identity/verifications: the id starting with anchor_pro_consent_ that POST /v1/identity/consent_tokens returned for this subject, purpose and checks.

target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance_reached one of optional

The level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.

AssuranceLevelor
passport_id string · nullable optional

The id of the Passport this verification resolved to, starting with anchor_pro_passport_: what every module checks before it acts. Read it at GET /v1/identity/passports/{passport_id}; null until outcome is resolved.

evidence_bundle_id string · nullable optional

The id of the bundle listing every source consulted and what each said. Read it at GET /v1/identity/verifications/{verification_id}/evidence_bundle using this verification's id; null while there is nothing to show yet.

unresolved_source string · nullable optional

Named when the outcome is unresolved. Silence always names its source.

retry_after string · date-time · nullable optional

When to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.

blocked_because string · nullable optional

Why outcome is blocked: no_consent (no active token), consent_scope_insufficient (the token doesn't cover what was asked) or subject_blocklisted. The subject is entitled to know which; null for any other outcome.

no_consentconsent_scope_insufficientsubject_blocklistednull
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/mark_unresolved" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityVerificationsMarkUnresolved({ verificationId: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/mark_unresolved', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Idempotency-Key': crypto.randomUUID(),
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_verifications_mark_unresolved(verification_id='anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/mark_unresolved',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityVerificationsMarkUnresolved(verification_id: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/mark_unresolved');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityVerificationsMarkUnresolved("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/mark_unresolved"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityVerificationsMarkUnresolved(verificationId: "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/mark_unresolved");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityVerificationsMarkUnresolved(ctx, "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/mark_unresolved", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "identity_verification",
  "livemode": false,
  "mocked": true,
  "outcome": "unresolved",
  "origin": "partner_api",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "target_assurance": "dal_0",
  "assurance_reached": "dal_0",
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "evidence_bundle_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "unresolved_source": "example",
  "retry_after": "2026-09-01T09:00:00Z",
  "blocked_because": "no_consent",
  "created_at": "2026-09-01T09:00:00Z"
}
POST/v1/identity/verifications/{verification_id}/block#

Stop a check the blocklist or consent forbids

identity.verifications.block

You are closing a check that must not run, because the subject is blocklisted or the consent for it is missing or too narrow. Recording that as blocked keeps it apart from a check a source could not answer, which is the difference between a rule to satisfy and a source to wait for.

Name the verification in the path and send no request body. You get the verification back with outcome now blocked.

The verification must exist, from POST /v1/identity/verifications, and must still be waiting on an outcome.

Blocking publishes no event, where resolving and marking unresolved each publish one. Where consent was the bar, collect a token covering the purpose and checks at POST /v1/identity/consent_tokens, then open a fresh POST /v1/identity/verifications. A verification somebody has already decided is refused with conflict.

Path parameters

verification_id string required

The verification's identifier, starting with anchor_pro_verification_: the id returned by POST /v1/identity/verifications or listed by GET /v1/identity/verifications, or a batch result's verification_id.

Headers

Idempotency-Key string required

A key you make up for this request, such as a UUID, up to 255 characters. Sending the same key with the same body returns the original response, marked Droomwork-Idempotent-Replay: true; sending it with a different body is refused with idempotency_key_reused.

Returns

The verification in its new state.

id string required

The verification's identifier, starting with anchor_pro_verification_. POST /v1/identity/verifications returns it and it never changes; pass it as verification_id wherever a call names this verification.

object always "identity_verification" required

Always identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm this record is in: false is the sandbox, true is live. Read it before you act on anything.

mocked boolean required

Where these figures came from: true when they were mocked, false when they were computed for real. Not the inverse of livemode: each record carries the answer that was true for it.

outcome string required

unresolved is a real outcome and never a rejection. A source that did not answer proves nothing either way. Don't treat silence as a pass.

pendingresolvedunresolvedblocked
origin string required

How the claim arrived. The behaviour is the same whichever way it came in.

partner_apiself_serveassisted_enrolment
subject_ref string required

Your opaque reference to the person being verified, exactly as you sent subject_ref at POST /v1/identity/verifications. Not a name and not an identifier.

consent_token_id string optional

The consent token this verification ran under, as you sent it at POST /v1/identity/verifications: the id starting with anchor_pro_consent_ that POST /v1/identity/consent_tokens returned for this subject, purpose and checks.

target_assurance string optional

What was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.

dal_0dal_1dal_2dal_3dal_4
assurance_reached one of optional

The level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.

AssuranceLevelor
passport_id string · nullable optional

The id of the Passport this verification resolved to, starting with anchor_pro_passport_: what every module checks before it acts. Read it at GET /v1/identity/passports/{passport_id}; null until outcome is resolved.

evidence_bundle_id string · nullable optional

The id of the bundle listing every source consulted and what each said. Read it at GET /v1/identity/verifications/{verification_id}/evidence_bundle using this verification's id; null while there is nothing to show yet.

unresolved_source string · nullable optional

Named when the outcome is unresolved. Silence always names its source.

retry_after string · date-time · nullable optional

When to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.

blocked_because string · nullable optional

Why outcome is blocked: no_consent (no active token), consent_scope_insufficient (the token doesn't cover what was asked) or subject_blocklisted. The subject is entitled to know which; null for any other outcome.

no_consentconsent_scope_insufficientsubject_blocklistednull
created_at string · date-time optional

When the record was created, as an RFC 3339 timestamp in UTC.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.
409The record is not in a state that allows this.
422A required fact is missing. Call the readiness endpoint to see what.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X POST "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/block" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityVerificationsBlock({ verificationId: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/block', {
  method: 'POST',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
    'Idempotency-Key': crypto.randomUUID(),
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_verifications_block(verification_id='anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z')
import os
import uuid

import requests

response = requests.request(
    'POST',
    'https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/block',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
        'Idempotency-Key': str(uuid.uuid4()),
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityVerificationsBlock(verification_id: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/block');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
    'Idempotency-Key: ' . bin2hex(random_bytes(16)),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityVerificationsBlock("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/block"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .header("Idempotency-Key", UUID.randomUUID().toString())
    .method("POST", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityVerificationsBlock(verificationId: "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/block");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityVerificationsBlock(ctx, "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Execute()
req, _ := http.NewRequest("POST", "https://sandbox.droomwork.io/v1/identity/verifications/anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z/block", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "identity_verification",
  "livemode": false,
  "mocked": true,
  "outcome": "blocked",
  "origin": "partner_api",
  "subject_ref": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "consent_token_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "target_assurance": "dal_0",
  "assurance_reached": "dal_0",
  "passport_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "evidence_bundle_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "unresolved_source": "example",
  "retry_after": "2026-09-01T09:00:00Z",
  "blocked_because": "no_consent",
  "created_at": "2026-09-01T09:00:00Z"
}
GET/v1/identity/events#

Replay what happened to an identity record

identity.events.list

ANCHOR records what happens to the records it holds, and you read them back here. Every event carries the record as it stood at that moment, so your product can show how an identity record changed, not only where it stands now.

You get events oldest first, each under an evt_ identifier and carrying the sequence it holds in its own record's history.

after counts inside one stream, so it needs stream beside it. Across records you get the order ANCHOR recorded events in, which is not always occurred_at order.

An event says what happened to a record and never who did it. GET /v1/identity/audit_entries is who, matched on request_id.

Open one at GET /v1/identity/events/{event_id}. Until something has happened in ANCHOR, this comes back empty and not refused.

Query parameters

stream string optional

The id of the record whose events you want, such as a verification's (anchor_pro_verification_…) from POST /v1/identity/verifications; a Passport, consent token or disclosure works the same. Leave it out to get events across every record.

after integer optional

The sequence of the last event you handled on that stream, read off an event in this list or a webhook delivery; you get the events after it. 0 reads from the start, and it needs stream beside it.

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

Returns

A page of events.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of StoredEvent required

The records on this page, in the order the list promises. Empty when nothing matched.

13 fields of StoredEvent
id string required

The event's identifier, starting with evt_, the same on a webhook delivery and on the module's events list, such as GET /v1/payroll/events. It never changes: a redelivery carries the same id, so you can recognise an event you have already handled.

type string required

What happened, as module.resource.past_tense_verb, for example run.payslip.calculated. Pick your handler on it; data takes the shape this type promises.

schema_version integer · minimum 1 required

The version of the shape data takes for this type, starting at 1. A change to the shape raises it, so check it before you read data.

org_id string required

The organisation the event belongs to, by its id, which starts with org_: the one POST /v1/registrations gave you and GET /v1/me returns. You only ever receive events for your own organisation.

sequence integer · minimum 0 required

Per organisation and per stream. It is how a consumer tells a replay from a new event, and it is what the delivery guarantee rests on.

occurred_at string · date-time required

When the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.

request_id string optional

The request that caused this event, where one did: the Droomwork-Request-Id that request returned, starting with req_. Absent for an event a schedule raised, such as an engagement lapsing on its end date.

livemode boolean required

Which realm the event happened in. False is the sandbox.

mocked boolean required

Whether a mock produced this fact, rather than an engine computing it. Recorded on the event when it was appended and never worked out afterwards from the realm: the two answers agree while every module is on its mock and part on the day the first engine ships. See ADR-0011.

source string required

Which part of Heros is the authority for this fact: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance), route (payouts), gateway (the API's front door), iam (accounts and API keys), ledger (the books), registry (rule packs), delivery (webhooks and messages), documents (rendered payslips and instruments) or intelligence (AI decisions). Read the fact from there when it matters; your own copy is never the authority.

anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligence
object always "event" required

Always event. Tells you which kind of record you are looking at, so one handler can read any response.

stream string required

The id of the record this event is about, such as a verification's anchor_pro_verification_…. sequence counts per organisation and per stream, so it means nothing without this; pass it as stream to GET /v1/identity/events to replay.

data object required

The record the event is about, as it stood when the event happened. Its shape follows type: a verification event carries the verification, a Passport event the Passport.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/events?stream=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
const result = await api.identityEventsList({ stream: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', after: 0, limit: 25 });
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/events?stream=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
result = api.identity_events_list(stream='anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', after=0, limit=25)
import os

import requests

# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/events?stream=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
$result = $api->identityEventsList(stream: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', after: 0, limit: 25);
<?php
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/events?stream=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var result = api.identityEventsList("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", 0, 25, null);
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/events?stream=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
var result = api.IdentityEventsList(stream: "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", after: 0, limit: 25);
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/events?stream=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
result, _, err := client.ANCHORAPI.IdentityEventsList(ctx).Stream("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").After(0).Limit(25).Execute()
// query parameters: stream (optional), after (optional), limit (optional), starting_after (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/events?stream=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&after=0&limit=25", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "type": "run.payslip.calculated",
      "schema_version": 1,
      "org_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "sequence": 0,
      "occurred_at": "2026-09-01T09:00:00Z",
      "livemode": false,
      "mocked": true,
      "source": "anchor",
      "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "event",
      "stream": "example",
      "data": {}
    }
  ],
  "has_more": true
}
GET/v1/identity/events/{event_id}#

Open one event you kept the identifier for

identity.events.retrieve

An event identifier on its own becomes the whole record here: what happened, which identity record it happened to, and that record's state at the time. So an identifier kept in your own log is enough to show the same event again later.

Send the event's id in the path. You get the whole event: the stream it belongs to, its sequence in that stream, and data, the record the event is about.

Carry stream and sequence to GET /v1/identity/events as stream and after to read what followed. An identifier you hold no record for is refused.

Path parameters

event_id string required

The event's id, as GET /v1/identity/events lists it or as a webhook delivery carries it. It starts with evt_.

Returns

The event.

id string required

The event's identifier, starting with evt_, the same on a webhook delivery and on the module's events list, such as GET /v1/payroll/events. It never changes: a redelivery carries the same id, so you can recognise an event you have already handled.

type string required

What happened, as module.resource.past_tense_verb, for example run.payslip.calculated. Pick your handler on it; data takes the shape this type promises.

schema_version integer · minimum 1 required

The version of the shape data takes for this type, starting at 1. A change to the shape raises it, so check it before you read data.

org_id string required

The organisation the event belongs to, by its id, which starts with org_: the one POST /v1/registrations gave you and GET /v1/me returns. You only ever receive events for your own organisation.

sequence integer · minimum 0 required

Per organisation and per stream. It is how a consumer tells a replay from a new event, and it is what the delivery guarantee rests on.

occurred_at string · date-time required

When the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.

request_id string optional

The request that caused this event, where one did: the Droomwork-Request-Id that request returned, starting with req_. Absent for an event a schedule raised, such as an engagement lapsing on its end date.

livemode boolean required

Which realm the event happened in. False is the sandbox.

mocked boolean required

Whether a mock produced this fact, rather than an engine computing it. Recorded on the event when it was appended and never worked out afterwards from the realm: the two answers agree while every module is on its mock and part on the day the first engine ships. See ADR-0011.

source string required

Which part of Heros is the authority for this fact: anchor (identity), proof (credentials), rail (engagements), flow (sourcing), match (allocation), run (payroll), remit (remittance), route (payouts), gateway (the API's front door), iam (accounts and API keys), ledger (the books), registry (rule packs), delivery (webhooks and messages), documents (rendered payslips and instruments) or intelligence (AI decisions). Read the fact from there when it matters; your own copy is never the authority.

anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligence
object always "event" required

Always event. Tells you which kind of record you are looking at, so one handler can read any response.

stream string required

The id of the record this event is about, such as a verification's anchor_pro_verification_…. sequence counts per organisation and per stream, so it means nothing without this; pass it as stream to GET /v1/identity/events to replay.

data object required

The record the event is about, as it stood when the event happened. Its shape follows type: a verification event carries the verification, a Passport event the Passport.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityEventsRetrieve({ eventId: '{event_id}' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_events_retrieve(event_id='{event_id}')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityEventsRetrieve(event_id: '{event_id}');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityEventsRetrieve("{event_id}");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityEventsRetrieve(eventId: "{event_id}");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityEventsRetrieve(ctx, "{event_id}").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "evt_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "type": "run.payslip.calculated",
  "schema_version": 1,
  "org_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "sequence": 0,
  "occurred_at": "2026-09-01T09:00:00Z",
  "livemode": false,
  "mocked": true,
  "source": "anchor",
  "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "event",
  "stream": "example",
  "data": {}
}
GET/v1/identity/audit_entries#

Who acted on your identity records

identity.audit_entries.list

This is who did what in ANCHOR, and how each attempt ended. Every attempt that changed something is recorded, and so is every one that was refused under a credential we recognised, so your product can answer who ran a verification or who revoked a disclosure.

You get a page of entries, newest first. Narrow to one action, one credential, one record, one outcome, or a window of time.

Your own successful reads leave no entry here. A Heros staff member's read is recorded, so you can see what we opened. Nothing refused with unauthenticated is recorded, so a revoked credential's calls are not here.

Read one in full at GET /v1/identity/audit_entries/{audit_entry_id}.

Query parameters

action string optional

Only attempts at one action, matched exactly on action as it reads on each entry: the method and route pattern, such as POST /v1/identity/verifications. Leave it out to get every action.

actor_id string optional

Only one credential's attempts, matched exactly on actor_id as it reads on each entry: your API key's id (key_…), your OAuth client_id or a staff member's. Leave it out for every actor; add outcome=refused to watch one key.

resource string optional

Only attempts on one kind of record, matched exactly on resource as it reads on each entry: the collection segment of the route, such as verifications or passports. Leave it out to get every kind.

resource_id string optional

Only attempts on one record, matched exactly on resource_id as it reads on each entry: the record's id as it appeared in the route, such as a verification's anchor_pro_verification_…. Pair it with resource; leave it out to get every record.

outcome string optional

Only entries with one outcome: succeeded (it went through), refused (turned away with a 4xx status) or failed (it broke on our side with a 5xx). Leave it out to get all three.

succeededrefusedfailed
recorded_after string optional

Only entries whose at is after this moment, as an RFC 3339 timestamp in UTC. Exclusive: an entry at exactly this instant is left out; leave it out to start from the oldest entry.

recorded_before string optional

Only entries whose at is before this moment, as an RFC 3339 timestamp in UTC; exclusive, like recorded_after. Pair the two to bound a window; leave it out to run to the newest entry.

limit integer optional

How many records to return on one page, from 1 to 100, and 25 if you leave it out. When has_more is true, pass the last record's id as starting_after to get the next page.

starting_after string optional

The id of the last record on the previous page of this same list. Leave it out to get the first page; when a page comes back with has_more true, send its last record's id here to get the page after it.

Returns

A page of audit entries.

object always "list" required

Always list. Tells you which kind of record you are looking at, so one handler can read any response.

data array of AuditEntry required

The records on this page, in the order the list promises. Empty when nothing matched.

13 fields of AuditEntry
id string required

The record's identifier, as GET /v1/identity/audit_entries lists it. It starts with aud_ and never changes; pass it as audit_entry_id to GET /v1/identity/audit_entries/{audit_entry_id}.

object always "audit_entry" required

Always audit_entry. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm the action happened in. False is the sandbox.

mocked boolean required

Whether the route this attempt was aimed at is served by a mock. It describes the route, not the answer: a refused request and a replayed idempotent request against a mocked route both say true.

at string · date-time required

When the attempt was made, as an RFC 3339 timestamp in UTC. The list orders entries newest first by it.

request_id string required

The request that made this attempt. It's the same request_id an error response carries, so a refusal you were shown can be matched to its entry here.

actor_type string required

What kind of credential acted: client for one of your API keys or OAuth clients, staff for a Heros staff member acting under a grant. actor_id says which one.

actor_id string required

The id of the credential that made the attempt: your API key's (key_…) or the client_id you send to POST /v1/oauth/token when actor_type is client, a staff member's when it is staff. Filter the list on it to follow one.

action string required

What was attempted, as the method and the route pattern.

resource string · nullable optional

The kind of record acted on, as the collection name in the route, such as verifications. null when the route named none.

resource_id string · nullable optional

The id of the record the attempt named in its path, exactly as it appeared there, such as a Passport's anchor_pro_passport_…. null when the attempt was against a collection rather than one record.

outcome string required

How the attempt ended: succeeded means it went through, refused that it was turned away with a 4xx status, failed that it broke on our side with a 5xx. Filter the list on it with outcome.

succeededrefusedfailed
status integer required

The HTTP status code the attempt was answered with, such as 201 or 403. outcome groups it into three words; this is the exact number.

has_more boolean required

true when there are more records after this page. Pass the last record's id as starting_after to get the next page.

Other responses

400The request could not be read, or a value was refused.
401No valid credential was presented.
403The credential does not carry the required scope.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
curl -X GET "https://sandbox.droomwork.io/v1/identity/audit_entries?action=POST%20%2Fv1%2Fidentity%2Fverifications&actor_id=key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=verifications&resource_id=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
const result = await api.identityAuditEntriesList({ action: 'POST /v1/identity/verifications', actorId: 'key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource: 'verifications', resourceId: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome: 'succeeded', recordedAfter: '2026-01-01T00:00:00Z', recordedBefore: '2027-01-01T00:00:00Z', limit: 25 });
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
const response = await fetch('https://sandbox.droomwork.io/v1/identity/audit_entries?action=POST%20%2Fv1%2Fidentity%2Fverifications&actor_id=key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=verifications&resource_id=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
result = api.identity_audit_entries_list(action='POST /v1/identity/verifications', actor_id='key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource='verifications', resource_id='anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome='succeeded', recorded_after='2026-01-01T00:00:00Z', recorded_before='2027-01-01T00:00:00Z', limit=25)
import os

import requests

# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/audit_entries?action=POST%20%2Fv1%2Fidentity%2Fverifications&actor_id=key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=verifications&resource_id=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

# query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
$result = $api->identityAuditEntriesList(action: 'POST /v1/identity/verifications', actor_id: 'key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', resource: 'verifications', resource_id: 'anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z', outcome: 'succeeded', recorded_after: '2026-01-01T00:00:00Z', recorded_before: '2027-01-01T00:00:00Z', limit: 25);
<?php
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/audit_entries?action=POST%20%2Fv1%2Fidentity%2Fverifications&actor_id=key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=verifications&resource_id=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
var result = api.identityAuditEntriesList("POST /v1/identity/verifications", "key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "verifications", "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", "succeeded", "2026-01-01T00:00:00Z", "2027-01-01T00:00:00Z", 25, null);
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/audit_entries?action=POST%20%2Fv1%2Fidentity%2Fverifications&actor_id=key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=verifications&resource_id=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
var result = api.IdentityAuditEntriesList(action: "POST /v1/identity/verifications", actorId: "key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", resource: "verifications", resourceId: "anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z", outcome: "succeeded", recordedAfter: "2026-01-01T00:00:00Z", recordedBefore: "2027-01-01T00:00:00Z", limit: 25);
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/audit_entries?action=POST%20%2Fv1%2Fidentity%2Fverifications&actor_id=key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=verifications&resource_id=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
result, _, err := client.ANCHORAPI.IdentityAuditEntriesList(ctx).Action("POST /v1/identity/verifications").ActorId("key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Resource("verifications").ResourceId("anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z").Outcome("succeeded").RecordedAfter("2026-01-01T00:00:00Z").RecordedBefore("2027-01-01T00:00:00Z").Limit(25).Execute()
// query parameters: action (optional), actor_id (optional), resource (optional), resource_id (optional), outcome (optional), recorded_after (optional), recorded_before (optional), limit (optional), starting_after (optional)
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/audit_entries?action=POST%20%2Fv1%2Fidentity%2Fverifications&actor_id=key_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&resource=verifications&resource_id=anchor_pro_verification_01J8XQ4M7K2N9P3R5T7V9W1Y3Z&outcome=succeeded&recorded_after=2026-01-01T00%3A00%3A00Z&recorded_before=2027-01-01T00%3A00%3A00Z&limit=25", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "object": "list",
  "data": [
    {
      "id": "aud_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "object": "audit_entry",
      "livemode": false,
      "mocked": true,
      "at": "2026-09-01T09:00:00Z",
      "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "actor_type": "example",
      "actor_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
      "action": "example",
      "outcome": "succeeded",
      "status": 1,
      "resource": "verifications",
      "resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
    }
  ],
  "has_more": true
}
GET/v1/identity/audit_entries/{audit_entry_id}#

Read back one attempt in full

identity.audit_entries.retrieve

An audit entry is one attempt to change something, or one that was refused, and this reads one back. It names the exact status the caller was given, so an audit screen in your product can tell a refusal from a failure on our side instead of calling both denied.

Send the entry's id in the path. You get when the attempt was made, which credential made it, what it aimed at, and the status it was answered with.

An entry never says what changed, or the reason behind a refusal. For what happened to the record itself, read GET /v1/identity/events.

Where resource_id is set, open that record, such as a verification at GET /v1/identity/verifications/{verification_id}. An id you hold no record for is refused.

Path parameters

audit_entry_id string required

The entry's id, as GET /v1/identity/audit_entries lists it. It starts with aud_.

Returns

The audit entry.

id string required

The record's identifier, as GET /v1/identity/audit_entries lists it. It starts with aud_ and never changes; pass it as audit_entry_id to GET /v1/identity/audit_entries/{audit_entry_id}.

object always "audit_entry" required

Always audit_entry. Tells you which kind of record you are looking at, so one handler can read any response.

livemode boolean required

Which realm the action happened in. False is the sandbox.

mocked boolean required

Whether the route this attempt was aimed at is served by a mock. It describes the route, not the answer: a refused request and a replayed idempotent request against a mocked route both say true.

at string · date-time required

When the attempt was made, as an RFC 3339 timestamp in UTC. The list orders entries newest first by it.

request_id string required

The request that made this attempt. It's the same request_id an error response carries, so a refusal you were shown can be matched to its entry here.

actor_type string required

What kind of credential acted: client for one of your API keys or OAuth clients, staff for a Heros staff member acting under a grant. actor_id says which one.

actor_id string required

The id of the credential that made the attempt: your API key's (key_…) or the client_id you send to POST /v1/oauth/token when actor_type is client, a staff member's when it is staff. Filter the list on it to follow one.

action string required

What was attempted, as the method and the route pattern.

resource string · nullable optional

The kind of record acted on, as the collection name in the route, such as verifications. null when the route named none.

resource_id string · nullable optional

The id of the record the attempt named in its path, exactly as it appeared there, such as a Passport's anchor_pro_passport_…. null when the attempt was against a collection rather than one record.

outcome string required

How the attempt ended: succeeded means it went through, refused that it was turned away with a 4xx status, failed that it broke on our side with a 5xx. Filter the list on it with outcome.

succeededrefusedfailed
status integer required

The HTTP status code the attempt was answered with, such as 201 or 403. outcome groups it into three words; this is the exact number.

Other responses

401No valid credential was presented.
403The credential does not carry the required scope.
404No record with that identifier.

Errors it can return

Sends against https://sandbox.droomwork.io
Request
which?
curl -X GET "https://sandbox.droomwork.io/v1/identity/audit_entries/%7Baudit_entry_id%7D" \
  -H "Droomwork-Api-Key: $DROOMWORK_API_KEY"
import { Configuration, ANCHORApi } from '@heros/sdk';

const api = new ANCHORApi(new Configuration({ basePath: 'https://sandbox.droomwork.io', accessToken: process.env.DROOMWORK_API_KEY }));

const result = await api.identityAuditEntriesRetrieve({ auditEntryId: '{audit_entry_id}' });
const response = await fetch('https://sandbox.droomwork.io/v1/identity/audit_entries/%7Baudit_entry_id%7D', {
  method: 'GET',
  headers: {
    'Droomwork-Api-Key': process.env.DROOMWORK_API_KEY,
  },
});
const result = await response.json();
import os

import heros_sdk

config = heros_sdk.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = heros_sdk.ApiClient(config)
api = heros_sdk.ANCHORApi(client)

result = api.identity_audit_entries_retrieve(audit_entry_id='{audit_entry_id}')
import os

import requests

response = requests.request(
    'GET',
    'https://sandbox.droomwork.io/v1/identity/audit_entries/%7Baudit_entry_id%7D',
    headers={
        'Droomwork-Api-Key': os.environ['DROOMWORK_API_KEY'],
    },
)
result = response.json()
<?php
require_once __DIR__ . '/vendor/autoload.php';

$config = HerosSdk\Configuration::getDefaultConfiguration()
  ->setHost('https://sandbox.droomwork.io')
  ->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new HerosSdk\Api\ANCHORApi(new GuzzleHttp\Client(), $config);

$result = $api->identityAuditEntriesRetrieve(audit_entry_id: '{audit_entry_id}');
<?php
$ch = curl_init('https://sandbox.droomwork.io/v1/identity/audit_entries/%7Baudit_entry_id%7D');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => [
    'Droomwork-Api-Key: ' . getenv('DROOMWORK_API_KEY'),
  ],
]);
$result = json_decode(curl_exec($ch), true);
import com.useheros.sdk.ApiClient;
import com.useheros.sdk.Configuration;
import com.useheros.sdk.api.AnchorApi;

ApiClient client = Configuration.getDefaultApiClient();
client.setBasePath("https://sandbox.droomwork.io");
client.setAccessToken(System.getenv("DROOMWORK_API_KEY"));
AnchorApi api = new AnchorApi(client);

var result = api.identityAuditEntriesRetrieve("{audit_entry_id}");
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://sandbox.droomwork.io/v1/identity/audit_entries/%7Baudit_entry_id%7D"))
    .header("Droomwork-Api-Key", System.getenv("DROOMWORK_API_KEY"))
    .method("GET", HttpRequest.BodyPublishers.noBody())
    .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
String result = response.body();
using Heros.Sdk.Api;
using Heros.Sdk.Client;

var config = new Configuration { BasePath = "https://sandbox.droomwork.io" };
config.AccessToken = Environment.GetEnvironmentVariable("DROOMWORK_API_KEY");
var api = new ANCHORApi(config);

var result = api.IdentityAuditEntriesRetrieve(auditEntryId: "{audit_entry_id}");
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://sandbox.droomwork.io/v1/identity/audit_entries/%7Baudit_entry_id%7D");
request.Headers.Add("Droomwork-Api-Key", Environment.GetEnvironmentVariable("DROOMWORK_API_KEY"));
var response = await client.SendAsync(request);
var result = await response.Content.ReadAsStringAsync();
import heros "github.com/fenibofubara/heros-sdk-go"

ctx := context.WithValue(context.Background(), heros.ContextAPIKeys, map[string]heros.APIKey{
	"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := heros.NewConfiguration()
cfg.Servers = heros.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := heros.NewAPIClient(cfg)

result, _, err := client.ANCHORAPI.IdentityAuditEntriesRetrieve(ctx, "{audit_entry_id}").Execute()
req, _ := http.NewRequest("GET", "https://sandbox.droomwork.io/v1/identity/audit_entries/%7Baudit_entry_id%7D", nil)
	req.Header.Set("Droomwork-Api-Key", os.Getenv("DROOMWORK_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
	return err
}
defer res.Body.Close()
result, _ := io.ReadAll(res.Body)
Response
{
  "id": "aud_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "object": "audit_entry",
  "livemode": false,
  "mocked": true,
  "at": "2026-09-01T09:00:00Z",
  "request_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "actor_type": "example",
  "actor_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z",
  "action": "example",
  "outcome": "succeeded",
  "status": 1,
  "resource": "verifications",
  "resource_id": "sub_01J8XQ4M7K2N9P3R5T7V9W1Y3Z"
}