# 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 written to both the database and a per-tenant Redis List. External consumers poll the queue to read events, then issue a separate delete call to remove them.

## How It Works[​](#how-it-works "Direct link to How It Works")

### Feature Flag[​](#feature-flag "Direct link to Feature Flag")

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

### Event Ingestion[​](#event-ingestion "Direct link to Event Ingestion")

When an event is tracked via the SDK, Allegro saves it to the database and — if the feature flag is active — also pushes it onto the tenant's event queue. Each tenant has its own isolated queue.

### Reading Events (`get`)[​](#reading-events-get "Direct link to 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`)[​](#deleting-events-delete "Direct link to 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[​](#api-endpoints "Direct link to API Endpoints")

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

***

### `GET /api/platform/events`[​](#get-apiplatformevents "Direct link to get-apiplatformevents")

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`:**

```json
{
    "data": {
        "count": 2,
        "events": [
            {
                "id": "...",
                "type": "page_view",
                "session_id": "...",
                "audience_member_id": null,
                "url": "https://example.com/page",
                "page_hostname": "example.com",
                "page_path": "/page",
                "user_agent": "Mozilla/5.0 ...",
                "ip_address": "1.2.3.4",
                "event_sequence": 1,
                "created_at": "2026-02-25T18:00:00+00:00"
            }
        ]
    }
}

```

**Empty queue response:**

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

```

***

### `DELETE /api/platform/events`[​](#delete-apiplatformevents "Direct link to delete-apiplatformevents")

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:**

| Parameter | Type        | Required | Description                                                                |
| --------- | ----------- | -------- | -------------------------------------------------------------------------- |
| `count`   | integer ≥ 1 | Yes      | Number of events to remove. Use the `count` value from the `GET` response. |

**Response `204 No Content`** — events removed.

**Response `422 Unprocessable Content`** — `count` is missing or invalid.

***

## Recommended Consumer Pattern[​](#recommended-consumer-pattern "Direct link to Recommended Consumer Pattern")

```text
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.

***

## Configuration[​](#configuration "Direct link to Configuration")

| Config key                    | Default   | Description                                          |
| ----------------------------- | --------- | ---------------------------------------------------- |
| `eventqueue.redis_connection` | `default` | Laravel Redis connection to use for the event queue. |
