Webhooks
Receive real-time notifications when submissions are scored, jobs are completed, or other events occur.
Webhook Endpoints
Create Endpoint
POST /v1/webhook_endpoints| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | HTTPS URL to receive events |
events | string[] | yes | Event types to subscribe to |
Available events: submission.approved, submission.rejected, job.completed, job.cancelled
List Endpoints
GET /v1/webhook_endpointsGet Endpoint
GET /v1/webhook_endpoints/{endpoint_id}Update Endpoint
PATCH /v1/webhook_endpoints/{endpoint_id}Delete Endpoint
DELETE /v1/webhook_endpoints/{endpoint_id}Rotate Secret
POST /v1/webhook_endpoints/{endpoint_id}/rotate_secretWebhook Events
List Events
GET /v1/webhook_eventsReplay Event
POST /v1/webhook_events/{event_id}/replayRe-delivers a webhook event to all subscribed endpoints.
Signature Verification
Every webhook delivery includes two headers:
X-FirstHand-Signature— Combined timestamp and HMAC digestX-FirstHand-Event-Id— Event ID for idempotency and debugging
X-FirstHand-Signature: t=1710500000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
X-FirstHand-Event-Id: evt_01JQ...Verification steps:
- Parse the
t=(timestamp) andv1=(signature) components - Construct the signed payload:
{timestamp}.{raw_body} - Compute HMAC-SHA256 with your webhook secret
- Compare using a timing-safe function
- Reject if timestamp is older than 5 minutes
import { verifyWebhookSignature } from '@firsthand/sdk';
verifyWebhookSignature(rawBody, signatureHeader, webhookSecret);from firsthand import verify_webhook_signature
verify_webhook_signature(raw_body, signature_header, webhook_secret)Retry Policy
Failed deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 8 hours |
After 6 failed attempts, the event is marked as failed and can be manually replayed.