Version 1.0.0
Droomwork ANCHOR PRO
Identity resolution for Nigeria. One real person, not several accounts.
https://sandbox.droomwork.ioDroomwork-Api-Key: dw_test_… or Authorization: Bearer dw_test_…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.
| Level | Means |
|---|---|
| DAL-0 | Claimed. Someone said so |
| DAL-1 | Contactable. A channel was proved |
| DAL-2 | Matched against a government identifier on name and date of birth |
| DAL-3 | Biometric binding. Not in this release |
| DAL-4 | Adverse 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?
/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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of ConsentToken
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
12 fields of ConsentToken
id
string
requiredThe 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"
requiredAlways consent_token. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredYour 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
requiredWhat 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
requiredThe 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
requiredWhether 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.
activewithdrawnexpiredwording_version
string
optionalThe exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.
collected_channel
string
optionalHow 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_serveassistedwhatsappcollected_at
string · date-time
optionalWhen the subject gave this consent, as an RFC 3339 timestamp in UTC.
expires_at
string · date-time
requiredWhen 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
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/identity/consent_tokens" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, ANCHORApi } from '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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
}
/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
requiredA 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
requiredYour 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
requiredWhat 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
requiredThe 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
requiredThe 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
optionalHow 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_serveassistedwhatsappexpires_at
string · date-time
optionalWhen 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
requiredThe 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"
requiredAlways consent_token. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredYour 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
requiredWhat 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
requiredThe 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
requiredWhether 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.
activewithdrawnexpiredwording_version
string
optionalThe exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.
collected_channel
string
optionalHow 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_serveassistedwhatsappcollected_at
string · date-time
optionalWhen the subject gave this consent, as an RFC 3339 timestamp in UTC.
expires_at
string · date-time
requiredWhen 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
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways consent_token. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredYour 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
requiredWhat 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
requiredThe 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
requiredWhether 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.
activewithdrawnexpiredwording_version
string
optionalThe exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.
collected_channel
string
optionalHow 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_serveassistedwhatsappcollected_at
string · date-time
optionalWhen the subject gave this consent, as an RFC 3339 timestamp in UTC.
expires_at
string · date-time
requiredWhen 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
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredA 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
requiredThe 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"
requiredAlways consent_token. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredYour 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
requiredWhat 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
requiredThe 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
requiredWhether 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.
activewithdrawnexpiredwording_version
string
optionalThe exact wording the subject saw. What someone agreed to is answered from the record, not from whatever the current page says.
collected_channel
string
optionalHow 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_serveassistedwhatsappcollected_at
string · date-time
optionalWhen the subject gave this consent, as an RFC 3339 timestamp in UTC.
expires_at
string · date-time
requiredWhen 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
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
optionalHow 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
optionalThe 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
optionalReturn 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.
pendingresolvedunresolvedblockedReturns
A page of verifications.
object
always "list"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of Verification
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
16 fields of Verification
id
string
requiredThe 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"
requiredAlways identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredunresolved 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.
pendingresolvedunresolvedblockedorigin
string
requiredHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmentsubject_ref
string
requiredYour 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
optionalThe 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
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance_reached
one of
optionalThe level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.
AssuranceLevelorpassport_id
string · nullable
optionalThe 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
optionalThe 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
optionalNamed when the outcome is unresolved. Silence always names its source.
retry_after
string · date-time · nullable
optionalWhen to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.
blocked_because
string · nullable
optionalWhy 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_blocklistednullcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.AnchorApi;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)
// query parameters: limit (optional), starting_after (optional), outcome (optional)
result, _, err := client.ANCHORAPI.IdentityVerificationsList(ctx).Limit(25).Outcome(droomwork.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)
{
"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
}
/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
requiredA 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
requiredYour 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
requiredThe 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
optionalHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmenttarget_assurance
string
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4claims
object
requiredWhat the person says about themselves. Identifiers you submit here never come back in full.
5 fields
full_name
string
optionalThe 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
optionalThe 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
optionalThe 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
optionalWrite only. Accepted here, never returned anywhere.
bank_identifier
string · write only
optionalWrite 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
requiredThe 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"
requiredAlways identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredunresolved 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.
pendingresolvedunresolvedblockedorigin
string
requiredHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmentsubject_ref
string
requiredYour 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
optionalThe 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
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance_reached
one of
optionalThe level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.
AssuranceLevelorpassport_id
string · nullable
optionalThe 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
optionalThe 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
optionalNamed when the outcome is unresolved. Silence always names its source.
retry_after
string · date-time · nullable
optionalWhen to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.
blocked_because
string · nullable
optionalWhy 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_blocklistednullcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredunresolved 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.
pendingresolvedunresolvedblockedorigin
string
requiredHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmentsubject_ref
string
requiredYour 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
optionalThe 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
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance_reached
one of
optionalThe level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.
AssuranceLevelorpassport_id
string · nullable
optionalThe 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
optionalThe 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
optionalNamed when the outcome is unresolved. Silence always names its source.
retry_after
string · date-time · nullable
optionalWhen to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.
blocked_because
string · nullable
optionalWhy 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_blocklistednullcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways evidence_bundle. Tells you which kind of record you are looking at, so one handler can read any response.
verification_id
string
requiredThe 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
requiredOne 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
requiredWhich source was consulted, for example national_identity_register. Names the source, never what it holds.
result
string
requiredWhat 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_matchunresolvedqueried_at
string · date-time
requiredWhen the source was consulted, as an RFC 3339 timestamp in UTC.
provider
string
requiredWhich provider answered for this source. You can show an auditor who said what, not only which source was asked.
cached
boolean
optionalTrue when evidence inside its validity period was reused rather than re-charged.
policy_pack_version
string
requiredThe 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
optionalNamed where a person decided rather than a rule.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredA 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
requiredThe 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
requiredYour 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
requiredThe 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
optionalHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmenttarget_assurance
string
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4claims
object
requiredWhat the person says about themselves. Identifiers you submit here never come back in full.
5 fields
full_name
string
optionalThe 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
optionalThe 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
optionalThe 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
optionalWrite only. Accepted here, never returned anywhere.
bank_identifier
string · write only
optionalWrite only. Accepted here, never returned anywhere.
Returns
The batch, resolving per subject.
id
string
requiredThe 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"
requiredAlways verification_batch. Tells you which kind of record you are looking at, so one handler can read any response.
status
string
requiredWhere the batch stands: running while any subject's outcome is still pending, completed once none is. Read results for each subject's outcome.
runningcompletedsubject_count
integer
requiredHow many subjects you sent in this batch. results carries one entry for each of them.
results
array of object
requiredOne 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
requiredThe 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
optionalThe 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
requiredunresolved 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.
pendingresolvedunresolvedblockedblocked_because
string · nullable
optionalWhy this subject could not proceed, such as no_consent, when outcome is blocked. null for a subject that went ahead.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
]
}
/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
requiredThe 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
requiredThe 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"
requiredAlways verification_batch. Tells you which kind of record you are looking at, so one handler can read any response.
status
string
requiredWhere the batch stands: running while any subject's outcome is still pending, completed once none is. Read results for each subject's outcome.
runningcompletedsubject_count
integer
requiredHow many subjects you sent in this batch. results carries one entry for each of them.
results
array of object
requiredOne 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
requiredThe 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
optionalThe 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
requiredunresolved 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.
pendingresolvedunresolvedblockedblocked_because
string · nullable
optionalWhy this subject could not proceed, such as no_consent, when outcome is blocked. null for a subject that went ahead.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
]
}
/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
optionalHow 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of Source
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
7 fields of Source
id
string
requiredThe 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"
requiredAlways identity_source. Tells you which kind of record you are looking at, so one handler can read any response.
name
string
requiredThe source's name, for people to read. You never choose a source by name; we pick one for each check.
establishes
array of CheckKind
requiredThe checks this source can establish, such as government_identifier or contactability. One source may establish several.
max_assurance
string
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4health
string
requiredWhether 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.
healthydegradedunavailableevidence_validity_days
integer
optionalHow long evidence from this source stays usable before it must be re-queried.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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
}
/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
optionalHow 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
optionalThe 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
optionalReturn 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_4Returns
A page of Passports, each with a valid_until you never sent, so you can re-check before that date arrives.
object
always "list"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of Passport
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
14 fields of Passport
id
string
requiredThe 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"
requiredAlways passport. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredYour 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
requiredWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance
string
optionalHow 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.
attestedverifiedstatus
string
requiredlivedowngradedrevokedidentifiers_held
array of object
optionalWhich identifiers are on file, masked. Enough to tell a subject what you hold, never enough to use it.
2 fields
kind
string
requiredWhat a consent token may authorise. Ask for what you need and no more.
government_identifierbank_identifierdate_of_birth_matchname_matchcontactabilitymasked
string
requiredAn 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
optionalWhether something disqualifying exists. Whether, not what. The detail is not yours to see.
evidence_bundle_ids
array of string
optionalThe 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
optionalWhether 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.
activepausedvalid_until
string · date-time
requiredWhen the supporting evidence lapses and the level falls on its own.
issued_at
string · date-time
optionalWhen the Passport was first issued, as an RFC 3339 timestamp in UTC. Normally the moment the first verification resolved.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.AnchorApi;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)
// query parameters: limit (optional), starting_after (optional), assurance_level (optional)
result, _, err := client.ANCHORAPI.IdentityPassportsList(ctx).Limit(25).AssuranceLevel(droomwork.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)
{
"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
}
/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
requiredA 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
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4Returns
The new Passport, carrying a date you never sent for when its level stops being supported.
id
string
requiredThe 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"
requiredAlways passport. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredYour 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
requiredWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance
string
optionalHow 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.
attestedverifiedstatus
string
requiredlivedowngradedrevokedidentifiers_held
array of object
optionalWhich identifiers are on file, masked. Enough to tell a subject what you hold, never enough to use it.
2 fields
kind
string
requiredWhat a consent token may authorise. Ask for what you need and no more.
government_identifierbank_identifierdate_of_birth_matchname_matchcontactabilitymasked
string
requiredAn 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
optionalWhether something disqualifying exists. Whether, not what. The detail is not yours to see.
evidence_bundle_ids
array of string
optionalThe 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
optionalWhether 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.
activepausedvalid_until
string · date-time
requiredWhen the supporting evidence lapses and the level falls on its own.
issued_at
string · date-time
optionalWhen the Passport was first issued, as an RFC 3339 timestamp in UTC. Normally the moment the first verification resolved.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways passport. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredYour 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
requiredWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance
string
optionalHow 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.
attestedverifiedstatus
string
requiredlivedowngradedrevokedidentifiers_held
array of object
optionalWhich identifiers are on file, masked. Enough to tell a subject what you hold, never enough to use it.
2 fields
kind
string
requiredWhat a consent token may authorise. Ask for what you need and no more.
government_identifierbank_identifierdate_of_birth_matchname_matchcontactabilitymasked
string
requiredAn 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
optionalWhether something disqualifying exists. Whether, not what. The detail is not yours to see.
evidence_bundle_ids
array of string
optionalThe 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
optionalWhether 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.
activepausedvalid_until
string · date-time
requiredWhen the supporting evidence lapses and the level falls on its own.
issued_at
string · date-time
optionalWhen the Passport was first issued, as an RFC 3339 timestamp in UTC. Normally the moment the first verification resolved.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
optionalHow 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
optionalThe 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of MergeCandidate
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
8 fields of MergeCandidate
id
string
requiredThe 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"
requiredAlways merge_candidate. Tells you which kind of record you are looking at, so one handler can read any response.
left_ref
string
requiredOne 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
requiredThe 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
requiredOnly a strong match merges automatically. Probable is raised for you to decide.
strongprobableweaksignals
array of object
requiredWhat matched and what did not. Both, so you see the case against as well as the case for.
3 fields
signal
string
requiredWhich attribute was compared, such as date_of_birth. One entry per attribute, whichever way it came out.
agrees
boolean
requiredtrue 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
optionalA plain-words note on how this attribute compared, when there's more to say than agrees. Left out otherwise.
status
string
optionalWhere 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.
openmergedrejectedcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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
}
/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
requiredThe 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
requiredThe 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"
requiredAlways merge_candidate. Tells you which kind of record you are looking at, so one handler can read any response.
left_ref
string
requiredOne 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
requiredThe 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
requiredOnly a strong match merges automatically. Probable is raised for you to decide.
strongprobableweaksignals
array of object
requiredWhat matched and what did not. Both, so you see the case against as well as the case for.
3 fields
signal
string
requiredWhich attribute was compared, such as date_of_birth. One entry per attribute, whichever way it came out.
agrees
boolean
requiredtrue 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
optionalA plain-words note on how this attribute compared, when there's more to say than agrees. Left out otherwise.
status
string
optionalWhere 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.
openmergedrejectedcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredA 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
requiredThe 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
requiredThe 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
optionalThe 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
requiredWhy 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
requiredA second actor, distinct from the caller.
Returns
The merge.
id
string
requiredThe 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"
requiredAlways identity_merge. Tells you which kind of record you are looking at, so one handler can read any response.
surviving_ref
string
requiredThe 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
requiredThe 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
optionalThe 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
requiredTwo distinct actors. The same credential twice is refused.
2 fields of FourEyes
requested_by
string
requiredThe actor who requested the action: the credential that made the call. Never the same as approved_by.
approved_by
string
requiredThe 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
optionalWhy 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
requiredAlways 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
optionalA 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
optionalWhen the merge was reversed, as an RFC 3339 timestamp in UTC. null while the merge still stands.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe id of the merge you want to reverse, from the response to POST /v1/identity/merges.
Headers
Idempotency-Key
string
requiredA 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
requiredWhy 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
requiredA second actor who approved the reversal, distinct from you. The same credential twice is refused.
Returns
The reversed merge.
id
string
requiredThe 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"
requiredAlways identity_merge. Tells you which kind of record you are looking at, so one handler can read any response.
surviving_ref
string
requiredThe 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
requiredThe 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
optionalThe 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
requiredTwo distinct actors. The same credential twice is refused.
2 fields of FourEyes
requested_by
string
requiredThe actor who requested the action: the credential that made the call. Never the same as approved_by.
approved_by
string
requiredThe 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
optionalWhy 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
requiredAlways 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
optionalA 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
optionalWhen the merge was reversed, as an RFC 3339 timestamp in UTC. null while the merge still stands.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/v1/identity/blocklist_entries#Blocklist an identity
identity.blocklist_entries.create
This stops one person transacting anywhere on Droomwork. 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
requiredA 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
requiredYour 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
requiredWhy 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
requiredA second actor, distinct from the caller.
Returns
The blocklist entry.
id
string
requiredThe 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"
requiredAlways blocklist_entry. Tells you which kind of record you are looking at, so one handler can read any response.
subject_ref
string
requiredYour 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
requiredWhy this person was blocklisted, as you stated it on the request.
four_eyes
FourEyes
requiredTwo distinct actors. The same credential twice is refused.
2 fields of FourEyes
requested_by
string
requiredThe actor who requested the action: the credential that made the call. Never the same as approved_by.
approved_by
string
requiredThe 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
requiredactive while the person is stopped from transacting anywhere on Droomwork; lifted once you reversed the blocklisting at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse.
activeliftedbefore_state_ref
string
optionalA 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe id of the entry you want to lift, from the response to POST /v1/identity/blocklist_entries.
Headers
Idempotency-Key
string
requiredA 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
requiredWhy 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
requiredA 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
requiredThe 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"
requiredAlways blocklist_entry. Tells you which kind of record you are looking at, so one handler can read any response.
subject_ref
string
requiredYour 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
requiredWhy this person was blocklisted, as you stated it on the request.
four_eyes
FourEyes
requiredTwo distinct actors. The same credential twice is refused.
2 fields of FourEyes
requested_by
string
requiredThe actor who requested the action: the credential that made the call. Never the same as approved_by.
approved_by
string
requiredThe 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
requiredactive while the person is stopped from transacting anywhere on Droomwork; lifted once you reversed the blocklisting at POST /v1/identity/blocklist_entries/{blocklist_entry_id}/reverse.
activeliftedbefore_state_ref
string
optionalA 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
optionalHow 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
optionalThe 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
optionalReturns 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.
activerevokedexpiredsuspendedReturns
A page of disclosures.
object
always "list"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of Disclosure
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
13 fields of Disclosure
id
string
requiredThe 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"
requiredAlways disclosure. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
optionalYour 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
requiredThe purpose you gave on the request, such as employment_verification. What you may see is scoped to it.
consent_token_id
string
optionalThe 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
optionalWhat you may see. Identifiers appear masked and never in full.
status
string
requiredactiverevokedexpiredsuspendedsuspended_because
string · nullable
optionalWhy 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_downgradednullexpires_at
string · date-time
requiredTime boxed. There is no open ended disclosure.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
has_more
boolean
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.sdk.api.AnchorApi;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.NewAPIClient(cfg)
// query parameters: limit (optional), starting_after (optional), status (optional)
result, _, err := client.ANCHORAPI.IdentityDisclosuresList(ctx).Limit(25).Status(droomwork.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)
{
"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
}
/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
requiredA 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
requiredThe 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
requiredWhat 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
requiredThe 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
optionalThe 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
requiredWhen the disclosure ends, as an RFC 3339 timestamp in UTC. Required: there is no open ended disclosure.
Returns
The disclosure.
id
string
requiredThe 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"
requiredAlways disclosure. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
optionalYour 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
requiredThe purpose you gave on the request, such as employment_verification. What you may see is scoped to it.
consent_token_id
string
optionalThe 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
optionalWhat you may see. Identifiers appear masked and never in full.
status
string
requiredactiverevokedexpiredsuspendedsuspended_because
string · nullable
optionalWhy 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_downgradednullexpires_at
string · date-time
requiredTime boxed. There is no open ended disclosure.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
optionalThe 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"
requiredAlways 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
requiredThe 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
optionalWhen this report was produced, as an RFC 3339 timestamp in UTC.
disclosures
array of object
requiredOne 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
requiredThe 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
optionalThe 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
requiredactiverevokedexpiredsuspendedsequence
integer
requiredThe 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
optionalWhen the disclosure last changed standing, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
# 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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways disclosure. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
optionalYour 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
requiredThe purpose you gave on the request, such as employment_verification. What you may see is scoped to it.
consent_token_id
string
optionalThe 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
optionalWhat you may see. Identifiers appear masked and never in full.
status
string
requiredactiverevokedexpiredsuspendedsuspended_because
string · nullable
optionalWhy 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_downgradednullexpires_at
string · date-time
requiredTime boxed. There is no open ended disclosure.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredA 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
requiredThe 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"
requiredAlways disclosure. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredThe 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
optionalYour 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
requiredThe purpose you gave on the request, such as employment_verification. What you may see is scoped to it.
consent_token_id
string
optionalThe 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
optionalWhat you may see. Identifiers appear masked and never in full.
status
string
requiredactiverevokedexpiredsuspendedsuspended_because
string · nullable
optionalWhy 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_downgradednullexpires_at
string · date-time
requiredTime boxed. There is no open ended disclosure.
created_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredA 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
requiredaccesscorrectionerasurerestrict_processingportabilitysubject_ref
string
requiredThe 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
optionalFree 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
requiredThe 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"
requiredAlways subject_request. Tells you which kind of record you are looking at, so one handler can read any response.
kind
string
requiredaccesscorrectionerasurerestrict_processingportabilitysubject_ref
string
requiredThe 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
requiredWhere 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_progresscompletedrefuseddue_by
string · date
requiredThe 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
optionalWhat was kept and under which obligation. Erasure removes personal data while keeping signed records verifiable and honouring statutory retention.
3 fields
category
string
requiredThe 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
requiredWhy 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
optionalThe date the retention ends, as a calendar date in YYYY-MM-DD. Absent when no end date applies.
completed_at
string · date-time · nullable
optionalWhen the request was completed, as an RFC 3339 timestamp in UTC. null while the request is still open.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredThe 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"
requiredAlways subject_request. Tells you which kind of record you are looking at, so one handler can read any response.
kind
string
requiredaccesscorrectionerasurerestrict_processingportabilitysubject_ref
string
requiredThe 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
requiredWhere 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_progresscompletedrefuseddue_by
string · date
requiredThe 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
optionalWhat was kept and under which obligation. Erasure removes personal data while keeping signed records verifiable and honouring statutory retention.
3 fields
category
string
requiredThe 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
requiredWhy 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
optionalThe date the retention ends, as a calendar date in YYYY-MM-DD. Absent when no end date applies.
completed_at
string · date-time · nullable
optionalWhen the request was completed, as an RFC 3339 timestamp in UTC. null while the request is still open.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredA 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
requiredThe 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
requiredWhy 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
requiredThe 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
requiredThe 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"
requiredAlways access_grant. Tells you which kind of record you are looking at, so one handler can read any response.
subject_ref
string
requiredThe 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
requiredStated before anything is seen, and logged to the field.
granted_to
string
requiredWho 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
optionalThe 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
requiredWhen 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
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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"
requiredAlways gating_contract. Tells you which kind of record you are looking at, so one handler can read any response.
version
string
requiredWhich 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
requiredOne 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
requiredWhich module these requirements are for: proof (credentials), rail (working relationships), flow (sourcing), match (work allocation), run (payroll), remit (statutory remittance) or route (payouts).
proofrailflowmatchrunremitrouterequires
array of object
requiredWhat 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
requiredThe fact that must hold, in words, such as live unrevoked Passport.
minimum_assurance
string
requiredHow 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.
attestedverifiedcontract_row
string
optionalThe 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
optionalGates that hold in every configuration, whatever anyone has attested to.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/identity/gating_contract" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, ANCHORApi } from '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
]
}
/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 Droomwork 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"
requiredAlways readiness_report. Tells you which kind of record you are looking at, so one handler can read any response.
module
string
requiredThe 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).
anchorproofrailflowmatchrunremitroutemode
string
requiredintegrated means a required fact comes from the Droomwork 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.
integratedstandaloneready
boolean
requiredtrue 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
requiredOne 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
requiredNamed for what it is, not for who supplies it.
owner
string
requiredThe sibling module that owns this fact when running integrated.
contract_row
string
optionalrequired
string
requiredHow 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.
attestedverifiedheld
one of
requiredAssuranceorsource
string · nullable
optionalintegratedstandalonenullsatisfied
boolean
requiredmissing_because
string · nullable
optionalnot_suppliedassurance_too_lownullalways_enforced
array of string
optionalGates 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
optionalThe 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
optionalHow many subjects on file have no active consent covering the checks you use.
Other responses
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/identity/readiness" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, ANCHORApi } from '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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
}
/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
requiredThe 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
requiredA 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
requiredThe 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"
requiredAlways identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredunresolved 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.
pendingresolvedunresolvedblockedorigin
string
requiredHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmentsubject_ref
string
requiredYour 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
optionalThe 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
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance_reached
one of
optionalThe level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.
AssuranceLevelorpassport_id
string · nullable
optionalThe 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
optionalThe 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
optionalNamed when the outcome is unresolved. Silence always names its source.
retry_after
string · date-time · nullable
optionalWhen to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.
blocked_because
string · nullable
optionalWhy 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_blocklistednullcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredA 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
requiredThe 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"
requiredAlways identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredunresolved 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.
pendingresolvedunresolvedblockedorigin
string
requiredHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmentsubject_ref
string
requiredYour 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
optionalThe 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
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance_reached
one of
optionalThe level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.
AssuranceLevelorpassport_id
string · nullable
optionalThe 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
optionalThe 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
optionalNamed when the outcome is unresolved. Silence always names its source.
retry_after
string · date-time · nullable
optionalWhen to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.
blocked_because
string · nullable
optionalWhy 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_blocklistednullcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
requiredThe 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
requiredA 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
requiredThe 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"
requiredAlways identity_verification. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm this record is in: false is the sandbox, true is live. Read it before you act on anything.
mocked
boolean
requiredWhere 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
requiredunresolved 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.
pendingresolvedunresolvedblockedorigin
string
requiredHow the claim arrived. The behaviour is the same whichever way it came in.
partner_apiself_serveassisted_enrolmentsubject_ref
string
requiredYour 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
optionalThe 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
optionalWhat was actually established. DAL-3 and DAL-4 are documented and not delivered in this release.
dal_0dal_1dal_2dal_3dal_4assurance_reached
one of
optionalThe level the evidence actually established, which can be lower than target_assurance. null when nothing has been established yet.
AssuranceLevelorpassport_id
string · nullable
optionalThe 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
optionalThe 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
optionalNamed when the outcome is unresolved. Silence always names its source.
retry_after
string · date-time · nullable
optionalWhen to try again, as an RFC 3339 timestamp in UTC. Set when the outcome is unresolved; null otherwise.
blocked_because
string · nullable
optionalWhy 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_blocklistednullcreated_at
string · date-time
optionalWhen the record was created, as an RFC 3339 timestamp in UTC.
Other responses
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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"
}
/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
optionalThe 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
optionalThe 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
optionalHow 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
optionalThe 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of StoredEvent
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
13 fields of StoredEvent
id
string
requiredThe 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
requiredWhat 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
requiredThe 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
requiredThe 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
requiredPer 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
requiredWhen the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.
request_id
string
optionalThe 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
requiredWhich realm the event happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhich part of Droomwork 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.
anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligenceobject
always "event"
requiredAlways event. Tells you which kind of record you are looking at, so one handler can read any response.
stream
string
requiredThe 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
requiredThe 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
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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
}
/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
requiredThe 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
requiredThe 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
requiredWhat 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
requiredThe 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
requiredThe 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
requiredPer 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
requiredWhen the event happened, as an RFC 3339 timestamp in UTC. Not when it was delivered: a redelivery carries the original value.
request_id
string
optionalThe 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
requiredWhich realm the event happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhich part of Droomwork 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.
anchorproofrailflowmatchrunremitroutegatewayiamledgerregistrydeliverydocumentsintelligenceobject
always "event"
requiredAlways event. Tells you which kind of record you are looking at, so one handler can read any response.
stream
string
requiredThe 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
requiredThe 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
Errors it can return
curl -X GET "https://sandbox.droomwork.io/v1/identity/events/%7Bevent_id%7D" \
-H "Droomwork-Api-Key: $DROOMWORK_API_KEY"import { Configuration, ANCHORApi } from '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"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": {}
}
/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 Droomwork 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
optionalOnly 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
optionalOnly one credential's attempts, matched exactly on actor_id as it reads on each entry: your API key's id (api_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
optionalOnly 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
optionalOnly 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
optionalOnly 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.
succeededrefusedfailedrecorded_after
string
optionalOnly 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
optionalOnly 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
optionalHow 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
optionalThe 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"
requiredAlways list. Tells you which kind of record you are looking at, so one handler can read any response.
data
array of AuditEntry
requiredThe records on this page, in the order the list promises. Empty when nothing matched.
13 fields of AuditEntry
id
string
requiredThe record's identifier, as GET /v1/identity/audit_entries lists it. It starts with audit_entry_ and never changes; pass it as audit_entry_id to GET /v1/identity/audit_entries/{audit_entry_id}.
object
always "audit_entry"
requiredAlways audit_entry. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm the action happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhen the attempt was made, as an RFC 3339 timestamp in UTC. The list orders entries newest first by it.
request_id
string
requiredThe 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
requiredWhat kind of credential acted: client for one of your API keys or OAuth clients, staff for a Droomwork staff member acting under a grant. actor_id says which one.
actor_id
string
requiredThe id of the credential that made the attempt: your API key's (api_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
requiredWhat was attempted, as the method and the route pattern.
resource
string · nullable
optionalThe 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
optionalThe 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
requiredHow 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.
succeededrefusedfailedstatus
integer
requiredThe 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
requiredtrue when there are more records after this page. Pass the last record's id as starting_after to get the next page.
Other responses
Errors it can return
# 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=api_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 '@droomwork/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: 'api_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=api_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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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='api_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=api_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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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: 'api_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=api_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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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", "api_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=api_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 Droomwork.Sdk.Api;
using Droomwork.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: "api_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=api_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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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("api_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=api_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)
{
"object": "list",
"data": [
{
"id": "audit_entry_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
}
/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
requiredThe entry's id, as GET /v1/identity/audit_entries lists it. It starts with audit_entry_.
Returns
The audit entry.
id
string
requiredThe record's identifier, as GET /v1/identity/audit_entries lists it. It starts with audit_entry_ and never changes; pass it as audit_entry_id to GET /v1/identity/audit_entries/{audit_entry_id}.
object
always "audit_entry"
requiredAlways audit_entry. Tells you which kind of record you are looking at, so one handler can read any response.
livemode
boolean
requiredWhich realm the action happened in. False is the sandbox.
mocked
boolean
requiredWhether 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
requiredWhen the attempt was made, as an RFC 3339 timestamp in UTC. The list orders entries newest first by it.
request_id
string
requiredThe 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
requiredWhat kind of credential acted: client for one of your API keys or OAuth clients, staff for a Droomwork staff member acting under a grant. actor_id says which one.
actor_id
string
requiredThe id of the credential that made the attempt: your API key's (api_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
requiredWhat was attempted, as the method and the route pattern.
resource
string · nullable
optionalThe 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
optionalThe 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
requiredHow 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.
succeededrefusedfailedstatus
integer
requiredThe 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
Errors it can return
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 '@droomwork/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 droomwork
config = droomwork.Configuration(access_token=os.environ['DROOMWORK_API_KEY'])
config.host = 'https://sandbox.droomwork.io'
client = droomwork.ApiClient(config)
api = droomwork.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 = DroomworkSdk\Configuration::getDefaultConfiguration()
->setHost('https://sandbox.droomwork.io')
->setAccessToken(getenv('DROOMWORK_API_KEY'));
$api = new DroomworkSdk\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.droomwork.sdk.ApiClient;
import com.droomwork.sdk.Configuration;
import com.droomwork.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 Droomwork.Sdk.Api;
using Droomwork.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 droomwork "github.com/fenibofubara/droomwork-sdk-go"
ctx := context.WithValue(context.Background(), droomwork.ContextAPIKeys, map[string]droomwork.APIKey{
"apiKey": {Key: os.Getenv("DROOMWORK_API_KEY")},
})
cfg := droomwork.NewConfiguration()
cfg.Servers = droomwork.ServerConfigurations{{URL: "https://sandbox.droomwork.io"}}
client := droomwork.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)
{
"id": "audit_entry_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"
}