> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zixflow.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Event API Reference

> HTTP API for server-side event tracking, user lifecycle, and device management.

> **Who this is for:** Backend developers and integrators sending server-side events. Mobile/web app actions should use an [SDK](/documentation/sdk/javascript/introduction) instead.

Complete reference for all Zixflow event ingress API endpoints — what each one does, when to use it, how to authenticate, and what to send.

For Mintlify API playground pages (request/response fields), see the [Events API Reference](/api-reference/zixflow-ai/events/introduction) under Zixflow AI.

***

## API vs SDK: When to Use Which

Zixflow exposes two surfaces for sending data: the **client/server SDKs** and the **HTTP API**.

|                     | SDKs                                                                           | HTTP API                                                                                                                                 |
| ------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **Who uses it**     | Web and mobile apps (JavaScript, Flutter, React Native, iOS, Android, Node.js) | Backend servers, scripts, integrations                                                                                                   |
| **Auth**            | API key in SDK initialization                                                  | `Authorization: Basic base64(api_key:)`                                                                                                  |
| **Batching**        | Automatic (typically \~20 events or \~30s)                                     | Manual (you control when to call `/batch`)                                                                                               |
| **Offline support** | Built-in queue + retry on mobile/web SDKs                                      | Your responsibility                                                                                                                      |
| **When to use**     | User-facing app actions                                                        | Server-side events (order shipped, subscription renewed), user lifecycle management (delete, suppress), device registration from backend |

**Rule of thumb:** If the event happens because a user tapped something in your app, use an SDK. If the event happens on your server (fulfillment, billing, fraud detection), use the API directly.

***

## Authentication

Both API groups use the same scheme:

```
Authorization: Basic <base64(api_key:)>
```

The API key goes in the **username** field of HTTP Basic Auth. The password is always empty — note the trailing colon before base64 encoding.

**Example:**

```bash theme={null}
# API key: ws_abc123
echo -n "ws_abc123:" | base64
# → d3NfYWJjMTIzOg==

curl -X POST https://api-events.zixflow.com/v1/track \
  -H "Authorization: Basic d3NfYWJjMTIzOg==" \
  -H "Content-Type: application/json" \
  -d '{"userId": "user_123", "event": "Button Clicked"}'
```

**Getting your API key:** Dashboard → Workspace Settings → API Keys

**Auth error responses:**

| HTTP Status               | Reason                                     |
| ------------------------- | ------------------------------------------ |
| `401 Unauthorized`        | Missing, invalid, or unrecognised API key  |
| `503 Service Unavailable` | Auth cache (Redis) temporarily unavailable |

***

## Two API Groups

The service exposes two distinct API groups, each serving a different use case:

| Group                  | Base path            | Used by                                              | Traffic |
| ---------------------- | -------------------- | ---------------------------------------------------- | ------- |
| **Data Pipelines API** | `/v1/`               | Mobile/web SDKs, server-to-server event tracking     | \~98%   |
| **Track API v1**       | `/api/v1/customers/` | Backend servers for user/device lifecycle management | \~2%    |

***

## Data Pipelines API (`/v1/`)

These endpoints accept event data in Segment-compatible format. All endpoints under `/v1/` share the same auth middleware and rate limiter.

**All requests accept:** `Content-Type: application/json`\
**All success responses:** `HTTP 200` with empty body `{}`

***

### POST `/v1/identify` (alias: `/v1/i`)

**Purpose:** Create or update a user profile. Call this when a user signs up, logs in, or when you want to update their attributes.

**When to use:**

* User signs up or logs in → call identify with their userId and traits
* User updates their profile (name, email, plan) → call identify with updated traits
* Merging anonymous pre-signup activity → call identify with both `userId` and `anonymousId`

**Request body:**

```json theme={null}
{
  "userId": "user_12345",
  "anonymousId": "anon_abc-123",
  "traits": {
    "email": "sarah@example.com",
    "name": "Sarah Johnson",
    "plan": "enterprise",
    "company": "Acme Corp"
  },
  "timestamp": "2026-05-04T10:00:00.000Z",
  "messageId": "msg_unique_id",
  "context": {
    "ip": "203.0.113.1"
  }
}
```

**Fields:**

| Field         | Type     | Required | Description                                                |
| ------------- | -------- | -------- | ---------------------------------------------------------- |
| `userId`      | string   | Yes\*    | Your application's stable identifier for this user         |
| `anonymousId` | string   | Yes\*    | Anonymous ID from SDK (required if no userId)              |
| `traits`      | object   | No       | Key-value user attributes to set/update                    |
| `timestamp`   | ISO 8601 | No       | When the identify happened (defaults to server time)       |
| `messageId`   | string   | No       | Deduplication ID (SDK generates this automatically)        |
| `context`     | object   | No       | Device/platform context (SDK populates this automatically) |

\*At least one of `userId` or `anonymousId` is required. Providing both triggers identity merge.

**What happens:**

1. If `userId` is new → creates a new user profile
2. If `userId` exists → merges `traits` with existing profile (additive, not replace)
3. If both `userId` + `anonymousId` provided → links all anonymous events to the identified profile

***

### POST `/v1/track` (alias: `/v1/t`)

**Purpose:** Record a user action or business event. The most commonly used endpoint for custom event tracking.

**When to use:**

* Any user action: button tapped, purchase completed, feature used
* Business events from your server: subscription renewed, payment failed
* Device lifecycle: `Application Installed`, `Application Opened` (SDK sends these automatically)

**Request body:**

```json theme={null}
{
  "userId": "user_12345",
  "event": "Order Completed",
  "properties": {
    "order_id": "ord_789",
    "revenue": 129.99,
    "currency": "USD",
    "product_name": "Nike Air Max 90"
  },
  "timestamp": "2026-05-04T14:32:15.000Z"
}
```

**Fields:**

| Field         | Type     | Required | Description                                  |
| ------------- | -------- | -------- | -------------------------------------------- |
| `userId`      | string   | Yes\*    | Identified user ID                           |
| `anonymousId` | string   | Yes\*    | Anonymous user ID (if user not identified)   |
| `event`       | string   | **Yes**  | Name of the event (e.g. `"Order Completed"`) |
| `properties`  | object   | No       | Event-specific data                          |
| `timestamp`   | ISO 8601 | No       | When the event occurred                      |

\*At least one of `userId` or `anonymousId` is required.

**Semantic events** — certain event names trigger special handling beyond the standard event pipeline:

| Event name                  | Special behavior                                           |
| --------------------------- | ---------------------------------------------------------- |
| `Device Created or Updated` | Routes to device registration pipeline                     |
| `Device Deleted`            | Soft-deletes device record (sets `active=false`)           |
| `Device Registered`         | Routes to device logs table                                |
| `Device Updated`            | Routes to device logs table                                |
| `User Deleted`              | Sets `is_deleted=true` on profile, deactivates all devices |
| `User Suppressed`           | Sets `is_suppressed=true`, blocks all channel delivery     |
| `User Unsuppressed`         | Clears suppression flag                                    |
| `Application Installed`     | Routes to device logs table                                |
| `Application Opened`        | Routes to device logs table                                |
| `Application Backgrounded`  | Routes to device logs table                                |
| `Application Foregrounded`  | Routes to device logs table                                |
| `Application Crashed`       | Routes to device logs table                                |

***

### POST `/v1/screen` (alias: `/v1/s`)

**Purpose:** Record a mobile screen view. Used by the Flutter SDK automatically when `autoTrackScreenViews: true` is configured, or manually when you want to attach screen-specific properties.

**When to use:**

* Mobile apps to track navigation flow
* Attribute screen-level data (product ID, category) to the view event

**Request body:**

```json theme={null}
{
  "userId": "user_12345",
  "name": "Product Detail",
  "properties": {
    "product_id": "prod_789",
    "category": "Footwear",
    "price": 129.99
  }
}
```

**Fields:**

| Field         | Type   | Required | Description                           |
| ------------- | ------ | -------- | ------------------------------------- |
| `userId`      | string | Yes\*    | Identified user ID                    |
| `anonymousId` | string | Yes\*    | Anonymous user ID                     |
| `name`        | string | No       | Screen name (e.g. `"Product Detail"`) |
| `category`    | string | No       | Screen category                       |
| `properties`  | object | No       | Screen-specific properties            |

***

### POST `/v1/page` (alias: `/v1/p`)

**Purpose:** Record a web page view. Equivalent to `/v1/screen` but for web apps.

**When to use:**

* Web apps and marketing sites
* Track specific page metadata (title, URL, referrer)

**Request body:**

```json theme={null}
{
  "userId": "user_12345",
  "name": "Pricing",
  "properties": {
    "path": "/pricing",
    "url": "https://app.example.com/pricing",
    "referrer": "https://google.com",
    "title": "Pricing — Example"
  }
}
```

**Fields:**

| Field         | Type   | Required | Description                                |
| ------------- | ------ | -------- | ------------------------------------------ |
| `userId`      | string | Yes\*    | Identified user ID                         |
| `anonymousId` | string | Yes\*    | Anonymous user ID                          |
| `name`        | string | No       | Page name                                  |
| `properties`  | object | No       | Page metadata (path, url, referrer, title) |

***

### POST `/v1/batch` (alias: `/v1/b`)

**Purpose:** Send multiple events in a single HTTP request. This is what the Flutter SDK uses under the hood — the SDK queues events locally and sends them as a batch every 30 seconds or after 20 events accumulate.

**When to use:**

* The SDK handles this for you automatically
* If calling the API directly from a server and you have multiple events to send, prefer `/batch` over individual calls to reduce latency and network overhead

**Request body:**

```json theme={null}
{
  "batch": [
    {
      "type": "identify",
      "userId": "user_12345",
      "traits": { "plan": "enterprise" }
    },
    {
      "type": "track",
      "userId": "user_12345",
      "event": "Plan Upgraded",
      "properties": { "from_plan": "pro", "to_plan": "enterprise" }
    },
    {
      "type": "screen",
      "userId": "user_12345",
      "name": "Home"
    }
  ],
  "sentAt": "2026-05-04T10:00:30.000Z"
}
```

**Batch item `type` values:**

| Type       | Equivalent endpoint                                         | Notes                         |
| ---------- | ----------------------------------------------------------- | ----------------------------- |
| `identify` | [`/v1/identify`](/api-reference/zixflow-ai/events/identify) | `traits` field                |
| `track`    | [`/v1/track`](/api-reference/zixflow-ai/events/track)       | `event` + `properties` fields |
| `screen`   | [`/v1/screen`](/api-reference/zixflow-ai/events/screen)     | `name` + `properties` fields  |
| `page`     | [`/v1/page`](/api-reference/zixflow-ai/events/page)         | `name` + `properties` fields  |

Group and Alias are **not supported**. Do not send `type: "group"` or `type: "alias"`.

**Limits:**

* Minimum: 1 item per batch
* Maximum: 100 items per batch
* Items are processed concurrently

Each batch item shares the same fields as its corresponding individual endpoint, plus a `type` field to identify the kind.

***

## Track API v1 (`/api/v1/customers/`)

Server-side endpoints for user and device lifecycle management. Use these from your backend — not from mobile apps.

**Auth:** Same as Data Pipelines — `Authorization: Basic base64(api_key:)`\
**User identifier:** Passed in the URL path (`/:identifier`), not the request body

***

### PUT `/api/v1/customers/:identifier`

**Purpose:** Create or update a user profile. Server-side equivalent of `/v1/identify`.

**When to use:**

* Your backend creates a user and wants to push their profile to Zixflow
* Updating user attributes from your server (plan change, billing update)

**Request body:**

```json theme={null}
{
  "email": "sarah@example.com",
  "name": "Sarah Johnson",
  "plan": "enterprise",
  "created_at": 1746352800
}
```

**Notes:**

* Attributes are flat key-value (not nested under `traits`)
* Timestamps must be **Unix seconds** (not ISO 8601)
* `:identifier` is your user ID

***

### POST `/api/v1/customers/:identifier/events`

**Purpose:** Track an event for a specific user. Server-side equivalent of `/v1/track`.

**When to use:**

* Order fulfilled (triggered by your fulfillment system, not the user's app)
* Payment received / payment failed (from billing system)
* Subscription renewed (from subscription service)
* Any event your server generates, not the user's device

**Request body:**

```json theme={null}
{
  "name": "Order Fulfilled",
  "data": {
    "order_id": "ord_789",
    "tracking_number": "USPS-1Z999AA10123456784",
    "carrier": "USPS"
  },
  "timestamp": 1746352800
}
```

**Notes:**

* Event name goes in `name` (not `event`)
* Event data goes in `data` (not `properties`)
* Timestamp is Unix seconds

***

### PUT `/api/v1/customers/:identifier/devices`

**Purpose:** Register a push notification device token for a user.

**When to use:**

* Your server manages push token registration (less common — the Flutter SDK handles this automatically via `registerDeviceToken()`)
* Server-side integrations that need to register tokens on behalf of users

**Request body:**

```json theme={null}
{
  "device": {
    "id": "fcm_token_abc123...",
    "platform": "android",
    "last_used": 1746352800
  }
}
```

***

### DELETE `/api/v1/customers/:identifier/devices/:token`

**Purpose:** Remove a specific device token from a user. Call this when a user logs out of a device (so they don't receive push on a device they're no longer signed in to).

**Request body:** Empty

**Effect:** Soft-deletes the device record — sets `active=false`, `push_enabled=false`.

***

### DELETE `/api/v1/customers/:identifier`

**Purpose:** Delete a user profile.

**Request body:** Empty

**Effect:** Sets `is_deleted=true` on the profile and deactivates all linked devices. The profile record and event history are preserved for analytics — this is a soft delete.

**When to use:**

* User requests account deletion (GDPR right to erasure)
* Account cleanup / fraud removal

***

### POST `/api/v1/customers/:identifier/suppress`

**Purpose:** Suppress all outbound messages for a user across every channel simultaneously.

**Request body:** Empty

**Effect:** Sets `is_suppressed=true` on the profile. No push notifications, emails, SMS, WhatsApp, or RCS will be sent to this user until unsuppressed.

**When to use:**

* User has opted out of all communications
* Account is suspended or in bad standing
* Compliance hold

**Note:** Suppression is different from channel opt-out. Channel opt-out (`email_opt_out`, `push_opt_out`, etc.) is per-channel. Suppression blocks everything with a single flag.

***

### POST `/api/v1/customers/:identifier/unsuppress`

**Purpose:** Lift suppression and resume message delivery for a user.

**Request body:** Empty

**Effect:** Clears `is_suppressed` flag. Message delivery resumes on all channels the user hasn't individually opted out of.

***

### GET `/api/v1/customers/:identifier/subscription_preferences`

**Purpose:** Retrieve the current channel opt-out state for a user.

**Query parameters:**

| Parameter | Values                  | Default | Description                        |
| --------- | ----------------------- | ------- | ---------------------------------- |
| `id_type` | `id`, `email`, `cio_id` | `id`    | How to interpret the `:identifier` |

**Example:**

```
GET /api/v1/customers/sarah@example.com/subscription_preferences?id_type=email
```

**Response:** User's current opt-out state per channel (`email_opt_out`, `sms_opt_out`, `push_opt_out`, `whatsapp_opt_out`, `rcs_opt_out`) with their opt-out timestamps.

***

## Error Responses

All endpoints return consistent error shapes:

```json theme={null}
{
  "error": "VALIDATION_FAILED",
  "details": [
    {
      "reason": "REQUIRED",
      "field": "userId",
      "message": "userId is required"
    }
  ]
}
```

| HTTP Status | Meaning                                                   |
| ----------- | --------------------------------------------------------- |
| `200`       | Success — event accepted                                  |
| `400`       | Bad request — invalid JSON or failed validation           |
| `401`       | Unauthorized — missing or invalid API key                 |
| `404`       | Not found — user identifier not found (Track API v1 only) |
| `503`       | Service unavailable — auth cache temporarily unavailable  |
| `500`       | Internal error — unexpected failure                       |

***

## Complete Endpoint Map

API Reference pages: [Events Introduction](/api-reference/zixflow-ai/events/introduction)

### Data Pipelines (`/v1/`)

```
POST  /v1/identify    (alias: /v1/i)   — Create/update user profile
POST  /v1/track       (alias: /v1/t)   — Track event
POST  /v1/screen      (alias: /v1/s)   — Track mobile screen view
POST  /v1/page        (alias: /v1/p)   — Track web page view
POST  /v1/batch       (alias: /v1/b)   — Send multiple events at once
```

### Track API v1 (`/api/v1/customers/`)

```
PUT    /api/v1/customers/:id                          — Create/update user
POST   /api/v1/customers/:id/events                   — Track server-side event
PUT    /api/v1/customers/:id/devices                  — Register device token
DELETE /api/v1/customers/:id/devices/:token           — Remove device token
DELETE /api/v1/customers/:id                          — Delete user (soft)
POST   /api/v1/customers/:id/suppress                 — Suppress all channels
POST   /api/v1/customers/:id/unsuppress               — Resume delivery
GET    /api/v1/customers/:id/subscription_preferences — Get channel opt-out state
```

### Utility

```
GET /health   — Health check
GET /         — Service info
```
