Receive Website Visitors by Webhook
Use Case
- Get each resolved website visitor pushed to your own HTTPS endpoint within about 15 minutes, instead of polling
GET /api/v3/events/{tier}orGET /api/v3/events/{tier}/visitors/new - Choose how much identity each row carries by choosing a tier, and what you pay for by choosing a delivery mode
- Verify that every request came from Delivr and was not altered
The webhook is the destination of a pixel export on the continuous schedule. It runs every 5 minutes and delivers only visitors who have already been resolved to a person.
Prerequisites
- Organization API key + secret pair (Authentication), or access to the dashboard.
- A project id and the id of the pixel whose visitors you want.
- An HTTPS endpoint that answers on a public address.
- For
delivery_modenew, new-only delivery enabled on your account. Ask your account manager if the export is refused with a message saying it is not enabled.
Steps
1. Connect the Endpoint
Create a Webhook connection and store its signing secret exactly as in Receive Predictive Signals by Webhook: the endpoint rules, the signing_secret (returned at create and again only when you regenerate it), and Test connection are the same. This guide does not repeat them. Test connection is in the dashboard only; there is no API route for it. The same connection can serve both kinds of export.
2. Create the Pixel Export
Create an export whose source is a pixel, whose schedule is continuous, and whose destination is the Webhook connection:
curl -X POST "https://api.delivr.ai/api/v1/exports" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "X-Api-Secret: YOUR_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"project_id": "YOUR_PROJECT_ID",
"name": "Resolved visitors push",
"query": {
"sourceType": "pixel",
"sourceId": "YOUR_PIXEL_ID"
},
"destination": {
"type": "webhook",
"integration_id": "6c1f0e2a-8d3b-4c5e-9f10-2a3b4c5d6e7f",
"tier": "resolution_full_profile",
"delivery_mode": "new"
},
"schedule": "continuous"
}'schedule continuous is accepted only for a pixel source with exactly one destination, a Webhook, and a pixel export's Webhook destination is accepted only on the continuous schedule. Do not add additional_destinations. The Webhook destination needs both of these:
| Field | Description |
|---|---|
destination.tier | Required. resolution_hem, resolution_hem_pte or resolution_full_profile: the same tiers as the Events API v3. The tier fixes the columns each row carries and the rate you are billed at. web_event is not available, because it has no resolved identity. |
destination.delivery_mode | Required, with no default. all delivers every resolved event row. new delivers only people not yet delivered to the project at this tier this UTC calendar month, and bills each person once. |
Do not send include_email. The tier decides the columns, and only resolution_full_profile carries email. A missing or unknown tier, a missing delivery_mode, or include_email is refused with a 400 that names the field.
An export on the continuous schedule has no run_time: it is due every 5 minutes. It cannot carry a record limit, because each run delivers everything resolved in its window. Audience filters apply as they do on other pixel exports.
What Is Delivered, and When
Each run covers the events from the end of the previous successful run up to about 10 minutes ago, so a visitor reaches your endpoint 10 to 15 minutes after the event.
- Resolved visitors only. There are no unresolved
web_eventrows. A visitor who resolves after their window was delivered is not pushed afterwards; their events remain available fromGET /api/v3/events/{tier}. - No backfill. The first run covers events from 15 to 10 minutes before it runs, which can include a few minutes before the export was created. Nothing older is delivered; use the Events API for history.
- Catching up. After a failure or a pause, a run covers at most one hour of events, so a longer gap is delivered over several runs, oldest first.
- Order. Rows within a window are sorted by event time, then
event_id, and an event appears once. - Opt-outs. Identifiers of people who have opted out are removed from a row before it is sent.
In new mode a person appears once per window, in their first row of that window. Anyone already delivered to the project at this tier this month is left out. This is the same once-per-month ledger as GET /api/v3/events/{tier}/visitors/new, so a person you pulled from that route this month is not pushed, and a person pushed here is not returned by that route again this month. See what "month" means.
What Each Request Carries
A run sends POST requests with Content-Type: application/json, in batches of at most 100 rows and 256 KiB:
{
"type": "events.batch",
"export_id": "a8d1c4e2-5b7f-4e9a-8c3d-0f1e2d3c4b5a",
"window": { "start_ms": 1792022400000, "end_ms": 1792023000000 },
"data": [
{
"event_id": "0b6f9c1e-2d3a-4f5b-8a7c-9d0e1f2a3b4c",
"event_type": "page_view",
"ts_millis": 1792022461234,
"hem": "3f1b6e0c9a8d7e5f4a3b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f",
"company_domain": "acme.example"
}
]
}| Field | Description |
|---|---|
type | events.batch for a batch of visitors, webhook.test for a test event. Route on it if one endpoint receives several kinds. |
export_id | The export that sent the batch. |
window.start_ms, window.end_ms | The events window in Unix milliseconds, start included and end excluded. |
data | The rows, with the columns of the export's tier, the same as GET /api/v3/events/{tier} returns. The row above is shortened; call GET /api/v3/events/{tier}/schema for the columns of your tier. |
Requests carry the same three Standard Webhooks headers, signed the same way, as in the signals guide. Its Verify the Signature section has the steps, the Python and Node.js code and the test vector; use it unchanged.
The webhook-id of a batch is <export_id>:<window start ms>:<batch index>:<content hash>, where the content hash is 16 hex characters, for example a8d1c4e2-5b7f-4e9a-8c3d-0f1e2d3c4b5a:1792022400000:0:3f9a1c0e7b2d5a48. A retry or resend that carries the same rows carries the same id. A batch whose rows changed, for example because an identity resolved after the first attempt, gets a new id, so deduplicating on webhook-id never drops rows you have not seen.
Respond, Retries, and Duplicates
Acknowledgement, the 15-second limit, which statuses are retried, the wait between attempts and Retry-After are the same as in Respond, Retries, and Duplicates in the signals guide.
What differs is what happens after a batch is refused or its retries run out:
- The run fails and nothing after that batch is sent. The next run, 5 minutes later, resends the failed run's exact window, with the same
window. Batches whose rows are unchanged keep theirwebhook-idand body. After that it carries on from the end of that window. - In
newmode, people from batches you already acknowledged are not sent again. A batch whose people were all acknowledged is skipped, and the rest are resent under their original ids. - Deduplicate on
webhook-id. A second arrival of an id you already processed is a duplicate: answer2xxwithout processing it again. - Answer
2xxquickly. Batches are sent one after another, so a slow endpoint delays every batch behind it and the next run.
Delivr tells you when an export starts failing. It does not notify on every failed run, and sends nothing for a run that completes.
As with other scheduled exports, an export that delivers no rows for several days in a row is paused automatically. Resume it to start again; the runs after it catch up from where it stopped, an hour of events at a time.
Billing
You are billed only for what your endpoint acknowledged with a 2xx, at the rate of the export's tier.
new: one unit per person, once per project, per tier, per UTC calendar month, the first time they are acknowledged. People in a batch that was refused are not billed and stay new for the next run.all: one unit per row acknowledged in a run that completes. A run that fails bills nothing: the next run resends its window, and its batches are billed once, when that run completes.
new shares its ledger with GET /api/v3/events/{tier}/visitors/new, and all bills every row, as the regular tier routes do. See Rate Limits and Metering.
Updated about 1 hour ago
