Webhooks
Register HTTP endpoints to receive real-time notifications when submission status changes. All deliveries are signed with HMAC-SHA256.
Events
| Event | Trigger |
|---|---|
submission.received | New submission created |
submission.accepted | Store approved the order |
submission.rejected | Submission rejected |
submission.completed | Submission closed |
Payload format
{
"event": "submission.received",
"created_at": "2026-04-23T00:00:00.000Z",
"data": {
"submission": {
"id": "uuid",
"store_id": "uuid",
"customer_email": "customer@example.com",
"customer_name": "Jane Smith",
"status": "pending_review",
"created_at": "2026-04-23T00:00:00.000Z",
"quoted_at": null,
"completed_at": null
},
"items": [
{
"id": "uuid",
"raw_input": "4x Counterspell NM",
"quantity": 4,
"condition": "NM",
"quoted_price_cents": null
}
]
}
}Verifying signatures
Each delivery includes an X-Signature: sha256=<hex> header. Verify it against the raw request body using your subscription secret:
import { createHmac } from "crypto";
function verifySignature(rawBody: string, secret: string, signature: string): boolean {
const expected = "sha256=" + createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
// Timing-safe comparison
const a = Buffer.from(expected);
const b = Buffer.from(signature);
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
return diff === 0;
}Retry schedule
On non-2xx responses, deliveries are retried with exponential backoff:
- Attempt 1: immediate
- Attempt 2: +1 minute
- Attempt 3: +5 minutes
- Attempt 4: +30 minutes
- Attempt 5: +3 hours
After 5 failures, the delivery is marked failed.
Register a webhook
POST /api/v1/webhooks
Authorization: Bearer bk_YOUR_KEY
{
"url": "https://yoursite.com/webhooks/buylist",
"events": ["submission.received", "submission.completed"]
}
// Response includes secret (shown once):
{
"id": "uuid",
"url": "https://yoursite.com/webhooks/buylist",
"events": [...],
"secret": "your-hmac-secret",
"created_at": "..."
}