# GET /v1/projects/:projectId/events (/docs/api/reference/telemetry/events)



<Endpoint method="GET" path="/v1/projects/:projectId/events" auth="Bearer" scope="events:read" phase="1" />

Returns events the client SDK posted to `in.layers.com/l/events`, in reverse-chronological order. Use it to render a live activity feed, debug SDK wiring during install, or export raw telemetry into your own warehouse.

Identifiers that could reveal a user — `email`, `phone`, `ip`, and common PII property keys — are redacted to `"[redacted]"` by default. Create the key with the `events:read+pii` sub-scope and Layers returns the raw values. Keys without that sub-scope cannot opt in per-request.

<Parameters
  title="Path"
  rows="[
  { name: 'projectId', type: 'string', required: true, description: 'Project ID.' },
]"
/>

<Parameters
  title="Query"
  rows="[
  { name: 'appId', type: 'string', description: 'Filter to a single SDK app.' },
  { name: 'eventNames', type: 'string[]', description: 'Comma-separated event names (e.g. `purchase_success,add_to_cart`). Omitted = all events.' },
  { name: 'userId', type: 'string', description: 'Filter to a single `app_user_id`.' },
  { name: 'since', type: 'string (ISO-8601)', description: 'Inclusive lower bound on `occurred_at`.' },
  { name: 'until', type: 'string (ISO-8601)', description: 'Exclusive upper bound on `occurred_at`.' },
  { name: 'cursor', type: 'string', description: 'Opaque pagination token.' },
  { name: 'limit', type: 'integer', description: 'Page size, 1–200.', default: '50' },
]"
/>

## Example request [#example-request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```bash
    curl "https://api.layers.com/v1/projects/prj_01HX9Y7K8M2P4RSTUV56789AB/events?eventNames=purchase_success&since=2026-04-18T00:00:00Z&limit=25" \
      -H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..."
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts
    const { items, nextCursor } = await layers.events.list(
      "prj_01HX9Y7K8M2P4RSTUV56789AB",
      {
        eventNames: ["purchase_success"],
        since: "2026-04-18T00:00:00Z",
        limit: 25,
      }
    );
    ```
  </Tab>

  <Tab value="Python">
    ```python
    result = layers.events.list(
        project_id="prj_01HX9Y7K8M2P4RSTUV56789AB",
        event_names=["purchase_success"],
        since="2026-04-18T00:00:00Z",
        limit=25,
    )
    ```
  </Tab>
</Tabs>

## Response [#response]

<Callout type="info">
  `receivedAt` currently mirrors `occurredAt` (single-timestamp storage). A future schema bump will track ingest receipt independently for client-skew debugging. Today, treating either field interchangeably is safe.
</Callout>

<Response status="200" description="OK">
  ```json
  {
    "items": [
      {
        "eventId": "evt_01HXA9Z2R3S4T5U6V7W8X9Y0A",
        "appId": "sdk_01HXA1NPQR5TVWXYZABCDEFGH",
        "userId": "user_a7f3c1d9",
        "eventName": "purchase_success",
        "occurredAt": "2026-04-18T19:42:08.214Z",
        "receivedAt": "2026-04-18T19:42:08.214Z",
        "platform": "ios",
        "sdkVersion": "1.4.0",
        "properties": {
          "order_id": "order_81422",
          "value": 18.5,
          "currency": "USD",
          "items": [{ "sku": "latte-12oz", "qty": 2 }]
        },
        "identity": {
          "email": "[redacted]",
          "phone": "[redacted]",
          "ip": "[redacted]"
        }
      }
    ],
    "nextCursor": "evt_01HXA9Z1QPONM..."
  }
  ```
</Response>

## Errors [#errors]

| Status | Code              | When                                                  |
| ------ | ----------------- | ----------------------------------------------------- |
| 422    | `VALIDATION`      | Bad `since`/`until`, unknown `appId`, invalid cursor. |
| 401    | `UNAUTHENTICATED` | Missing or invalid key.                               |
| 403    | `FORBIDDEN_SCOPE` | Key lacks `events:read`.                              |
| 404    | `NOT_FOUND`       | Project does not exist.                               |
| 429    | `RATE_LIMITED`    | Read budget exhausted.                                |

## See also [#see-also]

* [`GET /v1/projects/:projectId/conversions`](/docs/api/reference/telemetry/conversions) — aggregated rollup
* [`GET /v1/projects/:projectId/users/:appUserId/signal`](/docs/api/reference/telemetry/user-signal) — per-user state
