Webhook Events#

This page documents the envelope shared by every webhook and the data payload of each event type. For delivery, signing, and retry behaviour see the Webhooks guide.

Event envelope#

Every webhook body is an envelope wrapping an event-specific data object:

event_idstring

Stable unique id of the event. Mirrors the X-Evolu-Event-Id header — deduplicate on it.

event_typestring

The event type — entry.received, entry.completed, or entry.evaluated.

campaign_idstring<uuid>

The campaign the event belongs to.

created_atstring<date-time>

ISO 8601 timestamp of when the event was generated.

livemodeboolean

true for production data, false for entries created with a test-mode key.

api_versionstring

The webhook schema version, e.g. 2026-04-30.

requestobject

Correlation context — { "idempotency_key": "..." }, echoing the key from the original entry submission.

dataobject

The event-specific payload — documented per event below.

entry.received

Dispatched immediately after an entry is accepted for asynchronous processing — a delivery confirmation ("pong"). An entry rejected at submission is not processed asynchronously (its rejection is returned synchronously in the 202 response), so it does not receive an entry.received event.

{  "event_id": "evt_entry_received_01905a3b7c8d7f00a1b2c3d4e5f6a7b8",  "event_type": "entry.received",  "campaign_id": "0190559e-1f00-7a00-bc00-1a2b3c4d5e6f",  "created_at": "2026-04-30T14:30:01Z",  "livemode": true,  "api_version": "2026-04-30",  "request": { "idempotency_key": "550e8400-e29b-41d4-a716-446655440000" },  "data": {    "entry_id": "01905a3b-7c8d-7f00-a1b2-c3d4e5f6a7b8",    "enrollment_id": "0190559e-aa00-7a00-bc00-1a2b3c4d5e6f",    "status": "PENDING"  }}
data.entry_idstring<uuid>

The accepted entry.

data.enrollment_idstring<uuid>

The participant's campaign enrollment.

data.statusstring

The entry status at acceptance time — typically PENDING.

entry.completed

Dispatched once fiscal validation and fraud checks finish. The entry has reached a terminal processing state.

{  "event_type": "entry.completed",  "data": {    "entry_id": "01905a3b-7c8d-7f00-a1b2-c3d4e5f6a7b8",    "enrollment_id": "0190559e-aa00-7a00-bc00-1a2b3c4d5e6f",    "status": "REJECTED",    "reason_code": "NOT_ELIGIBLE"  }}
data.statusstring

APPROVED or REJECTED.

data.reason_codestring | null

A machine-readable rejection reason when status is REJECTED; null otherwise.

entry.evaluated

Dispatched after the campaign's mechanics are evaluated. Carries one item per evaluated mechanic, with its proposed outcome.

{  "event_type": "entry.evaluated",  "data": {    "entry_id": "01905a3b-7c8d-7f00-a1b2-c3d4e5f6a7b8",    "enrollment_id": "0190559e-aa00-7a00-bc00-1a2b3c4d5e6f",    "mechanics": [      {        "mechanic_id": "0190559e-cc00-7a00-bc00-1a2b3c4d5e6f",        "mechanic_type": "lucky_numbers",        "status": "APPROVED",        "reason_code": null,        "outcome": {          "quantity": 2,          "series_count": 10,          "numbers": [            { "series": "0", "order_number": "12345" },            { "series": "1", "order_number": "67890" }          ]        }      }    ]  }}

The lucky_numbers outcome above is from a campaign in automatic generation mode — Evolu generated the numbers and ships them in outcome.numbers. A manual-mode campaign omits numbers and you generate them yourself:

{  "mechanic_type": "lucky_numbers",  "status": "APPROVED",  "outcome": { "quantity": 5, "series_count": 10 }}
data.mechanicsarray

One entry per evaluated mechanic.

data.mechanics[].mechanic_typestring

The mechanic type. API-channel entries are evaluated against lucky_numbers and instant_prize.

data.mechanics[].statusstring

APPROVED or REJECTED for that mechanic.

data.mechanics[].outcomeobject

The proposed outcome, specific to the mechanic.

  • lucky_numbers — always carries { "quantity": N, "series_count": S }: how many lucky numbers the participant qualified for, and the campaign's series range (valid series values are 0 to S - 1). In automatic generation mode it additionally carries numbers — the numbers Evolu generated (see below). In manual mode numbers is absent and you generate them yourself.
  • instant_prize — for a win, { "won": true, "prize_definition_id": "…", "prize_name": "…", "award_status": "AWARDED" }; for a declared loss, { "won": false }. This echoes the win you registered, confirmed once the entry passed processing.
data.mechanics[].outcome.numbersarray

Automatic generation mode only. The lucky numbers Evolu generated and assigned to the participant, ordered by (series, order_number). Empty when none were assigned (e.g. the mechanic was rejected). Each item is { "series": "0", "order_number": "12345" } — the series (a non-negative integer string, 0 to series_count - 1) and the order_number (a 5-digit string). These are the participant's official numbers; there is nothing to submit back.

Lucky numbers — automatic vs. manual generation

The lucky-numbers outcome depends on the campaign's generation mode:

  • Automatic — Evolu generates the numbers during evaluation and delivers them in outcome.numbers. Persist them; there is nothing to submit back.
  • Manual — Evolu computes the qualified quantity but does not generate the numbers. Use outcome.quantity and outcome.series_count to generate them, then submit them via the Lucky Numbers endpoint.