Tracking events
The TrackEvent API surface, property rules, validation, global properties, and revenue helpers.
The basics
// Bare event
ReflectSDK.TrackEvent("level_started");
// Event with properties
ReflectSDK.TrackEvent("level_started", new Dictionary<string, object> {
{ "level", 3 },
{ "difficulty", "hard" },
{ "from_menu", true },
});Events are queued, batched, and sent every 30 seconds (or 50 events, whichever comes first). They survive app kill — the queue is persisted to disk on pause.
Property types
Anything JSON-serializable works:
string,int,long,float,double,boolDateTime/DateTimeOffset— converted to ISO-8601 in UTC- Nested
IDictionary/ arrays (1 level deep) null— preserved
Anything else gets .ToString()’d.
Validation rules
The SDK validates client-side so you never spend Cloudflare bandwidth on rejected events:
| Limit | Value | Drop reason |
|---|---|---|
| Event name | 1–64 chars, [a-z][a-z0-9_-]* | event_name_invalid_chars |
| Properties per event | ≤ 25 | too_many_props |
| Property key length | ≤ 40 chars | bad_prop_key |
| Property string value length | ≤ 1024 chars (truncated) | silent truncation |
Underscore-prefixed names (e.g. _user_alias, _crash) are reserved for SDK-internal events.
Global properties
Set values once, get them merged into every event:
ReflectSDK.SetGlobalProperty("user_tier", "premium");
ReflectSDK.SetGlobalProperty("ab_variant", "B");
ReflectSDK.SetGlobalProperty("app_locale", Application.systemLanguage.ToString());
// Per-event props win on key collision
ReflectSDK.TrackEvent("level_complete", new Dictionary<string, object> {
{ "level", 5 },
{ "user_tier", "trial" }, // overrides the global for this one event
});
// Remove
ReflectSDK.UnsetGlobalProperty("ab_variant");
// Wipe all
ReflectSDK.ClearGlobalProperties();Revenue events
Three flavors with progressively more affordances:
// 1) Manual — full control
ReflectSDK.TrackEvent("purchase", new Dictionary<string, object> {
{ "product_id", "sku_pro_pack" },
{ "price_local", 9.99 },
{ "currency_code", "USD" },
{ "transaction_id", "txn_abc123" },
});
// 2) Helper with revenue fields
ReflectSDK.TrackPurchase(
productId: "sku_pro_pack",
price: 9.99,
currencyCode: "USD",
transactionId: "txn_abc123",
receiptData: storeKitReceiptBase64 // optional — server validates
);
// 3) Subscription helpers
ReflectSDK.TrackSubscription(
productId: "sub_monthly",
price: 4.99,
currencyCode: "USD",
transactionId: "sub_txn_xyz",
isTrial: false
);
ReflectStandardEvents.SubscriptionDidRenew("sub_monthly", 4.99, "USD", "txn_renew");
ReflectStandardEvents.SubscriptionDidCancel("sub_monthly", reason: "user_cancelled");receipt_dataattributions.is_revenue_validated on success. Spoofed receipts get fraud_flag’d. See REST API.Force flush
The SDK flushes automatically every 30s, on app_pause, and when BatchSize is reached. If you need to flush manually (e.g. just before a critical screen exit):
ReflectSDK.Flush();Standard event names
Reflect ships a vocabulary of pre-defined event names + typed helpers matching AppsFlyer / Firebase conventions. See Standard events.