Events
Report sign ups, purchases and other conversions with Lander.trackEvent, backed by an offline queue.
App user IDs
Setting an app user ID with Lander.setUserId is currently supported in Flutter 0.4.0 and later.
This SDK version does not expose that method; its events continue to use the SDK installation ID.
Installs and opens are recorded for you. Use Lander.trackEvent for the things that happen after: a sign up, a purchase, a completed onboarding. Pass the linkId of the link that brought the user in, and the conversion is attributed to that link.
Track an event
Lander.trackEvent(
"purchase",
mapOf("productId" to "123", "amount" to 49.99, "currency" to "USD"),
linkId = link?.linkId, // optional: the LanderLink that opened this screen
)Map<String, Object> properties = new HashMap<>();
properties.put("productId", "123");
properties.put("amount", 49.99);
Lander.trackEvent("purchase", properties, linkId);
Lander.trackEvent("signup"); // name only@JvmStatic
@JvmOverloads
fun trackEvent(
name: String,
properties: Map<String, Any?> = emptyMap(),
linkId: String? = null,
)Prop
Type
trackEvent is fire and forget: it validates the event, hands it to the on device queue and returns. It does not suspend and is safe to call from the main thread.
What Lander records without you
| Event | When | Sent by |
|---|---|---|
| Install | The first launch report of a new install ID | Lander.initialize (POST /v1/sdk/init) |
| Open | Each time a link opens the app and the SDK resolves it | The resolve call (POST /v1/sdk/resolve) |
| Handoff open | A successful claimHandoff | The claim call |
Do not track these yourself, or they will be counted twice.
Attribute events to a link
linkId is only known while you have the LanderLink. Pass it along to the screen (an intent extra or a navigation argument) and use it there:
class ProductActivity : ComponentActivity() {
private val productId by lazy { intent.getStringExtra("productId")!! }
// Set by the activity that received the LanderLink, if any.
private val linkId by lazy { intent.getStringExtra("linkId") }
private fun buy() {
Lander.trackEvent(
"purchase",
mapOf("productId" to productId, "amount" to 49.99, "currency" to "USD"),
// Attribute the purchase to the link that opened this screen, if any.
linkId = linkId,
)
}
}If the conversion happens later (a sign up a few screens or days after the link), keep the linkId yourself, for example in SharedPreferences, and pass it when the event happens. Unresolved links have no linkId; passing null simply records the event without a link.
How the queue works
Events go to a queue on the device first, then to POST /v1/sdk/events in batches:
- Batched. A flush runs about 2 seconds after an event (so bursts go out together), when the app goes to the background (the last activity stops), and on the next launch. Each request carries up to 100 events.
- Survives offline and restarts. The queue is stored in
noBackupFilesDir, outside backups. Up to 1000 events are kept; beyond that the oldest are dropped. - Waits for the install. Lander rejects events from installs it does not know, so events wait until the first launch report has succeeded, then go out.
- Retries with backoff. A failed batch is retried after 1 second, doubling up to 5 minutes, with jitter. A longer
Retry-Afterfrom the API is honored. - Counted once. Every event carries a client generated UUID, so a batch sent twice (for example after a lost response) is deduplicated by Lander.
- Never stuck. A batch the API rejects as invalid is dropped so it cannot block the queue. An auth error (
401or403) pauses sending until the next launch and keeps the events.
Each event is stored with the time it happened (occurredAt, UTC), so a purchase made offline on Monday and sent on Tuesday is still dated Monday.
Errors
trackEvent throws an IllegalArgumentException for programming mistakes, right away:
nameis empty or longer than 100 characters.linkIdis not a UUID.propertiescontains something that is not a JSON value (aDate, a custom class, a map with non string keys,NaNor an infinite number). Convert those first, for example a date to an ISO 8601 string.
It never throws for network or server errors; the queue deals with those. It throws an IllegalStateException if called before Lander.initialize.
See your events
Open Live events in the dashboard to watch app events arrive as you test. It refreshes every 5 seconds.