Checks
Check documents and read results.
The CheckResult object
One item's result. Every field is present (null when absent).
Attributes
idrequired Type:stringThe result's id.
item_indexrequired Type:integerkindrequired Type:"check""signer_match"The item's kind. A signer match has its answer in
signer_match; itsfile,subject_reference,decision,signaturesandinput_hashesare empty.filerequired Type:stringnullsubject_referencerequired Type:stringnullYour id for the person compared against.
referencerequired Type:CheckReferencenullprocessrequired Type:Processnullmetadatarequired Type:Metadatanullstatusrequired Type:"completed""failed"errorrequired Type:ItemErrornulldecisionrequired Type:Decisionnullinput_typerequired Type:stringnullpagesrequired Type:integernullnotesrequired Type:string[]Document notes:
codeorcode:detail, pages from 1.signaturesrequired Type:Signature[]anchor_probabilityrequired Type:numbernullsigner_matchrequired Type:SignerMatchResultnullversionsrequired Type:Versionsinput_hashesrequired Type:objectnulltimings_msrequired Type:object
{
"id": "ver_01j9x3m8v7k2q4r5s6t7u8v9w0",
"item_index": 0,
"kind": "check",
"file": "questioned",
"subject_reference": "cust-88",
"reference": "APP-1042",
"process": "loan-application",
"metadata": {
"branch": "leeds"
},
"status": "completed",
"error": null,
"decision": "APPROVE",
"input_type": "photo",
"pages": 1,
"notes": [],
"signatures": [
{
"index": 0,
"page": 1,
"bbox": [
412,
1880,
498,
160
],
"located_by": "template",
"quality": {
"ok": true,
"effective_dpi": 214,
"sharpness": 812.5,
"contrast": 180,
"issues": []
},
"match_probability": 0.94,
"band": [
0.88,
0.97
],
"reference_count": 2,
"per_reference": [
{
"ref": "ref_01j9x3m8v7k2q4r5s6t7u8v9w1",
"probability": 0.95
},
{
"ref": "ref_01j9x3m8v7k2q4r5s6t7u8v9w2",
"probability": 0.92
}
],
"flags": [],
"decision": "APPROVE",
"reasons": [
"Compared against 2 references",
"Overall appearance is within this writer's usual range",
"Shape alignment is within this writer's usual range",
"Approved, not auto-approved: the lowest likely score (88.0%) is under the auto-approve bar (97.0%), and auto-approve is off in policy default@7"
],
"best_match": true
}
],
"anchor_probability": 0.94,
"signer_match": null,
"versions": {
"model": "nib-2.6",
"policy": "default@7",
"api_version": "2026-09-27"
},
"input_hashes": {
"questioned": "sha256:8550d9b6affca27a5645d75b058479d2e081b16a135e2d4255e5e83714ebf987",
"references": [
"sha256:070a8e98f85afe04ba05542428664ec3684db24bc356133c11d41f9fab27f135",
"sha256:1cf1da50e3f0be0b50197022be0eedf20fa688cf20ca1154b388098a9d2e29e1"
]
},
"timings_ms": {
"total": 1840
}
} The Job object
Attributes
job_idrequired Type:stringstatusrequired Type:"queued""running""completed""failed""rejected"sourcerequired Type:"api""bucket""desk"itemsrequired Type:integercreated_atrequired Type:stringstarted_atrequired Type:stringnullcompleted_atrequired Type:stringnullstatus_urlrequired Type:stringsummaryrequired Type:JobSummarynullresultsrequired Type:CheckResult[]next_cursorrequired Type:stringnullPass as
cursor;nullon the last page.
{
"job_id": "inv-2291",
"status": "running",
"source": "api",
"items": 40,
"created_at": "2026-09-25T10:15:30.123Z",
"started_at": "2026-09-25T10:15:31.000Z",
"completed_at": null,
"status_url": "/nib/checks/inv-2291",
"summary": null,
"results": [],
"next_cursor": null
} List checks
GET /nib/checks
Needs nib:checks:read: Read checks and results
Newest first. Page with cursor.
Parameters
viewqueryoptional Type:"jobs""items""summary"jobs(default),items, orsummary(counts per decision).cursorqueryoptional Type:stringThe previous page's
next_cursor.limitqueryoptional Type:integersincequeryoptional Type:stringCreated at or after (RFC 3339).
untilqueryoptional Type:stringCreated before (RFC 3339).
statusqueryoptional Type:"queued""running""completed""failed""rejected"view=jobsonly.sourcequeryoptional Type:"api""bucket""desk"view=jobsonly.desk: checks made by unforged/desk.referencequeryoptional Type:CheckReferenceview=itemsorsummary: exact check reference.processqueryoptional Type:Processview=itemsorsummary: exact process.subject_referencequeryoptional Type:Idview=itemsorsummary: exact subject reference.decisionqueryoptional Type:stringComma-separated decisions;
ERRORfor items without one.input_typequeryoptional Type:stringview=itemsorsummary: e.g.photo,scan,form,signature_image.qqueryoptional Type:stringview=itemsorsummary: prefix of the reference, subject, file name or check id.
Returns
200A page of jobs or items, or the counts400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl "https://api.unforged.sh/nib/checks" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/nib/checks",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/checks', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Check one document or a batch
POST /nib/checks
Needs nib:checks:create: Submit checks, sessions, live
Multipart: a JSON request part (CheckRequest) and one part per file, or JSON with URL inputs. One item of up to 10 pages returns 200; larger checks are queued (202). Accept: text/event-stream streams the check. A signer_match item (SignerMatchItem) is always queued, can't be streamed, and counts one against your monthly quota for each of its documents.
Parameters
Idempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
Acceptheaderoptional Type:stringtext/event-streamto stream the check.
Request body
itemsrequired Type:CheckItemSignerMatchItem[]policyoptional Type:stringprocessoptional Type:ProcessDefault
processfor items.metadataoptional Type:MetadataDefault
metadatafor items.job_idoptional Type:Id
Returns
200Type:CheckResultThe result, or the event stream202Type:CheckAcceptedQueued; pollstatus_url400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized402Type:Problembilling_required403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)408Type:Problemtimeout409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable413Type:Problemtoo_large415Type:Problemunsupported_media421Type:Problemwrong_region422Type:Problemfetch_failedornot_allowlisted429Type:Problemrate_limitedorquota_exceeded503Type:Problembusy,not_ready,org_movingorstorage_provisioning(your organisation's storage is still being set up); retry afterRetry-After504Type:Problemtimeout; a check continues asynchronously (job_id,status_url)defaultType:Probleminternal
curl -X POST "https://api.unforged.sh/nib/checks" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"file": {
"url": "https://files.example.com/app-1041.png?sig=abc"
},
"subject_reference": "cust-88",
"target": {
"page": 1,
"box": "applicant_signature"
}
}
],
"policy": "default",
"metadata": {
"case_ref": "A-1234"
}
}' import requests
r = requests.post(
"https://api.unforged.sh/nib/checks",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
json={
"items": [
{
"file": {
"url": "https://files.example.com/app-1041.png?sig=abc"
},
"subject_reference": "cust-88",
"target": {
"page": 1,
"box": "applicant_signature"
}
}
],
"policy": "default",
"metadata": {
"case_ref": "A-1234"
}
},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/checks', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
body: JSON.stringify({
"items": [
{
"file": {
"url": "https://files.example.com/app-1041.png?sig=abc"
},
"subject_reference": "cust-88",
"target": {
"page": 1,
"box": "applicant_signature"
}
}
],
"policy": "default",
"metadata": {
"case_ref": "A-1234"
}
}),
});
console.log(await res.json()); {
"id": "ver_01j9x3m8v7k2q4r5s6t7u8v9w0",
"item_index": 0,
"kind": "check",
"file": "questioned",
"subject_reference": "cust-88",
"reference": "APP-1042",
"process": "loan-application",
"metadata": {
"branch": "leeds"
},
"status": "completed",
"error": null,
"decision": "APPROVE",
"input_type": "photo",
"pages": 1,
"notes": [],
"signatures": [
{
"index": 0,
"page": 1,
"bbox": [
412,
1880,
498,
160
],
"located_by": "template",
"quality": {
"ok": true,
"effective_dpi": 214,
"sharpness": 812.5,
"contrast": 180,
"issues": []
},
"match_probability": 0.94,
"band": [
0.88,
0.97
],
"reference_count": 2,
"per_reference": [
{
"ref": "ref_01j9x3m8v7k2q4r5s6t7u8v9w1",
"probability": 0.95
},
{
"ref": "ref_01j9x3m8v7k2q4r5s6t7u8v9w2",
"probability": 0.92
}
],
"flags": [],
"decision": "APPROVE",
"reasons": [
"Compared against 2 references",
"Overall appearance is within this writer's usual range",
"Shape alignment is within this writer's usual range",
"Approved, not auto-approved: the lowest likely score (88.0%) is under the auto-approve bar (97.0%), and auto-approve is off in policy default@7"
],
"best_match": true
}
],
"anchor_probability": 0.94,
"signer_match": null,
"versions": {
"model": "nib-2.6",
"policy": "default@7",
"api_version": "2026-09-27"
},
"input_hashes": {
"questioned": "sha256:8550d9b6affca27a5645d75b058479d2e081b16a135e2d4255e5e83714ebf987",
"references": [
"sha256:070a8e98f85afe04ba05542428664ec3684db24bc356133c11d41f9fab27f135",
"sha256:1cf1da50e3f0be0b50197022be0eedf20fa688cf20ca1154b388098a9d2e29e1"
]
},
"timings_ms": {
"total": 1840
}
} A job and its results
GET /nib/checks/{job_id}
Needs nib:checks:read: Read checks and results
Paged by item_index. format=ndjson streams a finished job's results (409 conflict while it runs).
Parameters
job_idpathrequired Type:Idcursorqueryoptional Type:stringThe previous page's
next_cursor.limitqueryoptional Type:integerformatqueryoptional Type:"json""ndjson"decisionqueryoptional Type:stringComma-separated decisions;
ERRORfor items without one.
Returns
200Type:JobThe job400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl "https://api.unforged.sh/nib/checks/{job_id}" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/nib/checks/{job_id}",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/checks/{job_id}', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); {
"job_id": "inv-2291",
"status": "running",
"source": "api",
"items": 40,
"created_at": "2026-09-25T10:15:30.123Z",
"started_at": "2026-09-25T10:15:31.000Z",
"completed_at": null,
"status_url": "/nib/checks/inv-2291",
"summary": null,
"results": [],
"next_cursor": null
} The questioned signature, cropped (PNG)
GET /nib/checks/{job_id}/items/{index}/image
Needs nib:checks:read: Read checks and results
Greyscale, at most 640 px. 404 if none was located or the input has expired (inbox_ttl after the check, 24 h by default).
Parameters
job_idpathrequired Type:Idindexpathrequired Type:integerThe item's index in the request.
signaturequeryoptional Type:integerThe signature's index (default: the best match).
Returns
200The crop400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceeded503Type:Problembusy,not_ready,org_movingorstorage_provisioning(your organisation's storage is still being set up); retry afterRetry-AfterdefaultType:Probleminternal
curl "https://api.unforged.sh/nib/checks/{job_id}/items/{index}/image" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/nib/checks/{job_id}/items/{index}/image",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/checks/{job_id}/items/{index}/image', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); A specimen signature of a signer match (PNG)
GET /nib/checks/{job_id}/items/{index}/specimens/{n}/image
Needs nib:checks:read: Read checks and results
The specimen signature a signer match compared, as listed in its result: n counts the specimens across the item's people, in order, from 0. The result's image gives the path. We keep these crops with the result, so they're deleted when your history retention deletes the job. 404 for a set that needed marking, or a specimen we no longer have.
Parameters
job_idpathrequired Type:Idindexpathrequired Type:integerThe item's index in the request.
npathrequired Type:integerThe specimen's place in the result, from 0.
Returns
200The crop400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl "https://api.unforged.sh/nib/checks/{job_id}/items/{index}/specimens/{n}/image" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/nib/checks/{job_id}/items/{index}/specimens/{n}/image",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/checks/{job_id}/items/{index}/specimens/{n}/image', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Confirm an item's signature as genuine
POST /nib/checks/{job_id}/items/{index}/confirm
Needs nib:references:write: Enroll references, confirm-to-learn
If learning is on, the signature may become a reference. No body.
Parameters
job_idpathrequired Type:Idindexpathrequired Type:integerThe item's index in the request.
Idempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
Returns
200Type:ConfirmedRecorded400Type:Probleminvalid_request,invalid_version,idempotency_key_requiredorinvalid_signature401Type:Problemunauthorized402Type:Problembilling_required403Type:Problempermission_denied,org_suspended, oraddress_not_allowed(your organisation only accepts requests from the addresses it has listed)404Type:Problemnot_found409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable421Type:Problemwrong_region429Type:Problemrate_limitedorquota_exceededdefaultType:Probleminternal
curl -X POST "https://api.unforged.sh/nib/checks/{job_id}/items/{index}/confirm" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.post(
"https://api.unforged.sh/nib/checks/{job_id}/items/{index}/confirm",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/checks/{job_id}/items/{index}/confirm', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); {
"job_id": "inv-2291",
"item_index": 0,
"confirmed_by": "api_key:key_01j9x3m8v7k2q4r5s6t7u8v9w0",
"confirmed_at": "2026-09-25T11:00:00Z",
"learning": "queued"
}