Skip to main content

Event Queue

The event queue is a Redis List-based pipeline for delivering events from Allegro to external consumers in real time. When enabled, every event tracked by the SDK is pushed onto a per-tenant Redis List. External consumers poll the queue to read events, then issue a separate delete call to remove them.

Each destination that receives a tracked event is independent, and each is controlled by its own feature flag: the Redis queue described here is gated by UseRedisEvents, while writing events to the database is gated separately by StoreEventsInDatabase. A tenant streaming events off Redis can therefore turn off database storage without affecting the queue.

How It Works

Feature Flag

The queue is opt-in per tenant via the UseRedisEvents Pennant feature flag.

Event Ingestion

When an event is tracked via the SDK and the UseRedisEvents flag is active, Allegro pushes it onto the tenant's event queue. Each tenant has its own isolated queue. Storing the same event in the database is handled independently by the StoreEventsInDatabase flag, so the queue receives events whether or not database storage is enabled.

Reading Events (get)

Consumers call GET /api/platform/events to read up to 10,000 events from the front of the queue. This is a non-destructive read — events remain in the queue until you explicitly delete them.

Deleting Events (delete)

After processing the events, call DELETE /api/platform/events?count=N where N is the count value returned by the preceding GET. Passing the exact count prevents a race condition: any events that arrived between the GET and the DELETE are left untouched and will be returned on the next GET.


API Endpoints

All endpoints are under the /api/platform prefix and require Sanctum authentication (api middleware + auth:sanctum).


GET /api/platform/events

Read up to 10,000 events from the front of the queue. Non-destructive — events are not removed until DELETE is called.

If the UseRedisEvents feature flag is not active for the current tenant, returns an empty result.

Response 200 OK:

{
"data": {
"count": 2,
"events": [
{
"id": "9b1f...",
"type": "page_view",
"session_id": "7c2a...",
"audience_member_id": "4d8e...",
"device_id": "1a6b...",
"page_hostname": "example.com",
"page_path": "/articles/hello",
"page_title": "Hello, world",
"property_slug": "magazine",
"utm_source": "newsletter",
"user_agent": "Mozilla/5.0 ...",
"ip_address": "1.2.3.4",
"data": { "any": "event-specific values" },
"created_at": "2026-02-25T18:00:00+00:00",
"updated_at": "2026-02-25T18:00:00+00:00"
}
]
}
}

The exact set of keys varies per event — see Event Payload below for the full field list and which fields are always present.

Empty queue response:

{
"data": {
"count": 0,
"events": []
}
}

Event Payload

Each entry in the events array is the event as it was recorded at tracking time, serialized directly from its stored attributes. The queue carries this raw event — not an enriched or derived form.

Fields are included only when set

A field appears in the payload only if it was set on the event at tracking time — not based on whether the value is meaningful. Required and always-derived fields are set on every event (though some, such as user_agent and ip_address, may be null). Optional fields are set only when the SDK sent them, or when they were derived from data that was sent; an optional field that was never sent is omitted entirely rather than emitted as null. (A field a client explicitly sends as null is still present, as null.) As a result, payloads vary in shape from one event to the next.

Always present

FieldTypeDescription
iduuidUnique event identifier.
typestringEvent type (e.g. page_view, click, purchase).
session_iduuidThe customer session the event belongs to.
user_agentstring | nullRequest User-Agent header. null if the request had none.
ip_addressstring | nullOriginating IP address. null if it could not be determined.
property_slugstring | nullSlug of the property the request was made on. null when the request used a plain organization host with no property context.
created_atISO 8601 stringWhen the event was recorded.
updated_atISO 8601 stringEqual to created_at for queued events.

Present when set

These appear only when the tracking call supplied them (directly or via the tracked URL).

FieldTypeSource
audience_member_iduuidThe authenticated audience member, when known.
device_iduuidThe originating device, when known.
page_hostnamestringHost parsed from the tracked URL.
page_pathstringPath parsed from the tracked URL.
page_titlestringPage title.
utm_sourcestringUTM parameter parsed from the tracked URL's query string.
utm_mediumstringUTM parameter parsed from the tracked URL's query string.
utm_campaignstringUTM parameter parsed from the tracked URL's query string.
utm_termstringUTM parameter parsed from the tracked URL's query string.
utm_contentstringUTM parameter parsed from the tracked URL's query string.
content_typestringType of content the event relates to.
content_idstringIdentifier of the content the event relates to.
publisherstringPublisher associated with the content.
refererstringReferring URL.
object_typestringType of the object the event acted on.
object_idstringIdentifier of the object the event acted on.
contextstringFree-form context label supplied by the integration.
conversion_pv_idstringPage-view event ID attributed as the conversion source.
url_in_clickstringDestination URL for click / link-click events.
dataobjectArbitrary event-specific key/value data, returned as a nested object.
note
There is no url field

The tracked page URL is never stored or emitted as-is. It is broken apart into page_hostname, page_path, and any utm_* query parameters, and the original URL is then discarded.

Raw, not enriched

The queue reflects the event exactly as captured. Attributes populated by later, asynchronous processing — such as the parsed user-agent breakdown stored in user_agent_parsed — are not part of the queue payload.


DELETE /api/platform/events

Remove exactly count events from the front of the queue. The count parameter is required and must match the count returned by the preceding GET call — this prevents accidentally deleting events that arrived after the read.

Query parameters:

ParameterTypeRequiredDescription
countinteger ≥ 1YesNumber of events to remove. Use the count value from the GET response.

Response 204 No Content — events removed.

Response 422 Unprocessable Contentcount is missing or invalid.


loop:
GET /api/platform/events
if count == 0 → sleep, continue

process each event

DELETE /api/platform/events?count={count}

Read all events first, process them, then delete — passing the exact count from the GET response. Because GET is non-destructive, events are safe to re-read if your consumer needs to retry before issuing the DELETE. Using the explicit count ensures events that arrived between your GET and DELETE are never accidentally removed.