Webhook endpoints
Webhook endpoints and their delivery log.
The Webhook object
Attributes
idrequired Type:stringurlrequired Type:stringeventsrequired Type:EventType[]created_atrequired Type:string
The WebhookEvent object
Attributes
idrequired Type:stringThe same on every retry.
typerequired Type:EventType"webhook.test"An event you subscribed to, or
webhook.test.created_atrequired Type:stringdatarequired Type:objectThe event's data.
job.completedhas the first 100 results;items_urlpages the rest.
{
"id": "evt_01j9x3m8v7k2q4r5s6t7u8v9w0",
"type": "job.completed",
"created_at": "2026-09-25T10:15:30.123Z",
"data": {
"job_id": "inv-2291",
"status": "completed",
"total": 1,
"counts": {
"APPROVE": 1
},
"failed": 0,
"items": [
{
"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": [],
"anchor_probability": 0.94,
"signer_match": null,
"versions": {
"model": "nib-2.6",
"policy": "default@7",
"api_version": "2026-09-27"
},
"input_hashes": null,
"timings_ms": {
"total": 1840
}
}
],
"items_truncated": false,
"versions": {
"model": "nib-2.6",
"policy": "default@7",
"api_version": "2026-09-27"
},
"status_url": "/nib/checks/inv-2291"
}
} Webhook endpoints
GET /nib/webhooks
Needs org:read: See org, members, usage
Returns
200The endpoints401Type: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/webhooks" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/nib/webhooks",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/webhooks', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Create a webhook endpoint
POST /nib/webhooks
Needs nib:settings:manage: Policy, learning, templates, webhooks, bucket keys
The URL must be public https and answer a signed webhook.test event with 2xx within 10 s (else 400 webhook_test_failed). The secret is shown once. At most 20 endpoints.
Request body
urlrequired Type:stringeventsrequired Type:EventType[]
Returns
201Type:WebhookCreatedCreated400Type: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)409Type:Problemconflict,idempotency_conflict,idempotency_in_progressoridempotency_not_replayable413Type:Problemtoo_large421Type: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 -X POST "https://api.unforged.sh/nib/webhooks" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/unforged",
"events": [
"job.completed",
"reference.rejected"
]
}' import requests
r = requests.post(
"https://api.unforged.sh/nib/webhooks",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
json={
"url": "https://hooks.example.com/unforged",
"events": [
"job.completed",
"reference.rejected"
]
},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/webhooks', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
body: JSON.stringify({
"url": "https://hooks.example.com/unforged",
"events": [
"job.completed",
"reference.rejected"
]
}),
});
console.log(await res.json()); {
"id": "whk_01j9x3m8v7k2q4r5s6t7u8v9w0",
"url": "https://hooks.example.com/unforged",
"events": [
"job.completed",
"reference.rejected"
],
"created_at": "2026-09-25T10:15:30.123Z",
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
} Delete a webhook endpoint
DELETE /nib/webhooks/{webhook_id}
Needs nib:settings:manage: Policy, learning, templates, webhooks, bucket keys
Queued deliveries are dropped.
Parameters
webhook_idpathrequired Type:string
Returns
204Done401Type: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 -X DELETE "https://api.unforged.sh/nib/webhooks/{webhook_id}" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.delete(
"https://api.unforged.sh/nib/webhooks/{webhook_id}",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/webhooks/{webhook_id}', {
method: 'DELETE',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Replace a webhook's signing secret
POST /nib/webhooks/{webhook_id}/secret/roll
Needs nib:settings:manage: Policy, learning, templates, webhooks, bucket keys
The new secret is shown once and signs every delivery from now on.
Parameters
webhook_idpathrequired Type:string
Returns
200Type:WebhookCreatedThe endpoint and its new secret401Type: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 -X POST "https://api.unforged.sh/nib/webhooks/{webhook_id}/secret/roll" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.post(
"https://api.unforged.sh/nib/webhooks/{webhook_id}/secret/roll",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/webhooks/{webhook_id}/secret/roll', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); {
"id": "whk_01j9x3m8v7k2q4r5s6t7u8v9w0",
"url": "https://hooks.example.com/unforged",
"events": [
"job.completed",
"reference.rejected"
],
"created_at": "2026-09-25T10:15:30.123Z",
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
} The delivery log, newest first
GET /nib/webhooks/{webhook_id}/deliveries
Needs nib:settings:manage: Policy, learning, templates, webhooks, bucket keys
Parameters
webhook_idpathrequired Type:stringlimitqueryoptional Type:integercursorqueryoptional Type:stringThe previous page's
next_cursor.
Returns
200Type:WebhookDeliveryPageA page of attempts (kept 30 days)400Type: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/webhooks/{webhook_id}/deliveries" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.get(
"https://api.unforged.sh/nib/webhooks/{webhook_id}/deliveries",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/webhooks/{webhook_id}/deliveries', {
method: 'GET',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); Send an attempt's event again
POST /nib/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver
Needs nib:settings:manage: Policy, learning, templates, webhooks, bucket keys
Queues the same event again.
Parameters
webhook_idpathrequired Type:stringdelivery_idpathrequired Type:stringIdempotency-Keyheaderrequired Type:string1–255 printable ASCII characters, kept 24 hours.
Returns
202Type:RedeliveryQueued400Type: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_exceeded503Type:Problembusy,not_ready,org_movingorstorage_provisioning(your organisation's storage is still being set up); retry afterRetry-AfterdefaultType:Probleminternal
curl -X POST "https://api.unforged.sh/nib/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver" \
-H "Authorization: Bearer $UNFORGED_KEY" \
-H "Unforged-Version: 2026-10-14" import requests
r = requests.post(
"https://api.unforged.sh/nib/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver",
headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json()) const res = await fetch('https://api.unforged.sh/nib/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json()); {
"status": "queued",
"redelivery_of": "whd_01j9x3m8v7k2q4r5s6t7u8v9w0"
}