Collect analytics events

Send browser and backend events, pageviews, identity updates, and group traits.

POST https://api.embrasure.ai/collect/v1/batch

Start with the JavaScript quickstart, or submit JSON directly. The OpenAPI schema provides the native request and response models.

Authentication

Send the public project_key in the JSON body. It permits event submission only, and resolves the workspace, project, and environment on the server. Do not send workspace IDs or storage destinations.

Requests that send an Origin header must match one of the project's allowed origins exactly. Backend requests can also send Authorization: Bearer sk_... with an unrevoked server key from the same environment as the project_key to mark their source as trusted. An Authorization header that does not validate returns 401; it does not fall back to an untrusted request. Public keys and browser origins do not authenticate individual users or grant report access. Do not put server keys in browser code.

For untrusted requests, the collector derives context.browser, os, device, and geo from the request and replaces any client-sent values. Trusted requests keep the context they send.

Request

Send Content-Type: application/json. To compress the body, gzip it and send Content-Encoding: gzip; other encodings return 415.

curl --request POST 'https://api.embrasure.ai/collect/v1/batch' \
  --header 'Content-Type: application/json' \
  --data '{
    "project_key": "pk_your_project_key",
    "events": [{
      "id": "signup_started:request_123",
      "type": "track",
      "timestamp": "2026-09-15T12:00:00Z",
      "anonymous_id": "visitor_123",
      "event": "signup_started",
      "properties": { "source": "pricing" }
    }]
  }'

Use the actual event time with a timezone and a new ID for every new action. Keep the ID unchanged across retries of that action.

Every message requires id, timestamp, and type:

TypeAdditional required fieldsTypical optional fields
trackevent and either user_id or anonymous_idproperties, groups, context, session_id
pageEither user_id or anonymous_idproperties, context, session_id
identifyuser_idanonymous_id, traits, set_once
groupgroup_type, group_keytraits, set_once, actor identity

Identifiers are nonblank strings up to 200 characters. Custom event names cannot start with $; the SDK's friction signals ($rage_click, $dead_click, $error and $form_abandon) are the only reserved track names accepted. Only track messages take an event name. Properties, context, and traits are JSON objects; group associations map a group type to a group key. Unknown fields are rejected. There are no screen or alias messages. See the schema for the complete model.

Limits and retries

  • 1 to 100 events per batch.
  • 1 MiB for both the incoming body and decoded JSON; 32 KiB per event, measured as normalized JSON.
  • At most 10 levels of nesting in properties, context, traits, and set_once, and 20 group associations per event.
  • Whole-batch validation: an invalid event rejects the entire request before delivery.
  • 60 requests per minute for each project key and collector process. A throttled request returns 429 with Retry-After: 60.
  • Retry network errors, 408, 429, and 5xx with backoff and unchanged event IDs. A partial delivery failure can return 503; stable IDs allow storage to deduplicate the retry.

Stored events deduplicate by workspace, project, environment, and event ID. The first stored value wins; sending a changed payload with the same ID does not correct the original event.

Response

Successful native collection returns HTTP 202:

{
  "status": "accepted",
  "received": 1,
  "received_at": "2026-09-15T12:00:01Z",
  "request_id": "example-request-id"
}

accepted means the collection sink acknowledged the batch. It does not mean warehouse processing is complete or a report can already read it. received is the batch count, not the number of new unique stored events. Responses also include X-Request-ID, X-Embrasure-Collection-Status, and Cache-Control: no-store.

A collector configured to discard events, such as a local development collector, returns HTTP 200 with status: "discarded" and does not persist them.

Errors

Errors return detail containing message, code, request_id, and an optional hint. Invalid event contents are not echoed back.

StatusMeaning
400Invalid Content-Length or gzip stream
401Unknown or revoked project key, collection turned off for the workspace, or invalid server credential
403Browser origin is not allowed
408Request body read timed out
413Request body or decoded JSON exceeds 1 MiB
415Unsupported content type or encoding
422Malformed JSON, invalid event schema, batch or event limit exceeded, or unsupported PostHog event
429Request rate guard reached; honor Retry-After
503Collection disabled (collection_disabled) or delivery temporarily unavailable (Retry-After: 1)

Supported PostHog transport

The collector accepts an analytics subset at /collect/e/, /collect/i/v0/e/, and /collect/batch/. Supported payloads are a single event, a browser event array, or { "api_key": "pk_...", "batch": [...] }, sent as JSON or text/plain, optionally compressed with ?compression=gzip-js. The project key can also come from properties.token or a per-event api_key. Send each event's PostHog uuid: events without one get a generated ID and cannot be deduplicated across retries. These routes return HTTP 200 with the same receipt fields; inspect status to distinguish acceptance from discard mode. Their flexible request bodies are documented here rather than modeled in OpenAPI.

For posthog-js version 1.427.2, use the compatibility helper:

import posthog from "posthog-js";
import { posthogConfig } from "@embrasure/analytics/posthog";

posthog.init("pk_your_project_key", {
  ...posthogConfig("https://api.embrasure.ai/collect"),
});
posthog.capture("signup_started", { source: "pricing" });
posthog.capture("$pageview");

Keep the helper settings intact: they disable unsupported remote configuration, automatic capture, and replay. Explicit custom events, $pageview, $identify, $set, $set_once, and $groupidentify are supported, subject to identity requirements. This is not full PostHog compatibility: alias/merge, anonymous profile updates, screen events, autocapture, historical import, replay, flags, surveys, query/admin APIs, and v1 capture protocol are unsupported. Unsupported reserved events return 422; unsupported endpoints return 404.

The compatibility helper does not apply the native SDK's privacy defaults to PostHog. Apply your application's consent and property-filtering policy. Use the native server SDK when you need a separate server credential.