Events API
Track user behavior and actions with the Flameup Events API
Overview
The Events API allows you to track user actions and behaviors in your application. Events power analytics, trigger campaigns, and help you understand how users interact with your product.
/track, /track/batch) need events:write; reads (/people/{external_id}/events, /events/search) need events:read. /identify is a people write — see that section.Event Object
An event in Flameup has the following structure:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "purchase_completed",
"parameters": {
"order_id": "order_789",
"amount": 99.99,
"currency": "USD",
"items": ["product_a", "product_b"]
},
"created_at": "2024-01-15T10:30:01Z"
}
Field Reference
These are the fields accepted in a track request body:
| Field | Type | Required | Description |
|---|---|---|---|
event | string | Yes | Name of the event (e.g., "signup", "purchase") |
external_id | string | Yes | Your identifier for the user. Also accepted as userId or user_id |
parameters | object | No | Additional data about the event. Also accepted as properties |
timestamp | timestamp | No | When the event occurred (defaults to now) |
Identify a Person
Create or update one person and their traits. This is the other half of
ingestion: /identify says who someone is, /track says what they did.
Endpoint: POST /api/v1/identify
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | Your own user id. external_id and user_id are accepted aliases |
email | string | No | |
phone | string | No | |
anonymousId | string | No | An anonymous id whose earlier events should belong to this person. Max 36 characters — a longer value rejects the whole request with 400. The merge itself runs asynchronously after the response |
traits | object | No | Merged with existing traits; new values win |
timestamp | datetime | No | Defaults to now |
curl -X POST https://api.flameup.ai/api/v1/identify \
-H "Authorization: Bearer {API_KEY}" \
-H 'Content-Type: application/json' \
-d '{
"userId": "user_12345",
"email": "jane@example.com",
"traits": { "plan": "premium", "exam": "RPSC" }
}'
Send one identifier per request. If you send more than one of userId,
external_id or user_id and they disagree, the request is rejected rather
than one being chosen for you.
anonymousId limit is checked before the person is written, so it is not a best-effort part of the async merge: an over-long value fails the entire call with 400 and "error": "Anonymous ID must be 36 characters or less" — no person is created or updated and no merge is scheduled. A bare UUID fits exactly at 36; prefixed UUIDs (anon_550e8400-…), brace-wrapped GUIDs ({…}) and SHA-1/SHA-256 hex digests all exceed it.Permissions
people:write, or the narrower people:identify — which authorizes this
route and nothing else. That narrowness is what makes it safe on a key shipped
inside an app.
What comes back
{ "success": true, "person": { "id": "…", "workspace_id": "…", "userId": "user_12345" } }
person object is trimmed to those three fields for any key without people:read. A write-scoped key cannot use identify to read a person back — send an id you do not own and you learn nothing about it.Track an Event
Record a user action in Flameup.
Endpoint: POST /api/v1/track
async function trackEvent(externalId, eventName, parameters = {}) {
const response = await fetch(
'https://api.flameup.ai/api/v1/track',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
external_id: externalId,
event: eventName,
parameters: parameters
})
}
);
return response.json();
}
// Track a page view
await trackEvent('user_123', 'page_viewed', {
page: '/pricing',
referrer: '/home'
});
// Track a purchase
await trackEvent('user_123', 'purchase_completed', {
order_id: 'order_789',
amount: 99.99,
currency: 'USD',
items: [
{ sku: 'PROD-001', name: 'Widget', quantity: 2 },
{ sku: 'PROD-002', name: 'Gadget', quantity: 1 }
]
});
// Track a feature usage
await trackEvent('user_123', 'feature_used', {
feature: 'export_csv',
duration_seconds: 45
});
from datetime import datetime
def track_event(external_id, event_name, parameters=None):
response = requests.post(
'https://api.flameup.ai/api/v1/track',
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {API_KEY}'
},
json={
'external_id': external_id,
'event': event_name,
'parameters': parameters or {}
}
)
return response.json()
# Track a page view
track_event('user_123', 'page_viewed', {
'page': '/pricing',
'referrer': '/home'
})
# Track a purchase
track_event('user_123', 'purchase_completed', {
'order_id': 'order_789',
'amount': 99.99,
'currency': 'USD',
'items': [
{'sku': 'PROD-001', 'name': 'Widget', 'quantity': 2},
{'sku': 'PROD-002', 'name': 'Gadget', 'quantity': 1}
]
})
curl -X POST "https://api.flameup.ai/api/v1/track" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_api_key" \
-d '{
"external_id": "user_123",
"event": "purchase_completed",
"parameters": {
"order_id": "order_789",
"amount": 99.99,
"currency": "USD"
}
}'
Response (201 Created):
{
"success": true,
"data": {
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"person_id": "550e8400-e29b-41d4-a716-446655440001",
"event": "purchase_completed",
"parameters": {
"order_id": "order_789",
"amount": 99.99,
"currency": "USD"
},
"timestamp": "2024-01-15T10:30:00Z",
"processed_at": "2024-01-15T10:30:01Z",
"status": "processed"
}
}
Two fields appear only when they apply:
person_created: true— theexternal_idwas not a known person, so/trackcreated a minimal one before storing the event. This is the only signal that a mistyped identifier just minted a ghost profile instead of attaching to the person you meant.status: "accepted_with_warnings"with awarningsarray — the event was stored durably, but a post-storage processor (workflow or campaign trigger) failed. The event exists and will read back; it may not have triggered campaigns. A storage failure is never reported this way — that is a5xx.
Batch Track Events
Track multiple events in a single request (up to 100 events).
Endpoint: POST /api/v1/track/batch
const response = await fetch(
'https://api.flameup.ai/api/v1/track/batch',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
events: [
{
external_id: 'user_123',
event: 'page_viewed',
parameters: { page: '/home' }
},
{
external_id: 'user_123',
event: 'button_clicked',
parameters: { button: 'signup' }
},
{
external_id: 'user_456',
event: 'purchase_completed',
parameters: { amount: 99.99 }
}
]
})
}
);
const result = await response.json();
response = requests.post(
'https://api.flameup.ai/api/v1/track/batch',
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {API_KEY}'
},
json={
'events': [
{
'external_id': 'user_123',
'event': 'page_viewed',
'parameters': {'page': '/home'}
},
{
'external_id': 'user_123',
'event': 'button_clicked',
'parameters': {'button': 'signup'}
},
{
'external_id': 'user_456',
'event': 'purchase_completed',
'parameters': {'amount': 99.99}
}
]
}
)
result = response.json()
curl -X POST "https://api.flameup.ai/api/v1/track/batch" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your_api_key" \
-d '{
"events": [
{"external_id": "user_123", "event": "page_viewed", "parameters": {"page": "/home"}},
{"external_id": "user_123", "event": "button_clicked", "parameters": {"button": "signup"}}
]
}'
Response (201 Created when all events succeed):
{
"success": true,
"processed": 2,
"total": 2,
"results": [
{ "index": 0, "event_id": "550e8400-e29b-41d4-a716-446655440000", "person_id": "...", "event": "page_viewed", "status": "processed" },
{ "index": 1, "event_id": "660e8400-e29b-41d4-a716-446655440111", "person_id": "...", "event": "button_clicked", "status": "processed" }
]
}
If some events fail, the request returns 207 Multi-Status with an errors array and success: false. Every entry — success or failure — carries the index of the event in your submitted array, so retry the failed indexes only:
{
"success": false,
"processed": 1,
"total": 2,
"results": [
{ "index": 0, "event_id": "550e8400-e29b-41d4-a716-446655440000", "person_id": "...", "event": "page_viewed", "status": "processed" }
],
"errors": [
{ "index": 1, "error": "external_id or userId is required" }
]
}
Get Person Events
Retrieve events for a specific person.
Endpoint: GET /api/v1/people/{external_id}/events
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Number of results (max 500) |
const params = new URLSearchParams({
limit: '100'
});
const response = await fetch(
`https://api.flameup.ai/api/v1/people/${externalId}/events?${params}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`
}
}
);
const { events, total } = await response.json();
response = requests.get(
f'https://api.flameup.ai/api/v1/people/{external_id}/events',
headers={'Authorization': f'Bearer {API_KEY}'},
params={
'limit': 100
}
)
data = response.json()
events = data['events']
curl "https://api.flameup.ai/api/v1/people/{external_id}/events?limit=100" \
-H "Authorization: Bearer your_api_key"
Search Events by Type
Find recent events of one type across the whole workspace, rather than for a single person.
Endpoint: GET /api/v1/events/search
| Parameter | Required | Description |
|---|---|---|
event | Yes | The event name to search for |
since | No | Only events at or after this time (RFC 3339) |
limit | No | Default 50, max 500 |
curl "https://api.flameup.ai/api/v1/events/search?event=purchase_completed&since=2026-01-01T00:00:00Z" \
-H "Authorization: Bearer {API_KEY}"
Requires events:read.
Common Event Examples
Here are examples of commonly tracked events:
// User signed up
await trackEvent('user_123', 'signed_up', {
method: 'email', // or 'google', 'github'
referral_code: 'FRIEND50'
});
// User logged in
await trackEvent('user_123', 'logged_in', {
method: 'password', // or 'sso', 'magic_link'
device: 'mobile'
});
// User upgraded plan
await trackEvent('user_123', 'plan_upgraded', {
from_plan: 'free',
to_plan: 'premium',
annual: true
});
// User churned
await trackEvent('user_123', 'subscription_cancelled', {
reason: 'too_expensive',
feedback: 'Great product but over budget'
});
// Product viewed
await trackEvent('user_123', 'product_viewed', {
product_id: 'prod_001',
name: 'Premium Widget',
price: 49.99,
category: 'widgets'
});
// Added to cart
await trackEvent('user_123', 'cart_updated', {
action: 'add',
product_id: 'prod_001',
quantity: 2,
cart_total: 99.98
});
// Checkout started
await trackEvent('user_123', 'checkout_started', {
cart_items: 3,
cart_total: 149.97
});
// Purchase completed
await trackEvent('user_123', 'purchase_completed', {
order_id: 'order_789',
amount: 149.97,
currency: 'USD',
payment_method: 'credit_card',
items: [...]
});
// Feature used
await trackEvent('user_123', 'feature_used', {
feature: 'export_pdf',
duration_ms: 2340
});
// Search performed
await trackEvent('user_123', 'search_performed', {
query: 'integration guide',
results_count: 15,
clicked_result: 3
});
// File uploaded
await trackEvent('user_123', 'file_uploaded', {
file_type: 'csv',
file_size_kb: 1024,
rows_imported: 5000
});
// Report generated
await trackEvent('user_123', 'report_generated', {
report_type: 'monthly_summary',
date_range: '2024-01',
format: 'pdf'
});
// Email opened
await trackEvent('user_123', 'email_opened', {
campaign_id: 'camp_456',
subject: 'Your weekly digest'
});
// Push notification clicked
await trackEvent('user_123', 'push_clicked', {
campaign_id: 'camp_789',
action: 'view_order'
});
// NPS submitted
await trackEvent('user_123', 'nps_submitted', {
score: 9,
feedback: 'Love the product!'
});
// Support ticket created
await trackEvent('user_123', 'support_ticket_created', {
ticket_id: 'ticket_123',
category: 'billing',
priority: 'medium'
});
Event Naming Conventions
Recommended Format
Use object_action format with snake_case:
| Good | Avoid |
|---|---|
user_signed_up | UserSignedUp, user-signed-up |
purchase_completed | Purchase, bought |
feature_used | FeatureUsed, used_feature |
email_opened | OpenedEmail, open |
Standard Events
These events are recognized by Flameup for analytics:
| Event | Description |
|---|---|
signed_up | User created an account |
logged_in | User authenticated |
logged_out | User ended session |
purchase_completed | User made a purchase |
subscription_started | User started a subscription |
subscription_cancelled | User cancelled subscription |
Using Events for Campaigns
Events can trigger automated campaigns. When setting up a campaign:
- Event-based triggers: Start a campaign when a specific event occurs
- Event conditions: Target users who have (or haven't) performed certain events
- Event parameters: Use event parameters for personalization
Example campaign triggers:
# Welcome series
Trigger: signed_up
# Abandoned cart
Trigger: cart_updated
Condition: NOT purchase_completed within 24 hours
# Re-engagement
Trigger: None
Condition: NOT logged_in within 30 days
# Upsell
Trigger: feature_limit_reached
Condition: plan = 'free'
Best Practices
Focus on events that indicate user intent or value:
- Conversion events (signup, purchase, upgrade)
- Feature engagement
- Key user milestones
Avoid tracking every click or page view unless needed for specific analysis.
Data Storage
Events are optimized for:
- Fast writes (track millions of events)
- Efficient time-range queries
- Aggregations and analytics
- Real-time campaign triggering