Events
The track and identify wire APIs, and the property rules.
Track
POST {ingest_host}/analytics/events
X-VortexAuth-Key: <project_key>
Content-Type: application/json
{
"event": "signup.completed",
"external_user_id": "u-12345",
"properties": { "plan": "pro" },
"occurred_at": "2026-05-01T12:34:56.789Z"
}
| Field | Required | Notes |
|---|---|---|
event | yes | dot.case, e.g. signup.completed. |
external_user_id | yes | A stable id your app owns: the signed-in user id, or a persistent anonymous id. Never blank, never per-request. |
properties | no | Object with snake_case keys. |
occurred_at | no | RFC 3339. The server stamps now when absent. Set it only when replaying history. |
A 200 returns {"event_id": "<uuid>"}. A 4xx means the payload is
malformed and retrying it unchanged will fail identically; a 5xx is worth
retrying with backoff. The SDKs queue, persist, and retry for you.
Identify
POST {ingest_host}/analytics/identify
{ "alias_id": "<anonymous id>", "primary_id": "u-12345" }
Tells the server two ids are the same person. The SDKs call this from
identify() using the anonymous id they minted before sign-in.
Reserved properties
Keys with a $ prefix belong to the SDK. $lib, $lib_version, and
$session_id are stamped on every event; caller-supplied $ keys are
dropped with a console warning.
Three plain keys have product meaning:
environment— stamped from the client'senvironmentoption. Reads (dashboard, MCP) can filter on it, so keep development traffic labelled.signup_source— how this user arrived. When a user comes through a tracked link, the SDK stampsvx_linkand attribution falls back to it.plan— the user's plan, used by breakdowns and channel comparisons.
Naming
Use dot.case verbs in past tense: project.created, checkout.completed,
trial.started. The dashboard's event annotations let you add display
labels and hide internal events later, so pick names for the wire, not for
the chart.