Webhooks
Webhooks send your quiz events, as signed JSON, to any URL you choose: Make, n8n, Pabbly Connect, Pipedream, or your own server. They carry the same events and data as the Zapier integration. Available on the Starter plan and above, for Shopify and WooCommerce stores.
Add an endpoint
- Go to Integrations → Webhooks → Add endpoint.
- Paste your endpoint URL. It must use
https://and be publicly reachable. - Choose the events to send and, optionally, a single quiz.
- Click Add endpoint, then copy the signing secret. It is shown once.
- Click Send test to deliver a realistic sample event.
Events
| Event | Fires when |
|---|---|
lead.created | A shopper enters their email on a quiz |
quiz.completed | A shopper finishes a quiz |
cart.added | A shopper adds recommended products to their cart from the results |
checkout.started | A shopper starts checkout from the results |
order.created | A shopper who took a quiz places a paid order |
Request format
Each event is an HTTPS POST with a JSON body:
{
"id": "0f8c2d6e-3b1a-4c7e-9f21-5a6b7c8d9e01",
"type": "quiz.completed",
"created_at": "2026-10-06T09:44:02.000Z",
"test": false,
"data": {
"response_id": "7d1e4f2a-9c3b-4e8d-a6f5-1b2c3d4e5f60",
"quiz_id": "3a9b8c7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
"quiz_name": "Hair Discovery Quiz",
"store_name": "ARTINIQ",
"platform": "shopify",
"currency": "AUD",
"shopper_email": "[email protected]",
"shopper_first_name": "Jane",
"shopper_last_name": "Cooper",
"newsletter_opt_in": true,
"answers": [{ "question_id": "…", "question": "What's your hair type?", "answer": "Curly" }],
"answers_text": "What's your hair type?: Curly",
"recommended_products": [{ "product_id": "8012345678901", "name": "Amla Powder", "price": 40, "image_url": "…", "url": "…" }]
}
}
data contains exactly the fields described in Fields you can map.
Headers:
| Header | Value |
|---|---|
X-ShopperQuiz-Event | The event type, e.g. quiz.completed |
X-ShopperQuiz-Event-Id | The event ID. It is the same on every retry, so use it to ignore duplicates |
X-ShopperQuiz-Delivery-Id | This delivery's ID |
X-ShopperQuiz-Signature | t=<unix timestamp>,v1=<hex HMAC-SHA256> |
Verify the signature
Compute an HMAC-SHA256 of <t>.<raw request body> using your signing secret, and compare it to v1. Reject requests whose timestamp is more than 5 minutes old.
const crypto = require('crypto')
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')))
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false
const expected = crypto.createHmac('sha256', secret).update(parts.t + '.' + rawBody).digest('hex')
return parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))
}
To change the secret, click Rotate secret. The old secret stops working immediately.
Responding and retries
- Respond with any
2xxstatus within 10 seconds to acknowledge the event. - Any other response, or a timeout, is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours.
- Respond
410 Goneto turn the endpoint off permanently. - After 50 failed deliveries in a row, the endpoint is turned off automatically. Fix it, then switch it back on in the drawer.
- Redirects are not followed.
Every delivery, with its status and HTTP response, is listed under Recent deliveries, where you can also retry it.
Was this page helpful?