Sending Events
Zeotap’s Events API is compatible with the Segment specification, making it easy to migrate from Segment or use existing Segment libraries. This page covers the API format for all event types with full examples.
API Endpoint
POST https://composable.zeotap.com/api/v1/eventsAuthentication
Events are authenticated with a write key passed in the X-Write-Key header:
curl -X POST https://composable.zeotap.com/api/v1/events \
-H "X-Write-Key: YOUR_WRITE_KEY" \
-H "Content-Type: application/json" \
-d '{ ... }'The write key can also be passed as:
- the
Authorization: Basic base64(key:)header (Segment/analytics.js compatible), or - a
?writeKey=YOUR_WRITE_KEYquery parameter (required fornavigator.sendBeaconwhich cannot set custom headers).
Write keys are public identifiers — they are embedded in client-side JavaScript and are designed to be visible in outbound requests. Use the event source scoping and consent/rate-limit controls on each key to gate what events it can produce.
Response
| Status | Meaning |
|---|---|
200 OK | Event accepted |
400 Bad Request | Invalid event format (malformed JSON, missing required fields) |
401 Unauthorized | Invalid or missing write key |
403 Forbidden | Event type not allowed for this write key |
422 Unprocessable Entity | Event rejected by contract validation |
429 Too Many Requests | Rate limit exceeded |
Common Fields
All event types share these fields:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Event type: track, identify, page, screen, group |
userId | string | Conditional | The known user identifier. Required if anonymousId is not provided. |
anonymousId | string | Conditional | A client-generated anonymous identifier. Required if userId is not provided. |
timestamp | string (ISO 8601) | No | When the event occurred. Defaults to server receipt time. |
context | object | No | Additional context (IP, user agent, locale, device info, etc.) |
integrations | object | No | Per-destination forwarding overrides (e.g., { "Amplitude": false }) |
messageId | string | No | Client-generated unique ID for deduplication. Auto-generated if omitted. |
Track Events
Track events record user actions — anything a user does that you want to measure.
Format
{
"type": "track",
"userId": "user_123",
"event": "Order Completed",
"properties": {
"order_id": "ord_456",
"total": 99.99,
"currency": "USD",
"products": [
{
"product_id": "prod_789",
"name": "Zeotap T-Shirt",
"price": 29.99,
"quantity": 2
},
{
"product_id": "prod_012",
"name": "Zeotap Sticker Pack",
"price": 9.99,
"quantity": 4
}
],
"coupon": "SUMMER20",
"payment_method": "credit_card"
},
"timestamp": "2025-03-15T14:30:00Z",
"context": {
"ip": "203.0.113.42",
"userAgent": "Mozilla/5.0 ...",
"locale": "en-US",
"page": {
"url": "https://shop.example.com/checkout/confirmation",
"referrer": "https://shop.example.com/checkout/payment"
}
}
}curl Example
curl -X POST https://composable.zeotap.com/api/v1/events \
-H "X-Write-Key: sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"type": "track",
"userId": "user_123",
"event": "Order Completed",
"properties": {
"order_id": "ord_456",
"total": 99.99,
"currency": "USD",
"products": [
{ "product_id": "prod_789", "name": "Zeotap T-Shirt", "price": 29.99, "quantity": 2 }
]
},
"timestamp": "2025-03-15T14:30:00Z"
}'Common Track Events
| Event | Properties | Description |
|---|---|---|
Signed Up | plan, source | User created an account |
Logged In | method | User signed in |
Product Viewed | product_id, name, category, price | User viewed a product page |
Product Added | product_id, name, price, quantity, cart_id | User added item to cart |
Cart Viewed | cart_id, products[], total | User viewed their cart |
Checkout Started | order_id, total, products[] | User initiated checkout |
Order Completed | order_id, total, currency, products[], coupon | User completed a purchase |
Subscription Created | plan, price, interval | User started a subscription |
Identify Calls
Identify calls associate traits (attributes) with a user. Use them when you learn something new about a user.
Format
{
"type": "identify",
"userId": "user_123",
"traits": {
"email": "jane@example.com",
"name": "Jane Smith",
"plan": "enterprise",
"company": {
"id": "comp_456",
"name": "Acme Corp",
"industry": "Technology",
"employee_count": 250
},
"created_at": "2024-01-15T09:00:00Z",
"lifetime_value": 4500.00,
"newsletter_subscribed": true,
"tags": ["vip", "beta-tester"]
},
"timestamp": "2025-03-15T14:30:00Z",
"context": {
"ip": "203.0.113.42",
"userAgent": "Mozilla/5.0 ..."
}
}curl Example
curl -X POST https://composable.zeotap.com/api/v1/events \
-H "X-Write-Key: sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"type": "identify",
"userId": "user_123",
"traits": {
"email": "jane@example.com",
"name": "Jane Smith",
"plan": "enterprise",
"lifetime_value": 4500.00
}
}'Behavior
- Traits are merged — new traits are added and existing traits are updated
- To remove a trait, set it to
null - Identify calls update the user profile used by audiences and orchestrations
- If only
anonymousIdis provided, the traits are associated with the anonymous profile until an identify call with bothanonymousIdanduserIdlinks them
Page Views
Page calls record when a user views a page on your website.
Format
{
"type": "page",
"userId": "user_123",
"name": "Pricing",
"category": "Marketing",
"properties": {
"url": "https://www.example.com/pricing",
"path": "/pricing",
"title": "Pricing - Example",
"referrer": "https://www.google.com/",
"search": "?plan=enterprise",
"utm_source": "google",
"utm_medium": "cpc",
"utm_campaign": "brand-q1"
},
"timestamp": "2025-03-15T14:30:00Z"
}curl Example
curl -X POST https://composable.zeotap.com/api/v1/events \
-H "X-Write-Key: sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"type": "page",
"userId": "user_123",
"name": "Pricing",
"properties": {
"url": "https://www.example.com/pricing",
"path": "/pricing",
"title": "Pricing - Example",
"referrer": "https://www.google.com/"
}
}'Screen Views
Screen calls record when a user views a screen in your mobile app. The format is identical to page calls but uses type: "screen".
Format
{
"type": "screen",
"userId": "user_123",
"name": "Settings",
"category": "Account",
"properties": {
"variation": "dark_mode"
},
"timestamp": "2025-03-15T14:30:00Z",
"context": {
"device": {
"type": "ios",
"model": "iPhone 15 Pro",
"manufacturer": "Apple"
},
"os": {
"name": "iOS",
"version": "18.2"
},
"app": {
"name": "MyApp",
"version": "3.1.0",
"build": "245"
}
}
}curl Example
curl -X POST https://composable.zeotap.com/api/v1/events \
-H "X-Write-Key: sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"type": "screen",
"userId": "user_123",
"name": "Settings",
"category": "Account",
"properties": {
"variation": "dark_mode"
},
"context": {
"device": { "type": "ios", "model": "iPhone 15 Pro" }
}
}'Group Calls
Group calls associate a user with a company, organization, or team.
Format
{
"type": "group",
"userId": "user_123",
"groupId": "comp_456",
"traits": {
"name": "Acme Corp",
"industry": "Technology",
"plan": "enterprise",
"employee_count": 250,
"website": "https://acme.example.com",
"created_at": "2023-06-01T00:00:00Z",
"mrr": 15000.00
},
"timestamp": "2025-03-15T14:30:00Z"
}curl Example
curl -X POST https://composable.zeotap.com/api/v1/events \
-H "X-Write-Key: sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"type": "group",
"userId": "user_123",
"groupId": "comp_456",
"traits": {
"name": "Acme Corp",
"industry": "Technology",
"plan": "enterprise",
"employee_count": 250
}
}'Behavior
- Group traits are merged (same as identify)
- A user can belong to multiple groups
- Group associations are used by audience conditions that reference company/account attributes
Batch Endpoint
For server-side integrations sending many events, use the batch endpoint to send up to 500 events in a single request:
curl -X POST https://composable.zeotap.com/api/v1/events/batch \
-H "X-Write-Key: sk_live_abc123" \
-H "Content-Type: application/json" \
-d '{
"batch": [
{
"type": "track",
"userId": "user_123",
"event": "Product Viewed",
"properties": { "product_id": "prod_789" },
"timestamp": "2025-03-15T14:30:00Z"
},
{
"type": "track",
"userId": "user_456",
"event": "Order Completed",
"properties": { "order_id": "ord_789", "total": 149.99 },
"timestamp": "2025-03-15T14:31:00Z"
}
]
}'Batch Response
The batch endpoint returns a summary:
{
"received": 2,
"accepted": 2,
"rejected": 0,
"errors": []
}If some events are rejected, the errors array contains per-event error details with the event index.
Context Object
The context object provides additional metadata about the event. While optional, it enriches your data for analysis:
| Field | Type | Description |
|---|---|---|
context.ip | string | User’s IP address (used for geo enrichment) |
context.userAgent | string | Browser/device user agent string |
context.locale | string | User’s locale (e.g., en-US) |
context.timezone | string | User’s timezone (e.g., America/New_York) |
context.page.url | string | Current page URL |
context.page.path | string | Current page path |
context.page.referrer | string | Referring URL |
context.page.title | string | Page title |
context.device.type | string | Device type (ios, android, web) |
context.device.model | string | Device model |
context.device.manufacturer | string | Device manufacturer |
context.os.name | string | Operating system name |
context.os.version | string | Operating system version |
context.app.name | string | Application name |
context.app.version | string | Application version |
context.campaign.source | string | UTM source |
context.campaign.medium | string | UTM medium |
context.campaign.name | string | UTM campaign name |
context.consent | object | Consent state — one of the three forms below |
Consent
Attaching consent state to every event is the recommended way to keep compliance data alongside your analytics. context.consent accepts exactly one of three mutually exclusive forms:
| Form | Fields | When to use |
|---|---|---|
| TCF v2 string | { "tcfString": "..." } | EU/GDPR — server derives category booleans from TCF purpose consent bits |
| US Privacy string | { "usPrivacy": "1YNN" } | CCPA — server derives advertising/marketing opt-out status |
| Pre-parsed categories | { "analytics": true, "marketing": false, ... } | When your CMP already provides resolved boolean values |
Consent values are:
- Evaluated by forwarding rules — rules can require specific consent categories to be granted before forwarding an event to a destination. When raw strings (TCF/US Privacy) are provided, the server automatically resolves categories using standard IAB purpose mappings.
- Persisted in the warehouse — both the raw string and derived categories land in dedicated columns (
consent_string,us_privacy,consent_categories) on event tables for audit and cohort analysis.
TCF v2 example:
{
"type": "track",
"userId": "user_123",
"event": "Product Viewed",
"properties": { "product_id": "prod_789" },
"context": {
"consent": { "tcfString": "CPXxRfAPXxRfAAfKAB..." }
}
}Pre-parsed example:
{
"type": "track",
"userId": "user_123",
"event": "Product Viewed",
"properties": { "product_id": "prod_789" },
"context": {
"consent": {
"analytics": true,
"marketing": false,
"advertising": false,
"functional": true
}
}
}The Browser SDK manages consent state automatically via setConsent() — call it once when the user interacts with your consent banner and every subsequent event carries the current state.
Deduplication
Events are deduplicated using the messageId field. If you send two events with the same messageId, the second is silently ignored. This is useful for retry logic — if your request times out but the server received it, retrying with the same messageId prevents duplicate events.
If you don’t provide a messageId, Zeotap generates one server-side (no deduplication for those events).
Next Steps
- Event contracts — Enforce schemas on incoming events
- Event transformations — Modify events in-flight
- Event forwarding — Route events to downstream destinations
- Debugging — Verify events are arriving correctly