SDK de Android
El SDK de Kixo para Android es compatible con Kotlin 2.0+ y Java, requiere minSdk 24 (Android 7.0) y está compilado contra compileSdk 35. Tu app anfitriona sigue siendo responsable de su propio targetSdk. Con una sola llamada a Kixo.configure en tu Application.onCreate, se registran automáticamente pantallas, toques, sesiones, fallos y eventos del ciclo de vida. El seguimiento de push requiere el puente de FCM que se describe más abajo. El seguimiento automático de solicitudes de red no forma parte de la versión actual para Android. El SDK también admite repetición de sesiones, identidad y objetivos.
Inicio rápido
Tres archivos. Añade el repositorio Maven, añade la dependencia y pega dos líneas en tu subclase de Application.
Requisitos de compilación: compileSdk 35, minSdk 24, Kotlin 2.0+ o Java, y bytecode de Java 17.
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url = uri("https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo")
}
}
}Nota
Con eso queda hecha toda la integración de analítica. Los auto-trackers estándar vienen activados por defecto; el push sigue necesitando el puente de FCM de más abajo. Ajusta cada opción con KixoConfiguration.Builder(...) solo cuando haga falta.
Añádelo a tu app
El repositorio Maven de Kixo está alojado en GitHub Pages. Añádelo junto a google() y mavenCentral() en settings.gradle.kts:
// settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url = uri("https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo")
}
}
}Después, declara la dependencia en el módulo de tu app:
// app/build.gradle.kts
dependencies {
implementation("io.kixo:kixo-android-sdk:0.1.20")
}Consejo
android.permission.INTERNET y android.permission.ACCESS_NETWORK_STATE vienen incluidos en el manifiesto del SDK. Los permisos de notificaciones siguen siendo responsabilidad de tu app y se declaran cuando activas las funciones de push.
Proyectos con varios módulos
La configuración implementation de Gradle es no transitiva: declarar implementation("io.kixo:kixo-android-sdk:0.1.20") en un módulo de biblioteca (por ejemplo, :core_domain) NO hace que Kixo sea visible para :app ni para ningún otro consumidor. Hay dos patrones válidos; elige uno.
Patrón A — cada módulo que llama a Kixo lo declara por su cuenta (recomendado). mantiene al mínimo el classpath de cada módulo y evita recompilaciones en cascada. Usa un catálogo de versiones (libs.kixo.sdk) para cambiar la versión en un solo sitio.
// :core_domain/build.gradle.kts
dependencies {
implementation("io.kixo:kixo-android-sdk:0.1.20") // local use only
}
// :app/build.gradle.kts
dependencies {
implementation(project(":core_domain"))
implementation("io.kixo:kixo-android-sdk:0.1.20") // declared again — fine
}Patrón B — reexpórtalo mediante api(...). Es una única declaración, pero el ABI público del módulo de biblioteca pasa a incluir tipos de Kixo: cualquier cambio de versión obliga a recompilar todos los módulos dependientes. Úsalo solo cuando la biblioteca reutilice tipos de Kixo en sus propias firmas públicas, por ejemplo, si una función devuelve KixoDiagnostics.
// :core_domain/build.gradle.kts
dependencies {
api("io.kixo:kixo-android-sdk:0.1.20") // re-exposed
}
// :app/build.gradle.kts
dependencies {
implementation(project(":core_domain")) // gets Kixo for free
}Advertencia
Si al compilar un módulo aparece Unresolved reference: Kixo, a ese módulo le falta su propia dependencia del SDK: añade la línea implementation de arriba o usa el patrón B.
Inicializa
Configura Kixo desde tu subclase de Application: onCreate se ejecuta antes que cualquier activity, así que todas las pantallas, toques y eventos del ciclo de vida se capturan desde el primer fotograma. Registra Application en tu manifest con android:name=".MyApp".
import android.app.Application
import io.kixo.sdk.Kixo
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Kixo.configure(
context = this,
projectId = "kx_proj_YOUR_PROJECT_ID",
apiKey = "kx_key_YOUR_API_KEY",
)
}
}Si necesitas ajustar parámetros concretos —marcadores de seguimiento automático, cadencia de envío, muestreo de replay o un host de API personalizado— crea KixoConfiguration explícitamente:
import io.kixo.sdk.Kixo
import io.kixo.sdk.KixoConfiguration
val config = KixoConfiguration.Builder(
projectId = "kx_proj_YOUR_PROJECT_ID",
apiKey = "kx_key_YOUR_API_KEY",
)
.autoTrackScreens(true)
.autoTrackTaps(true)
.autoTrackNetwork(false) // reserved; no-op in the current Android release
.autoTrackCrashes(true)
.autoTrackSessions(true)
.autoTrackPush(true)
.flushIntervalMillis(30_000)
.flushAt(20)
.maxBufferSize(200)
.build(applicationContext)
Kixo.configure(this, config)Nota
Idempotente. Una segunda llamada a configure desde el mismo proceso no hace nada y se registra como WARN: el SDK conserva la primera configuración. Los eventos que tu singleton de autenticación deje en cola antes de que se complete antes configure se guardan en un búfer (máximo 50) y se reenvían cuando el SDK ya está inicializado, así que puedes llamar a Kixo.identify(...) desde un punto global antes de que termine Application.onCreate.
Registrar eventos
Tres primitivas cubren la mayor parte de la instrumentación: track para eventos, markGoal para señales de conversión y addBreadcrumb para contexto no asociado a eventos.
import io.kixo.sdk.Kixo
Kixo.track("video_played", mapOf(
"video_id" to "vid_42",
"duration_ms" to 18_500,
"autoplay" to false,
))
// markGoal(name, value?, currency?, properties?) — pass extra context
// through the named 'properties' argument (a Map can't be the 2nd
// positional arg; that slot is the Double 'value').
Kixo.markGoal("activated", properties = mapOf(
"step" to "onboarding_completed",
))
// Revenue goals use the typed value + currency parameters:
Kixo.markGoal("purchase_completed", value = 49.99, currency = "USD")
Kixo.addBreadcrumb(
message = "user toggled dark mode",
category = "ui",
level = "info",
)Consejo
Los objetivos se califican. Los objetivos marcados alimentan los embudos de activación de Kixo y la tarea diaria de detección de cambios: si el volumen de un objetivo cae un 70 % frente a la semana anterior, aparecerá en tu panel con la insignia Pendiente de revisión. Usa markGoal para los pocos momentos que de verdad importan; track para todo lo demás.
Eventos estándar
Azúcar sintáctico sobre Kixo.track para los eventos que Kixo reconoce por nombre: claves de texto literal que el detector de eventos estándar del backend compara tal cual. Validación en compilación de la forma de las propiedades y una única fuente de verdad para los nombres.
import io.kixo.sdk.Kixo
import io.kixo.sdk.SubscriptionInterval
Kixo.trackPurchase(
amount = 49.99,
currency = "USD",
productId = "pro_yearly",
)
Kixo.trackSubscriptionStart(
plan = "pro",
amount = 9.99,
currency = "USD",
interval = SubscriptionInterval.MONTH,
)
Kixo.trackSignup(method = "google")
Kixo.trackTrialStart(plan = "pro", days = 14)
Kixo.trackCancel(plan = "pro", reason = "too_expensive")
Kixo.trackUpgrade(fromPlan = "free", toPlan = "pro")
Kixo.trackActivation(event = "first_post_published")
Kixo.trackShare(channel = "twitter", contentId = "post_123")
Kixo.trackInvite(channel = "email", recipientCount = 5)Identificar usuarios
Vincula los eventos posteriores a un identificador de usuario estable y a un conjunto de atributos. La unión entre anónimo e identificado se resuelve en Kixo: los eventos capturados antes de identify se atribuyen retroactivamente al mismo usuario.
import io.kixo.sdk.Kixo
// Reserved standard property keys carry a $-prefix (Mixpanel
// convention) so they namespace away from your own custom traits
// and promote to the dashboard's profile columns. See
// io.kixo.sdk.StandardProperty for the typed catalogue, or the
// "Standard property catalog" section below for the full 37-key list.
Kixo.identify("user_123", mapOf(
"$email" to "jane@example.com", // identity
"$name" to "Jane Doe", // identity
"$plan" to "pro", // subscription pack
"$lifetime_orders" to 12, // e-commerce pack
"signup_source" to "twitter_ad", // custom trait
))
// Logout: clear identity, super-properties, and the persisted queue.
Kixo.reset()⚠️ Cuidado con el signo de dólar en Kotlin. Las claves estándar de identidad llevan el prefijo $ ($email, $name, $first_name) y, en un literal de Kotlin, debe escapar el signo de dólar como "\$email". Si escribes "$email", Kotlin interpolará tu variable email, así que el valor acabará silenciosamente como trait personalizado y nunca rellenará las columnas de correo o nombre en Audience. La forma más simple de evitarlo es usar la sobrecarga tipada (SDK 0.1.13+), que no da margen de error: Kixo.setUserProperty(StandardProperty.EMAIL, email).
Etiquetar a un usuario para segmentarlo
Usa setUserProperty con un valor booleano para añadir al usuario una etiqueta sencilla de sí o no. La etiqueta se conserva entre lanzamientos y sirve para segmentos, campañas de correo y consultas en el chat, sin más configuración que la llamada al SDK.
// Tag a user as subscribed — segments + campaigns can target this
Kixo.setUserProperty("subscribe", true)
// VIP membership
Kixo.setUserProperty("vip", true)
// String + numeric values work too
Kixo.setUserProperty("plan_tier", "enterprise")
Kixo.setUserProperty("lifetime_orders", 42)
// Bulk-set
Kixo.setUserProperties(mapOf(
"subscribe" to true,
"plan_tier" to "enterprise",
))Las propiedades se guardan con SharedPreferences entre lanzamientos y se adjuntan automáticamente a todos los eventos salientes. En el chat puedes pedir cosas como "envía un correo de bienvenida a los usuarios cuyo subscribe sea true"; Kixo crea el segmento y te deja el borrador de la plantilla. Se borran con Kixo.reset().
Catálogo estándar de propiedades
Las claves de propiedad reservadas llevan el prefijo $ para separarlas de tus atributos personalizados. El catálogo de Kixo incluye 37 claves repartidas en 3 bloques universales (identidad, geografía y ciclo de vida) y 5 bloques verticales B2B (suscripción, comercio electrónico, medios, marketplace y fidelización). Define solo las que encajen con tu producto: el panel se adapta y muestra únicamente los bloques que hayas rellenado.
Identidad
Siempre relevante. Define las columnas de la cabecera del perfil.
| Clave | Tipo | Descripción |
|---|---|---|
$email | cadena | Correo electrónico principal; suele usarse como clave de unión para consolidar identidades. |
$phone | cadena | Número de teléfono en formato E.164. |
$name | cadena | Nombre completo para mostrar. |
$first_name | cadena | Nombre. |
$last_name | cadena | Apellidos. |
$avatar_url | cadena | URL completa de la imagen de avatar del usuario. |
Geolocalización
Contexto geográfico.
| Clave | Tipo | Descripción |
|---|---|---|
$country | cadena | Código de país ISO 3166. |
$city | cadena | Nombre de la ciudad. |
$region | cadena | Estado o provincia. |
$timezone | cadena | Zona IANA como America/Los_Angeles. |
$language | cadena | Etiqueta IETF como en o ru-RU. |
$locale | cadena | Identificador de configuración regional completo. |
Ciclo de vida
Cuándo lo vimos.
| Clave | Tipo | Descripción |
|---|---|---|
$created | ISO8601 | Fecha y hora de registro o creación de la cuenta. |
$last_seen | ISO8601 | Última interacción. |
Suscripción
Úsalo si tu producto tiene planes.
| Clave | Tipo | Descripción |
|---|---|---|
$plan | cadena | Slug del nivel: free, pro, enterprise. |
$subscription_status | cadena | active / trial / cancelled / past_due. |
$trial_ends | ISO8601 | Cuándo termina la prueba actual. |
$mrr | número | Ingresos recurrentes mensuales en la divisa de la cuenta. |
$subscription_started | ISO8601 | Cuándo empezó la suscripción actual. |
Comercio electrónico
Úsalo si vendes productos.
| Clave | Tipo | Descripción |
|---|---|---|
$lifetime_orders | número | Número de pedidos completados. |
$lifetime_revenue | número | Gasto total. |
$aov | número | Valor medio del pedido. |
$last_purchase | ISO8601 | Última compra completada correctamente. |
$first_purchase | ISO8601 | Primera compra completada con éxito. |
$cart_abandoned_count | número | Número total de abandonos de carrito. |
Medios
Úsalo si publicas contenido.
| Clave | Tipo | Descripción |
|---|---|---|
$content_tier | cadena | free / premium / paid. |
$subscribed_categories | Cadena CSV o lista | Categorías que sigue el usuario. |
$watch_time_total | número | Tiempo total de visualización en segundos. |
$last_played | ISO8601 | Último inicio de reproducción. |
Marketplace
Úsalo si tu producto es una plataforma de dos caras.
| Clave | Tipo | Descripción |
|---|---|---|
$seller_tier | cadena | Slug del nivel del vendedor. |
$buyer_tier | cadena | Slug del nivel del comprador. |
$listings_count | número | Anuncios activos del usuario. |
$reviews_count | número | Reseñas recibidas por el usuario. |
$verified | booleano | Estado de KYC. |
Fidelización
Úsalo para programas de fidelización y recompensas.
| Clave | Tipo | Descripción |
|---|---|---|
$loyalty_points | número | Saldo actual de puntos canjeables. |
$vip_level | cadena | Slug del nivel VIP. |
$referral_count | número | Referencias correctas atribuidas a este usuario. |
Consejo
¿No aparece tu caso? Usa claves sin prefijo para los atributos personalizados. Se muestran en el panel de atributos personalizados del dashboard sin llenar de ruido las columnas de perfil. Los 5 bloques verticales anteriores son propuestas para las estructuras B2B más habituales; la terminología específica de cada cliente (por ejemplo, shipping_plan) se deja sin prefijo.
Superpropiedades
Pares clave-valor por sesión que se adjuntan automáticamente a todos los eventos salientes. A diferencia de los atributos de identify (que describen la identidad), las superpropiedades describen el contexto de la sesión: variante A/B activa, flavor de compilación o feature flags activadas. Se conservan entre lanzamientos y se borran con reset(). Si hay conflicto, las properties del evento en track siempre tienen prioridad.
import io.kixo.sdk.Kixo
Kixo.setSuperProperty("build_flavor", "beta")
Kixo.setSuperProperties(mapOf(
"ab_variant" to "B",
"referrer_campaign" to "autumn-launch",
))
// Sugar for A/B tracking — stored as 'experiment_<id>'.
Kixo.setExperimentVariant("checkout_v2", "variant_a")
Kixo.unsetSuperProperty("build_flavor")
Kixo.clearSuperProperties()Notificaciones push
Hay dos vías de integración. Elige la A si usas FCM y quieres la configuración funcional más corta; elige la B si ya tienes un FirebaseMessagingService personalizado que no puedes reorganizar o si quieres controlar explícitamente qué entregas de FCM ve Kixo.
Opción A — hereda de KixoFirebaseMessagingService (seguimiento automático)
Crea una subclase de KixoFirebaseMessagingService y llama a super.onMessageReceived(...) desde tu override: Kixo emite automáticamente push_received (payload visible) o push_silent (solo datos). La clase base también gestiona el registro de onNewToken si no lo sobrescribes. El registro de AndroidManifest.xml no cambia respecto al de un servicio FCM normal.
import com.google.firebase.messaging.RemoteMessage
import io.kixo.sdk.KixoFirebaseMessagingService
class MyMessagingService : KixoFirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage) // Kixo auto-tracks push_received
// … your own routing / notification display
}
}Nota
Kixo compila esta clase opcional contra Firebase Messaging, pero no añade Firebase a tu app de forma transitiva. El SDK declara Firebase como compileOnly; si eliges esta opción, tu app debe depender ya de firebase-messaging, como cualquier receiver de FCM.
Opción B — llama a la API manualmente desde tu propio servicio FCM
Registra tu token de FCM en Kixo mediante FirebaseMessagingService.onNewToken y, después, registra cada entrega de forma explícita. Usa esta vía si quieres que Kixo solo vea una parte de las entregas de FCM. Por ahora, la entrega en Android solo es compatible con FCM.
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.kixo.sdk.Kixo
import io.kixo.sdk.PushProvider
class MyMessagingService : FirebaseMessagingService() {
override fun onNewToken(token: String) {
Kixo.setPushToken(token, PushProvider.FCM)
}
override fun onMessageReceived(message: RemoteMessage) {
// Convert the FCM payload to a Map<String, Any?> and log it —
// Kixo correlates this with the open / dismiss it sees later.
Kixo.logPushReceived(message.data.toMap(), appState = "background")
}
}Android no expone un punto de enganche universal del ciclo de vida para aperturas, descartes ni botones de acción de las notificaciones. Reenvía esas señales desde los intents o receivers de notificaciones que cree tu app:
import io.kixo.sdk.Kixo
Kixo.logPushOpened(payload = pushPayload) // open
Kixo.logPushOpened(payload = pushPayload, actionId = "reply") // action-button tap
Kixo.logPushDismissed(payload = pushPayload) // swipe-awayReplay de sesiones
Reproduce una reconstrucción visual real de lo que vio la persona usuaria. En cada captura, el SDK codifica un fotograma comprimido de la pantalla (una imagen JPEG) junto con una instantánea estructural de la jerarquía de vistas y sube ambos, para que el reproductor del panel pueda mostrar una reproducción fiel al píxel junto a la cronología de interacción. Configura la repetición de sesiones del proyecto en Dashboard → Ajustes → Reproducción de sesiones. El SDK lee y actualiza automáticamente esa política del proyecto, incluido el enmascaramiento, los modos de captura y el permiso para subir por red móvil.
import io.kixo.sdk.Kixo
import io.kixo.sdk.KixoConfiguration
val config = KixoConfiguration.Builder(
projectId = "kx_proj_YOUR_PROJECT_ID",
apiKey = "kx_key_YOUR_API_KEY",
)
.build(applicationContext)
Kixo.configure(this, config)Si la subida por red móvil está desactivada, la repetición de sesiones en cola espera a una red permitida.
Consejo
Enmascara antes de subir. Kixo captura píxeles, así que el enmascaramiento se aplica antes de que nada salga del dispositivo. Los campos de contraseña y correo electrónico se detectan y se ocultan automáticamente; el texto de las instantáneas estructurales pasa por un filtro de PII; y cualquier vista marcada con setKixoMask(true) se rasteriza como un rectángulo opaco en el fotograma antes de codificar el JPEG: sus píxeles nunca salen del dispositivo. Las pantallas de Jetpack Compose se enmascaran por completo por defecto (llama a setKixoMask(false) en el ComposeView más externo para incluir una pantalla que ya hayas auditado). Los operadores revisan el reproductor de Replay junto con la cronología de eventos en el panel.
Recopilación de datos
El SDK captura los datos activados en tu proyecto, además de los eventos y las propiedades que envía tu aplicación.
Depuración
Kixo.diagnostics() devuelve una instantánea de solo lectura del estado del SDK, útil en una pantalla de depuración oculta o en una prueba de humo. Responde a «¿por qué no están llegando mis eventos?» sin necesidad de abrir el depurador.
import io.kixo.sdk.Kixo
val diag = Kixo.diagnostics()
Log.d("Kixo", "queued=${diag.queue.bufferedEventCount}")
Log.d("Kixo", "paused=${diag.paused}") // collection paused state
Log.d("Kixo", "lifecycleState=${diag.lifecycleState}") // SDK lifecycle stateFuerza un envío inmediato desde tu entorno de pruebas: bloquea hasta timeoutMs mientras espera una ida y vuelta de red:
import io.kixo.sdk.Kixo
// Async fire-and-forget — returns immediately.
Kixo.flush()
// Blocking variant for instrumentation tests. Never call on the main thread.
val landed: Boolean = Kixo.flushBlocking(timeoutMs = 5_000L)
assertTrue(landed)Compose Navigation
Las rutas de Activity / Fragment generan al instante eventos screen_view y registros estructurados de screen_visit, con metadatos de permanencia y flujo, sin configuración adicional. En Jetpack Compose Navigation, lanza Kixo.screen desde un LaunchedEffect asociado a la ruta: así el SDK verá un evento por destino, independientemente del número de recomposiciones.
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import io.kixo.sdk.Kixo
@Composable
fun AppNavHost(nav: NavHostController) {
NavHost(navController = nav, startDestination = "home") {
composable("home") {
LaunchedEffect("home") { Kixo.screen("HomeScreen") }
HomeScreen()
}
composable("settings") {
LaunchedEffect("settings") { Kixo.screen("SettingsScreen") }
SettingsScreen()
}
}
}Agentes de programación con AI
La superficie pública del SDK es pequeña y está pensada para el autocompletado: todos los métodos cuelgan del singleton Kixo, todos los ejemplos de Kotlin de esta guía empiezan con import io.kixo.sdk.Kixo, y nuestro README incluye un bloque "AI agent quick reference" que herramientas como Claude Code, Cursor y Codex pueden pegar directamente en su contexto. Si tu agente se atasca, este es el punto de partida canónico:
// Tell your AI coding agent:
// "Integrate the Kixo Android SDK using io.kixo:kixo-android-sdk
// from https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo.
// Call Kixo.configure(this, projectId, apiKey) in Application.onCreate.
// Then use Kixo.track / Kixo.identify / Kixo.markGoal as needed."Nota
Todas las secciones anteriores están pensadas para ese flujo: los imports siempre son explícitos, los tipos siempre se nombran y el singleton del SDK nunca usa alias. Pásale esta página a tu agente y deja que trabaje.