Layers

GET /v1/projects/:projectId/events

Query the SDK event stream - filtered, paginated, PII-redacted by default.

View as Markdown
GET/v1/projects/:projectId/events
Phase 1stable
Auth
Bearer
Scope
events:read

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.

Path
  • projectId
    stringrequired
    Project ID.
Query
  • appId
    stringoptional
    Filter to a single SDK app.
  • eventNames
    string[]optional
    Comma-separated event names (e.g. `purchase_success,add_to_cart`). Omitted = all events.
  • userId
    stringoptional
    Filter to a single `app_user_id`.
  • since
    string (ISO 8601, UTC Z)optional
    Inclusive lower bound on `occurred_at`.
  • until
    string (ISO 8601, UTC Z)optional
    Exclusive upper bound on `occurred_at`.
  • cursor
    stringoptional
    Opaque pagination token.
  • limit
    integeroptionaldefault: 50
    Page size, 1–200.

Example request

curl "https://api.layers.com/v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/events?eventNames=purchase_success&since=2026-04-18T00:00:00Z&limit=25" \
  -H "Authorization: Bearer lp_..."
const { items, nextCursor } = await layers.events.list(
  "prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  {
    eventNames: ["purchase_success"],
    since: "2026-04-18T00:00:00Z",
    limit: 25,
  }
);
result = layers.events.list(
    project_id="prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
    event_names=["purchase_success"],
    since="2026-04-18T00:00:00Z",
    limit=25,
)

Response

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.

Big-int identifiers in properties come back as strings. Postgres JSONB stores numbers without precision limits, but JSON's number type cannot represent integers above 2^53 - 1 (9_007_199_254_740_991). Meta ad_ids (17–19 digits) and TikTok event_ids (17-digit Snowflake) routinely exceed that. To prevent silent precision loss, the endpoint stringifies any integer property value outside ±(2^53 − 1) before serializing — so a properties.ad_id you sent as a number will read back as the same digits in a string. Floats (price 4.99, percentile 0.043) and small integers stay numeric. Recommendation: send big-int IDs as strings from the SDK so producer + consumer agree on the type.

200OK
{
  "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..."
}

Errors

StatusCodeWhen
422VALIDATIONBad since/until, unknown appId, invalid cursor.
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks events:read.
404NOT_FOUNDProject does not exist.
429RATE_LIMITEDRead budget exhausted.

See also

On this page