JVM and Android-compatible Kotlin client. Uses Kotlin coroutines for async flushing, OkHttp for HTTP transport, and implements Closeable for graceful shutdown. Requires JVM 17+ or Android API 26+.
A secret key (sk_…) must never ship inside an APK — anyone can extract it. Use a public key (pk_…) instead, which is safe to embed. When the apiKey starts with pk_, the SDK runs the session handshake: it exchanges the key for a short-lived JWT via POST /api/auth/session/, caches it, and re-mints on expiry/401 — all transparently (concurrent calls share one mint via a coroutine Mutex).
To bind events to a logged-in user, sign the user_id on your backend (HMAC-SHA256 over "$userId|$unixSeconds"with the key's HMAC secret) and return it from userProvider:
kotlin
val client = SegOpsClient(SegOpsOptions(
apiUrl = "https://api.segops.ai",
apiKey = "pk_...",
userProvider = {
SegOpsUserContext(
userId = currentUser.id,
userIdSig = signed.sig, // from your backend
userIdTs = signed.ts
)
}
))
For anonymous visitors, omit userProvider (or return SegOpsUserContext(anonymousId = ...)).
✦ Tip
Enable "require signed user_id" on the public key so the server rejects unsigned or forged identities. Keys used by mobile apps should have an empty origin allowlist (mobile clients send no Origin header).
segops.track(SegOpsEvent(
userId = "user-123",
eventType = "page_viewed",
// occurredAt defaults to Instant.now() when null
payload = mapOf("screen" to "Pricing")
))
identify(ctx: SegOpsContext)
Record user traits as a context_identified event.
kotlin
segops.identify(SegOpsContext(
userId = "user-123",
traits = mapOf("email" to "[email protected]", "plan" to "starter")
))
suspend fun flush()
Flush all buffered events immediately. Suspends until complete — call from a coroutine.
kotlin
coroutineScope {
launch { segops.flush() }
}
close()
Flush synchronously and shut down the background coroutine. SegOpsClient implements Closeable, so you can use it in a use {} block or call close()in your app's lifecycle teardown.
Transient 5xx errors are retried automatically with back-off (500 ms × attempt). If all retries are exhausted, the batch is dropped and the error is passed to your onError handler, which defaults to printing to System.err.
data class SegOpsEvent(
val userId: String,
val eventType: String,
val occurredAt: Instant? = null, // defaults to Instant.now()
val payload: Map<String, Any?> = emptyMap(),
)
data class SegOpsContext(
val userId: String,
val traits: Map<String, Any?>,
)
// Supplied to userProvider when authenticating with a pk_ key.
data class SegOpsUserContext(
val userId: String? = null, // omit for anonymous visitors
val anonymousId: String? = null,
val userIdSig: String? = null, // hex HMAC signature from your backend
val userIdTs: Long? = null, // unix-seconds paired with userIdSig
)