---
term: 'Presencia (Status Online)'
seoTitle: 'Presencia y Status Online: Heartbeats, Última Conexión y Fan-out'
headline: '¿Qué es la presencia (status online)?'
slug: presencia-status-online
category: api-realtime
shortDefinition: 'La presencia es una señal en tiempo real que indica si un usuario está online, ausente u offline, mantenida fresca por conexiones y heartbeats.'
relatedTerms:
  - real-time-live-queries
  - websockets-real-time-sync
  - pub-sub-pattern
  - sse-vs-websockets-vs-polling
contrastsWith:
  - push-notifications-apns-fcm
aboutTerms:
  - 'Online Status'
  - 'Last Seen'
  - 'Heartbeat'
  - 'Typing Indicators'
faq:
  - question: '¿Qué es un sistema de presencia?'
    answer: 'Un servicio que rastrea si cada usuario está online, ausente u offline y transmite los cambios, en tiempo real, a los usuarios con permiso para verlos. Bajo el capó: conexiones persistentes o heartbeats periódicos que escriben en un store rápido en memoria, con los cambios de estado repartidos por pub/sub.'
  - question: '¿Cómo funciona la "última conexión" (last seen)?'
    answer: 'El servidor registra un timestamp en cada heartbeat y en cada desconexión. Mientras estás online, se sirve el estado en vivo; cuando pasas a offline, responde en su lugar el último timestamp registrado. Es la única pieza durable de un sistema por lo demás efímero — el estado expira, la última conexión persiste.'
  - question: '¿Qué intervalo de heartbeat debería usar la presencia?'
    answer: 'Treinta segundos es el default común en producción, con el timeout de offline en dos a tres veces el intervalo (60–90 segundos). Los intervalos más rápidos encogen el retraso de detección pero multiplican la carga de escritura linealmente con tu población online; el intervalo también debe quedar por debajo de los timeouts de inactividad de la infraestructura, o los proxies matan primero las conexiones calladas.'
  - question: '¿Detección por conexión o por heartbeat — cuál es mejor?'
    answer: 'Ambas, para fallas distintas. Los eventos de conexión (abrir/cerrar) dan transiciones instantáneas pero se pierden las muertes silenciosas — un dispositivo que se queda sin batería no envía frame de cierre y deja una conexión zombi. La expiración del heartbeat acota esa obsolescencia al costo de un retraso de detección. Los sistemas de producción usan los eventos de conexión como camino rápido y el TTL del heartbeat como fuente de la verdad.'
  - question: '¿Cómo evitas que el status online parpadee?'
    answer: 'Haz debounce de la transición a offline: en la desconexión, arranca un temporizador de gracia — comúnmente de 30 a 60 segundos — y cancélalo si el usuario reconecta, publicando "offline" solo cuando el temporizador dispara. Los usuarios en ascensores y túneles de tren reconectan constantemente; sin el período de gracia, cada lista de contactos en la que aparecen parpadea con ellos.'
  - question: '¿Cómo escala la presencia a millones de usuarios?'
    answer: 'Respetando la matemática del fan-out: cada cambio de estado debe llegar a cada observador, así que N contactos por M transiciones explota rápido. Las palancas son un store en memoria con TTL para el estado, suscribirse solo a los usuarios visibles (la lista de chats abierta, no la agenda completa), consultar la última conexión a demanda en lugar de empujarla, y limitar la difusión de estado en grupos grandes.'
  - question: '¿Cómo funciona la presencia multi-dispositivo?'
    answer: 'Rastrea una conexión o un heartbeat por dispositivo y fusiona: el usuario está online mientras cualquier dispositivo lo esté, y offline solo cuando el último se calla. La política de merge habitual es "gana el más disponible" — online en el teléfono le gana a inactivo en el escritorio — con la última conexión tomada del dispositivo más reciente.'
  - question: '¿Pueden los usuarios ocultar su status online?'
    answer: 'Deberían poder — la visibilidad es una feature de producto, no una ocurrencia tardía: reglas como todos, solo contactos o nadie, normalmente con reciprocidad (oculta el tuyo y dejas de ver el de los demás). Los datos de presencia son datos de comportamiento; define quién puede observar a quién en el modelo de permisos, no en la interfaz.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'RFC 6121 — XMPP: Instant Messaging and Presence'
    url: 'https://datatracker.ietf.org/doc/html/rfc6121'
  - name: 'RFC 6455 — The WebSocket Protocol (ping/pong, close codes)'
    url: 'https://datatracker.ietf.org/doc/html/rfc6455'
  - name: 'Page Visibility API — MDN Web Docs'
    url: 'https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API'
  - name: 'Beacon API — MDN Web Docs'
    url: 'https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API'
  - name: 'Presence information — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Presence_information'
cta:
  title: 'Puntos verdes sin la infraestructura'
  text: 'Construye presencia en Back4app con lo que la plataforma ya opera: una clase Presence, saves de heartbeat, una suscripción de Live Query para tu lista de contactos y un barrido programado para los timeouts — permisos incluidos.'
  linkText: 'Empieza gratis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-09-04'
translationKey: presence-online-status
---

**La presencia es una señal en tiempo real que indica si un usuario está online, ausente u offline, mantenida fresca por conexiones y heartbeats.** El punto verde es una interfaz engañosamente simple sobre un problema distribuido genuinamente difícil: el servidor debe *inferir* la ausencia — los dispositivos rara vez anuncian su propia muerte — y luego difundir cada inferencia a todos los que observan, a escala de lista de contactos. Estandarizado mucho antes del chat moderno, en las [stanzas de presencia de XMPP](https://datatracker.ietf.org/doc/html/rfc6121), el patrón tiene fundamentos que no han cambiado desde entonces.

## Puntos clave

| Pregunta | Respuesta |
| --- | --- |
| Los estados | online · ausente/inactivo · offline — más overrides definidos por el usuario (no molestar) |
| El mecanismo | Escrituras de heartbeat + expiración por TTL; eventos de conexión como camino rápido |
| Los números | Heartbeat de ~30 s · timeout de offline en 2–3× el intervalo · 30–60 s de gracia contra el parpadeo |
| El compañero | La última conexión (last seen) — el timestamp durable detrás del punto efímero |
| La parte difícil | Fan-out: N observadores × M cambios de estado, multiplicados por tu éxito |

## El contrato del heartbeat

El patrón entero cabe en un intercambio:

```text
cliente     cada 30 s:  save { status: "online", lastActiveAt: now }
servidor    cada 60 s:  barrido — ¿lastActiveAt más viejo que 90 s?  → status: "offline"
                        (TTL = 2–3 × intervalo del heartbeat)
observador  status = "online"   → punto verde
            status = "offline"  → "visto a las 12:41"  ← lastActiveAt, la parte durable
```

El mismo contrato como código de SDK — heartbeat hacia afuera, suscripción hacia adentro:

**JavaScript:**

```javascript
// JavaScript / Node.js — Back4app JS SDK
// Presence: heartbeat your own status, subscribe to your contacts'
const heartbeat = () =>
  myPresence.save({ status: 'online', lastActiveAt: new Date() });
await heartbeat();
setInterval(heartbeat, 30_000); // server sweeps offline at 2–3× this

const contacts = new Parse.Query('Presence');
contacts.containedIn('user', myContactIds);
const sub = await contacts.subscribe();
sub.on('update', (p) => setStatus(p.get('user'), p.get('status')));
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// Presence: heartbeat your own status, subscribe to your contacts'
Future<void> heartbeat() async {
  myPresence
    ..set('status', 'online')
    ..set('lastActiveAt', DateTime.now());
  await myPresence.save();
}
Timer.periodic(const Duration(seconds: 30), (_) => heartbeat());

final contacts = QueryBuilder<ParseObject>(ParseObject('Presence'))
  ..whereContainedIn('user', myContactIds);
final sub = await LiveQuery().client.subscribe(contacts);
sub.on(LiveQueryEvent.update, (p) => setStatus(p.get('user'), p.get('status')));
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// Presence: heartbeat your own status, subscribe to your contacts'
func heartbeat() async throws {
    myPresence.status = "online"
    myPresence.lastActiveAt = Date()
    _ = try await myPresence.save()
}
// Fire every 30 s — the server sweeps offline at 2–3× this

let contacts = Presence.query(containedIn(key: "user", array: myContactIds))
let sub = try await contacts.subscribe()
sub.handleEvent { _, e in
    if case .updated(let p) = e { setStatus(p.user, p.status) }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// Presence: heartbeat your own status, subscribe to your contacts'
fun heartbeat() {
    myPresence.put("status", "online")
    myPresence.put("lastActiveAt", Date())
    myPresence.saveInBackground()
}
timer.scheduleAtFixedRate(0, 30_000) { heartbeat() } // sweep at 2–3× this

val contacts = ParseQuery.getQuery<ParseObject>("Presence")
contacts.whereContainedIn("user", myContactIds)
val sub = ParseLiveQueryClient.Factory.getClient().subscribe(contacts)
sub.handleEvent(SubscriptionHandling.Event.UPDATE) { _, p ->
    setStatus(p.getString("user"), p.getString("status"))
}
```

## Eventos de conexión vs. TTL del heartbeat

Los dos modelos de detección, comparados con honestidad — ninguna página de rankings hace esto en una sola tabla:

| | Ciclo de vida de la conexión | Heartbeat + TTL |
| --- | --- | --- |
| Señal de online | Socket abierto | Heartbeat fresco dentro de la ventana |
| Señal de offline | Evento/frame de cierre | Expiración del TTL — sin heartbeat por 2–3 intervalos |
| Velocidad de detección | Instantánea en un cierre limpio | Retraso acotado (hasta el timeout) |
| Fallas silenciosas | **Se le escapan** — un corte de energía no envía frame de cierre | Las atrapa — el silencio es la señal |
| Estado en el servidor | Seguimiento por conexión | Escrituras sin estado a un store con TTL |
| Cómo falla | Usuarios "online" zombis | Breves falsos "offline" por latidos perdidos |

El propio [protocolo WebSocket](https://datatracker.ietf.org/doc/html/rfc6455) enseña la lección: incluye frames de control ping/pong precisamente porque TCP no revela a un par desaparecido, y su taxonomía de close codes reserva el 1006 para "abnormal closure — no close frame received", el caso zombi. La presencia de producción, por lo tanto, apila ambos modelos: eventos de conexión para transiciones instantáneas, expiración de heartbeat como la verdad que atrapa lo que los eventos dejan pasar.

## La máquina de estados

```mermaid
flowchart LR
  accTitle: Máquina de estados de presencia con período de gracia
  accDescr: Un usuario pasa de offline a online al conectar o enviar heartbeat, de online a ausente cuando la app pasa a segundo plano o la entrada queda inactiva, y avanza hacia offline por un período de gracia que arranca en la desconexión o con latidos perdidos y se cancela si el usuario reconecta antes de que termine.
  OFF["offline<br/>(sirve la última conexión)"] -->|"conecta / heartbeat"| ON["online"]
  ON -->|"pestaña oculta · entrada inactiva"| AW["ausente"]
  AW -->|"la actividad se reanuda"| ON
  ON -->|"desconexión o latidos perdidos"| GR["período de gracia<br/>30–60 s"]
  AW -->|"desconexión o latidos perdidos"| GR
  GR -->|"reconecta a tiempo"| ON
  GR -->|"el timer dispara → publica"| OFF
```

Dos refinamientos distinguen a las implementaciones pulidas. **Ausente se detecta en el cliente:** el servidor no puede ver una pestaña en segundo plano, pero el navegador sí — la [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) marca las pestañas ocultas, los listeners de entrada marcan la inactividad, y una señal de offline de mejor esfuerzo al cerrar la pestaña viaja por la [Beacon API](https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API). **Offline lleva debounce:** el estado de gracia existe porque las conexiones móviles oscilan — publica "offline" en cada túnel y ascensor, y la lista de contactos de cada observador parpadea en solidaridad. Retrasa el anuncio, cancela en la reconexión, y la oscilación colapsa en quietud. Una distinción más que vale la pena robarle al protocolo que formalizó la presencia: presencia *computada* (online, por la conexión) versus estado *definido por el usuario* (no molestar) — el segundo siempre gana el merge.

## Efímera por diseño

La presencia es una caché de la realidad, no un registro: un estado que llega tarde es peor que ninguno — el "online" de ayer es una mentira hoy. Ese principio guía las decisiones de implementación: los estados viven en stores rápidos con TTL en lugar de tablas durables, nunca se encolan para entrega offline y son el primer dato que se descarta bajo backpressure. Solo `lastActiveAt` merece persistencia. **Los indicadores de escritura** son el principio llevado al extremo — micro-presencia acotada a una conversación, que expira en segundos, con debounce en el emisor y descartada en lugar de reintentada; si "Ada está escribiendo…" no puede llegar ahora, no debería llegar nunca.

## El problema del fan-out

El muro de escala de la presencia es la multiplicación, no el almacenamiento. Un ejemplo con números: 100.000 usuarios concurrentes, cada uno visible para 50 contactos, cada uno cambiando de estado unas modestas 20 veces por hora — eso da 100.000 × 50 × 20 = **100 millones de eventos de presencia por hora** para entregar, desde una feature que guarda una fila pequeña por usuario. Las palancas, en el orden en que se tiran: **suscríbete de forma estrecha** — observa a los usuarios en pantalla (la lista de conversaciones abierta), no el grafo completo de contactos; **consulta, no empujes, la cola larga** — busca la última conexión cuando se abre un perfil en lugar de transmitirla en stream; **limita la difusión en grupos** — pasados unos cientos de miembros, muestra presencia en la interacción, no para toda la lista; y **agrupa las transiciones** para que un usuario que oscila cueste un evento con debounce, no treinta. El fan-out en sí corre sobre la maquinaria estándar de [pub/sub](/glossary/es/patron-pub-sub/) — la presencia es el caso extremo at-most-once de ese patrón, donde descartar un evento viejo es una feature.

## Multi-dispositivo y privacidad

Un usuario, tres dispositivos, un punto — la presencia por *usuario* es un merge sobre la presencia por *dispositivo*. Rastrea el heartbeat de cada dispositivo por separado; el usuario está online mientras cualquier dispositivo lo esté, y la política de merge es "gana el más disponible": online en el teléfono le gana a inactivo en el escritorio, y el no-molestar de cualquier dispositivo se impone sobre el resto. La última conexión reporta el dispositivo más reciente. La privacidad es la otra mitad del diseño, y la mitad que los blogs de ingeniería omiten pese a ser lo que los usuarios más preguntan: reglas de visibilidad (todos / contactos / nadie), reciprocidad (oculta la tuya, pierde de vista la de los demás) y última conexión gruesa ("recientemente" en lugar de horarios exactos). Aplica las reglas en los permisos de la capa de datos — la presencia es dato de comportamiento, y un "online a las 3 a. m." filtrado es una divulgación real.

## Casos de uso comunes

- **Chat y mensajería** — el punto verde canónico, la última conexión y los indicadores de escritura.
- **Herramientas de colaboración** — quién está en el documento, presencia de cursor, barras laterales de "activos ahora".
- **Soporte y marketplaces** — enrutamiento por disponibilidad de agentes, señales de confianza tipo "el vendedor está online".
- **Multijugador y social** — lobbies, listas de amigos, atajos para unirte a tu amigo.
- **Herramientas de fuerza laboral** — estados de disponibilidad alimentando enrutamiento y dashboards de estado.

## ¿Cómo deberías construir presencia? Matriz de decisión

| Feature | Mecanismo |
| --- | --- |
| Punto verde en los contactos | Heartbeat + store con TTL, suscripción de live query sobre los usuarios visibles |
| Transiciones instantáneas en un chat abierto | Eventos de conexión como camino rápido sobre el socket |
| Última conexión | Timestamp en cada heartbeat; consulta a demanda |
| Detección de ausencia | En el cliente: listeners de visibilidad e inactividad, reportados en el heartbeat |
| Indicador de escritura | Micro-eventos por conversación, TTL de segundos, nunca persistidos |
| Presencia en un grupo de 5.000 miembros | No difundas — muestra en la interacción, limita la lista |

## Limitaciones y trade-offs

- **La presencia es probabilística.** Entre heartbeats, el punto es una conjetura; los sistemas honestos abrazan la obsolescencia acotada en lugar de fingir verdad instantánea.
- **La frescura cuesta escrituras.** Reducir el intervalo de heartbeat a la mitad duplica la carga de escritura de toda tu población online — la velocidad de detección se compra en throughput.
- **El fan-out escala con el éxito.** La feature es barata con 1.000 usuarios y un proyecto de arquitectura con 10 millones; diseña el alcance de las suscripciones antes de que el crecimiento lo fuerce.
- **La oscilación es inherente.** Las redes móviles garantizan churn de reconexión; sin debounce, la presencia amplifica el ruido de red en ruido de interfaz.
- **Tiene forma de vigilancia.** Los patrones de conexión revelan sueño, trabajo y hábitos; los controles de visibilidad y su aplicación en la capa de permisos son requisitos, no mejoras.

## Presencia en Back4app

Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. Los bloques de arriba mapean uno a uno: una clase `Presence` guarda `status` y `lastActiveAt`; el heartbeat es un save periódico (las pestañas de código); los observadores mantienen una suscripción de [Live Query](/glossary/es/live-queries-tiempo-real/) sobre los contactos que están en pantalla, recibiendo actualizaciones por push sobre la flota gestionada de [WebSockets](/glossary/es/websockets/) de la plataforma; un job programado de Cloud Code es el barrido de TTL, pasando a offline las filas viejas y disparando el debounce; y los permisos a nivel de clase más las ACLs implementan las reglas de visibilidad en la capa de datos, donde deben vivir. Nada a medida que operar — la presencia se vuelve un ejercicio de modelado de datos sobre infraestructura que el backend ya corre.
