Layers
Partner APIAPI referenceEvents

POST /v1/events

Server-side event forwarding for partners. Sends events into the same SDK ingest pipeline that `in.layers.com` uses, but authenticated with a partner Bearer key.

View as Markdown
POST/v1/events
Phase 1stableidempotent
Auth
Bearer
Scope
events:write

Server-side event forwarding. Use when the customer can't or won't ship the client SDK — your backend posts events into the same pipeline.

This is the partner-API surface for server-side event forwarding. It's distinct from the SDK runtime path (https://in.layers.com/events with X-App-Id SDK auth) — partners authenticate with a normal Bearer key and the events route through the same downstream pipeline (storage in sdk_events, attribution, CAPI relay).

Use this when:

  • The customer's app is a backend service or batch job — there's no client to ship the SDK to.
  • You want to send a synthesized event from a webhook (e.g. Stripe checkout.session.completed).
  • You're backfilling historical events into Layers' attribution.

For partner-issued client-side tracking, use one of the SDK guides.

Request

Send a batch of up to 100 events per call. Each event maps to a single row in sdk_events.

Body
  • projectId
    string (uuid)required
    Project to ingest into.
  • sdkAppId
    stringrequired
    The SDK app the events are attributed to. The CAPI relay uses this to resolve Meta / TikTok configs.
  • events
    object[]required
    1-100 events. Each must conform to the event schema below.

Each event:

events[]
  • name
    stringrequired
    Canonical event name. See [Standard events](/docs/sdk/standard-events).
  • occurredAt
    string (ISO 8601, UTC Z)required
    When the event occurred — set to the original timestamp, not "now". Used for time-series rollups and CAPI dedup.
  • appUserId
    stringoptional
    Stable user handle. Used for cross-device attribution.
  • eventId
    stringoptional
    Idempotency key per event. Reusing the same `eventId` against the same `(projectId, sdkAppId)` is a no-op.
  • properties
    objectoptional
    Free-form event payload. Reserved keys: `revenue`, `currency`, `product_id`, `quantity`.
  • context
    objectoptional
    Standard SDK context fields. `{ ip?, userAgent?, locale?, timezone?, app: { version }, device: { os, osVersion, model } }`.
  • identity
    objectoptional
    PII for advanced matching. `{ email?, phone?, externalId?, fbp?, fbc?, fbLoginId? }`. All fields are SHA-256-hashed at the relay before forwarding to Meta / TikTok.

Idempotency

Two layers:

  1. Per-batch: Idempotency-Key header on the request as usual.
  2. Per-event: eventId field on each event. The relay dedups by (projectId, sdkAppId, eventId) — partners can blindly retry batches without producing duplicate platform-side events.

If you don't supply eventId, Layers generates one server-side from a hash of (name, occurredAt, appUserId, properties).

Example

curl -X POST https://api.layers.com/v1/events \
  -H "Authorization: Bearer $LAYERS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "projectId": "prj_254a4ce1...",
    "sdkAppId": "app_8ffb9410eb0eb848264f8a65",
    "events": [
      {
        "name": "Purchase",
        "occurredAt": "2026-05-08T14:32:11Z",
        "appUserId": "user_42",
        "eventId": "evt_stripe_pi_3KZ4M2A...",
        "properties": {
          "revenue": 19.99,
          "currency": "USD",
          "product_id": "premium_yearly"
        },
        "identity": {
          "email": "alice@example.com"
        }
      }
    ]
  }'
{
  "accepted": 1,
  "skipped": 0,
  "failed": 0,
  "details": [
    {
      "eventId": "evt_stripe_pi_3KZ4M2A...",
      "status": "accepted"
    }
  ]
}

Responses

202Batch accepted. Events queue downstream — storage is synchronous, CAPI forwarding happens within ~1 minute.
{
  "accepted": 1,
  "skipped": 0,
  "failed": 0,
  "details": [
    { "eventId": "evt_…", "status": "accepted" }
  ]
}

status per event: accepted, skipped (duplicate eventId), or failed (validation error — details[].error carries the cause).

400Validation error.
{
  "error": {
    "code": "VALIDATION",
    "message": "events[0].occurredAt must be ISO-8601",
    "requestId": "req_..."
  }
}

Limits

  • 100 events per request. Larger batches are rejected with VALIDATION.
  • occurredAt lookback window: 7 days. Older events return accepted but are not relayed to CAPI (Meta and TikTok both refuse old events). Backfill > 7 days is stored only.
  • Per-app rate limit: 1000 events/sec sustained. Bursts are smoothed; sustained excess returns 429 RATE_LIMITED.

See also

On this page