Using the TypeScript SDK
If you’re using the TypeScript SDK (v0.2.0+), it provides typed payloads and helper functions that handle event parsing and state checks for you.
parseWebhookEvent() validates the payload shape and returns a fully typed LegitmarkWebhookEvent. The helper functions check specific state combinations so you don’t need to memorize them:
| Helper | Returns true when |
|---|---|
isAuthentic(event) | COMPLETE + APPROVED — item is genuine |
isCounterfeit(event) | COMPLETE + REJECTED — item is not authentic |
isCancelled(event) | CANCELLED — SR was cancelled |
needsResubmission(event) | media_rejected — images need re-upload |
isQcApproved(event) | QC + APPROVED — photos passed quality review |
isAuthenticationInProgress(event) | UNDERWAY + ASSIGNED — authenticator working |
Basic Handler
If you’re not using the TypeScript SDK, here are examples in multiple languages.Best Practices
Return 200 immediately
Return 200 immediately
Acknowledge the webhook as fast as possible. If your processing logic is complex or calls external services, queue the work and return
200 first.Handle duplicate deliveries
Handle duplicate deliveries
You may receive the same event more than once. Build a deduplication key from the payload fields that uniquely identify the event:
state_change:sr_uuid+state.primary+state.supplement(the same SR emits multiple state changes)media_rejected:sr_uuid+event_typeinvalidate_sr:sr_uuid+event_type
In production, use a persistent store (database, Redis) instead of an in-memory
Set so deduplication survives restarts.Log the raw payload
Log the raw payload
Store the full webhook payload for debugging. When something goes wrong, having the original data makes diagnosis much faster.
Handle unknown event types gracefully
Handle unknown event types gracefully
Don’t reject webhooks with unrecognized
event_type values. New event types may be added in the future. Return 200 for any event, even ones you don’t handle yet.Summary
Your webhook handler should:- Use an HTTPS endpoint and return 200 within 30 seconds
- Be idempotent — use payload fields to deduplicate (not just
sr_uuid, since one SR emits multiple events) - Accept unknown event types without failing (new events may be added)
- Process complex logic asynchronously to avoid timeouts