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 metadata
timestampstringISO 8601 timestamp (UTC)
app_user_idstringYour unique user identifier
session_idstringAuto-generated session identifier
event-payload.json · json
{
  "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 NameDescription
app_openUser opens the app
app_backgroundApp moves to background
session_startNew session begins
session_endSession ends after inactivity

Onboarding

Event NameDescription
onboarding_startUser begins onboarding flow
onboarding_stepUser completes an onboarding step
onboarding_completeUser finishes onboarding

Engagement

Event NameDescription
screen_viewUser views a screen
button_tapUser taps a button or control
searchUser performs a search
shareUser shares content

Revenue

Event NameDescription
purchase_startUser initiates a purchase
purchase_completePurchase transaction succeeds
subscription_startNew subscription begins
subscription_cancelUser cancels a subscription
trial_startFree trial begins
trial_convertTrial converts to paid subscription

Attribution

Event NameDescription
installApp installed (first open)
deeplink_openApp opened via deep link
ad_impressionUser views an ad
ad_clickUser 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.

PropertyDescription
$device_modelDevice hardware model (e.g. iPhone 15 Pro)
$os_versionOperating system version (e.g. iOS 17.2)
$app_versionApp bundle version (e.g. 2.1.0)
$localeDevice locale (e.g. en_US)
$timezoneDevice timezone (e.g. America/New_York)
$carrierMobile carrier name
$screen_widthScreen width in pixels
$screen_heightScreen height in pixels
$ipClient 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 flushes
flushAt20Batch size threshold to trigger a flush

You 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.

terminal · bash
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.