Contents

Webhook endpoints

Hear when a check finishes.

The LoupeWebhook object

Attributes

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

LoupeWebhook on its own page

The LoupeWebhookEvent object

What your endpoint receives. type says which event it is, and data has the check: status_url is the check to fetch for the whole result.

LoupeWebhookEvent on its own page

List Loupe's webhook endpoints

GET /loupe/webhooks

Needs loupe:settings:manage: Loupe settings: webhooks

Loupe's endpoints only.

Returns

  • 200 Type: LoupeWebhookList
    The endpoints
  • 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 /loupe/webhooks · curl
curl "https://api.unforged.sh/loupe/webhooks" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
GET /loupe/webhooks · Python
import requests

r = requests.get(
    "https://api.unforged.sh/loupe/webhooks",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json())
GET /loupe/webhooks · Node.js
const res = await fetch('https://api.unforged.sh/loupe/webhooks', {
  method: 'GET',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());

Create a Loupe webhook endpoint

POST /loupe/webhooks

Needs loupe:settings:manage: Loupe settings: webhooks

Like POST /nib/webhooks (the same signing, retries, delivery log and connection test), for Loupe's events: loupe.check.completed and loupe.check.failed. The secret is shown once. No Idempotency-Key: a retry creates another endpoint.

Request body

LoupeWebhookCreate

  • url required Type: string
  • events required Type: "loupe.check.completed" "loupe.check.failed"[]

Returns

  • 201 Type: LoupeWebhookCreated
    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)
  • 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
  • default Type: Problem
    internal
POST /loupe/webhooks · curl
curl -X POST "https://api.unforged.sh/loupe/webhooks" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14" \
  -H "Content-Type: application/json" \
  -d '{}'
POST /loupe/webhooks · Python
import requests

r = requests.post(
    "https://api.unforged.sh/loupe/webhooks",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
    json={},
)
print(r.json())
POST /loupe/webhooks · Node.js
const res = await fetch('https://api.unforged.sh/loupe/webhooks', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14', 'Content-Type': 'application/json' },
  body: JSON.stringify({}),
});
console.log(await res.json());

Delete a Loupe webhook endpoint

DELETE /loupe/webhooks/{webhook_id}

Needs loupe:settings:manage: Loupe settings: webhooks

404 for an unknown endpoint and for another product's.

Parameters

  • webhook_id pathrequired Type: string

    The endpoint's id.

Returns

  • 204
    Deleted
  • 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
DELETE /loupe/webhooks/{webhook_id} · curl
curl -X DELETE "https://api.unforged.sh/loupe/webhooks/{webhook_id}" \
  -H "Authorization: Bearer $UNFORGED_KEY" \
  -H "Unforged-Version: 2026-10-14"
DELETE /loupe/webhooks/{webhook_id} · Python
import requests

r = requests.delete(
    "https://api.unforged.sh/loupe/webhooks/{webhook_id}",
    headers={"Authorization": f"Bearer {UNFORGED_KEY}", "Unforged-Version": "2026-10-14"},
)
print(r.json())
DELETE /loupe/webhooks/{webhook_id} · Node.js
const res = await fetch('https://api.unforged.sh/loupe/webhooks/{webhook_id}', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${process.env.UNFORGED_KEY}`, 'Unforged-Version': '2026-10-14' },
});
console.log(await res.json());