Lookup notifications
Get notified when a lookup finishes
Getting started has you poll GET /v1/profiles/{businessId} until it stops returning 202. If you'd rather not poll, register a URL once and Lumen Sieve calls it for you the moment each resolve completes.
POST /v1/profiles/lookup completions. It does not cover ongoing monitoring of a business for later changes — that's a separate, not-yet-released feature.1. Register a URL
Requires the webhooks:manage scope. You choose which of the two events you want — most integrations want both.
curl -X POST https://api.lumen.com/v1/webhooks/subscriptions \
-H "Authorization: Bearer lumen_test_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.example.com/lumen/webhook",
"event_types": ["profile.resolved", "profile.failed"]
}'{
"secret": "whsec_5f2a...c19b",
"subscription": {
"id": "sub_...",
"url": "https://yourapp.example.com/lumen/webhook",
"event_types": ["profile.resolved", "profile.failed"],
"enabled": true,
"created_at": "2026-08-01T00:00:00Z"
}
}secret is shown exactly once, the same as an API key. Store it — you need it to verify deliveries (below).2. What you receive
When a lookup you started finishes, Lumen Sieve sends one POST to your URL — success or failure, never both for the same lookup:
{
"event_id": "evt_...",
"event_type": "profile.resolved",
"business_id": "616de6d1-7732-4ece-a551-5e3187923aa8",
"customer_id": "acc_...",
"profile_id": "ebbbe097-d475-4051-b358-8c638c85b32c",
"identifier": "Blispa Barbershop & Spa",
"status": "resolved",
"resolved_at": "2026-08-01T00:30:38Z"
}A profile.failed event carries the same shape with status: "failed" and an error field instead of profile_id. Either way, fetch GET /v1/profiles/{business_id} to get the actual profile — the event is just the notification, not the payload.
3. Verify the signature
Every delivery carries an X-LUMEN-Signature header: t=<unix timestamp>,v1=<hex HMAC-SHA256>, computed over the raw request body using the secret from step 1. Recompute it yourself and compare — never trust a delivery without checking this.
import hashlib, hmac, time
def verify(payload_bytes: bytes, signature_header: str, secret: str, tolerance_seconds: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in signature_header.split(","))
timestamp, signature = parts["t"], parts["v1"]
if abs(time.time() - int(timestamp)) > tolerance_seconds:
return False # too old — possible replay
expected = hmac.new(secret.encode(), payload_bytes, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)Retries and failed deliveries
If your endpoint doesn't respond with a 2xx, Lumen Sieve retries with backoff up to a configured attempt limit before giving up. Everything is inspectable and replayable after the fact:
| Endpoint | Purpose |
|---|---|
GET /v1/webhooks/deliveries | List recent delivery attempts across all your subscriptions |
GET /v1/webhooks/deliveries/{id} | Inspect one delivery — status, response, attempt count |
POST /v1/webhooks/deliveries/{id}/retry | Manually retry one delivery on demand |
GET /v1/webhooks/dead-letter | List deliveries that exhausted all retries |
POST /v1/webhooks/dead-letter/{id}/replay | Re-attempt a dead-lettered delivery |
POST /v1/webhooks/test | Send a synthetic test event to a URL before going live |
Managing your subscription
| Endpoint | Purpose |
|---|---|
GET /v1/webhooks/subscriptions | List your registered URLs |
DELETE /v1/webhooks/subscriptions/{id} | Remove a URL — no more deliveries to it |
