Contents

Webhook endpoints

Webhook endpoints and their delivery log.

The Webhook object

Attributes

  • id required Type: string
  • url required Type: string
  • events required Type: EventType[]
  • created_at required Type: string

Webhook on its own page

The WebhookEvent object

Attributes

  • id required Type: string

    The same on every retry.

  • type required Type: EventType "webhook.test"

    An event you subscribed to, or webhook.test.

  • created_at required Type: string
  • data required Type: object

    The event's data. job.completed has the first 100 results; items_url pages the rest.

WebhookEvent on its own page

The WebhookEvent object
{
  "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

  • 200
    The endpoints
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /nib/webhooks · curl
curl "https://api.unforged.sh/nib/webhooks" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /nib/webhooks · Python
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())
GET /nib/webhooks · Node.js
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

WebhookCreate

  • url required Type: string
  • events required Type: EventType[]

Returns

  • 201 Type: WebhookCreated
    Created
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 413 Type: Problem
    too_large
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • 503 Type: Problem
    busy, not_ready, org_moving or storage_provisioning (your organisation's storage is still being set up); retry after Retry-After
  • default Type: Problem
    internal
POST /nib/webhooks · curl
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"
  ]
}'
POST /nib/webhooks · Python
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())
POST /nib/webhooks · Node.js
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());
Response 201
{
  "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_id pathrequired Type: string

Returns

  • 204
    Done
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
DELETE /nib/webhooks/{webhook_id} · curl
curl -X DELETE "https://api.unforged.sh/nib/webhooks/{webhook_id}" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
DELETE /nib/webhooks/{webhook_id} · Python
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())
DELETE /nib/webhooks/{webhook_id} · Node.js
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_id pathrequired Type: string

Returns

  • 200 Type: WebhookCreated
    The endpoint and its new secret
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • 503 Type: Problem
    busy, not_ready, org_moving or storage_provisioning (your organisation's storage is still being set up); retry after Retry-After
  • default Type: Problem
    internal
POST /nib/webhooks/{webhook_id}/secret/roll · curl
curl -X POST "https://api.unforged.sh/nib/webhooks/{webhook_id}/secret/roll" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
POST /nib/webhooks/{webhook_id}/secret/roll · Python
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())
POST /nib/webhooks/{webhook_id}/secret/roll · Node.js
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());
Response 200
{
  "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_id pathrequired Type: string
  • limit queryoptional Type: integer
  • cursor queryoptional Type: string

    The previous page's next_cursor.

Returns

  • 200 Type: WebhookDeliveryPage
    A page of attempts (kept 30 days)
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • default Type: Problem
    internal
GET /nib/webhooks/{webhook_id}/deliveries · curl
curl "https://api.unforged.sh/nib/webhooks/{webhook_id}/deliveries" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /nib/webhooks/{webhook_id}/deliveries · Python
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())
GET /nib/webhooks/{webhook_id}/deliveries · Node.js
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_id pathrequired Type: string
  • delivery_id pathrequired Type: string
  • Idempotency-Key headerrequired Type: string

    1–255 printable ASCII characters, kept 24 hours.

Returns

  • 202 Type: Redelivery
    Queued
  • 400 Type: Problem
    invalid_request, invalid_version, idempotency_key_required or invalid_signature
  • 401 Type: Problem
    unauthorized
  • 403 Type: Problem
    permission_denied, org_suspended, or address_not_allowed (your organisation only accepts requests from the addresses it has listed)
  • 404 Type: Problem
    not_found
  • 409 Type: Problem
    conflict, idempotency_conflict, idempotency_in_progress or idempotency_not_replayable
  • 421 Type: Problem
    wrong_region
  • 429 Type: Problem
    rate_limited or quota_exceeded
  • 503 Type: Problem
    busy, not_ready, org_moving or storage_provisioning (your organisation's storage is still being set up); retry after Retry-After
  • default Type: Problem
    internal
POST /nib/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver · curl
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"
POST /nib/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver · Python
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())
POST /nib/webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver · Node.js
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());
Response 202
{
  "status": "queued",
  "redelivery_of": "whd_01j9x3m8v7k2q4r5s6t7u8v9w0"
}