Event Tracking
A deep dive into the Appflow event model, naming conventions, reserved properties, and best practices. This guide is platform-agnostic and applies to both the iOS and Android SDKs as well as server-side event ingestion.
Event Model
Every event sent to Appflow follows a consistent structure. Each event includes a name, an optional key-value property map, a timestamp, and identifiers that tie it to a specific user and session. Events are submitted in batches for efficiency.
event_namestringName of the event (snake_case)propertiesobjectKey-value map of event metadatatimestampstringISO 8601 timestamp (UTC)app_user_idstringYour unique user identifiersession_idstringAuto-generated session identifier{
"events": [
{
"event_name": "purchase_complete",
"app_user_id": "user_123",
"properties": {
"product_id": "pro_monthly",
"revenue": "9.99",
"currency": "USD"
},
"timestamp": "2025-03-07T10:30:00Z"
}
]
}Naming Conventions
Use snake_case for all event names. Group related events with a common prefix so they sort together in the dashboard. Below are the recommended event names organized by category.
Lifecycle
| Event Name | Description |
|---|---|
app_open | User opens the app |
app_background | App moves to background |
session_start | New session begins |
session_end | Session ends after inactivity |
Onboarding
| Event Name | Description |
|---|---|
onboarding_start | User begins onboarding flow |
onboarding_step | User completes an onboarding step |
onboarding_complete | User finishes onboarding |
Engagement
| Event Name | Description |
|---|---|
screen_view | User views a screen |
button_tap | User taps a button or control |
search | User performs a search |
share | User shares content |
Revenue
| Event Name | Description |
|---|---|
purchase_start | User initiates a purchase |
purchase_complete | Purchase transaction succeeds |
subscription_start | New subscription begins |
subscription_cancel | User cancels a subscription |
trial_start | Free trial begins |
trial_convert | Trial converts to paid subscription |
Attribution
| Event Name | Description |
|---|---|
install | App installed (first open) |
deeplink_open | App opened via deep link |
ad_impression | User views an ad |
ad_click | User taps an ad |
Reserved Properties
The SDK automatically attaches the following properties to every event. These are prefixed with $ and cannot be overridden. You do not need to set these manually.
| Property | Description |
|---|---|
$device_model | Device hardware model (e.g. iPhone 15 Pro) |
$os_version | Operating system version (e.g. iOS 17.2) |
$app_version | App bundle version (e.g. 2.1.0) |
$locale | Device locale (e.g. en_US) |
$timezone | Device timezone (e.g. America/New_York) |
$carrier | Mobile carrier name |
$screen_width | Screen width in pixels |
$screen_height | Screen height in pixels |
$ip | Client IP address (server-side only, never stored) |
Batching
Both the iOS and Android SDKs batch events locally before sending them to the Appflow API. This minimizes network requests and preserves battery life. By default, events are flushed every 30 seconds or when 20 events have accumulated, whichever comes first.
flushInterval30Seconds between automatic flushesflushAt20Batch size threshold to trigger a flushYou can adjust both values through AppflowOptions during SDK initialization. Setting flushAt to 1 disables batching and sends events immediately, which is useful during development but not recommended for production.
Server-Side Events
You can also send events from your backend directly to the Appflow API. This is useful for tracking server-side actions like subscription renewals, refunds, or backend-triggered campaigns. Use your secret key (sk_ prefix) for server-to-server requests instead of the client key.
curl -X POST https://api-v2.appflow.ai/v1/events/batch \
-H "Authorization: Bearer sk_xxx" \
-H "Content-Type: application/json" \
-d '{
"app_id": "app_xxx",
"events": [
{
"event_name": "subscription_renewed",
"app_user_id": "user_123",
"properties": {
"plan": "pro",
"period": "monthly",
"mrr_cents": "999"
}
}
]
}'The endpoint accepts the same event format as the SDKs. You can include up to 500 events per batch request. The API returns a 202 Accepted response on success with the number of events ingested.
Best Practices
Be specific with event names
Use descriptive names like checkout_step_2 instead of generic names like step. Specific names make it easy to build funnels and debug issues in the dashboard.
Use properties for variants
Track button_tap with a button property rather than creating separate events like subscribe_button_tap, settings_button_tap, etc. This keeps your event catalog manageable.
Keep property values as strings
All property values are stored as strings. The SDK will coerce numbers and booleans, but sending strings directly avoids ambiguity and ensures consistent behavior.
Never track PII in properties
Do not include personally identifiable information such as email addresses, phone numbers, or physical addresses in event properties. Use the identify call for user traits instead.
Test with debug mode first
Enable debug logging and the event overlay during development to verify that events are tracked correctly before you ship to production. This catches naming mistakes early.