---
term: 'Presença (Status Online)'
seoTitle: 'Presença e Status Online: Heartbeats, Visto por Último e Fan-out'
headline: 'O que é presença (status online)?'
slug: presenca-status-online
category: api-realtime
shortDefinition: 'Presença é um sinal em tempo real que indica se um usuário está online, ausente ou offline, mantido fresco por conexões e heartbeats.'
relatedTerms:
  - real-time-live-queries
  - websockets-real-time-sync
  - pub-sub-pattern
  - sse-vs-websockets-vs-polling
contrastsWith:
  - push-notifications-apns-fcm
aboutTerms:
  - 'Status Online'
  - 'Visto por Último'
  - 'Heartbeat'
  - 'Indicadores de Digitação'
faq:
  - question: 'O que é um sistema de presença?'
    answer: 'Um serviço que rastreia se cada usuário está online, ausente ou offline e transmite as mudanças, em tempo real, para quem tem permissão de vê-las. Por baixo do capô: conexões persistentes ou heartbeats periódicos escrevendo em um store rápido em memória, com as mudanças de status espalhadas por pub/sub.'
  - question: 'Como funciona o "visto por último"?'
    answer: 'O servidor grava um timestamp a cada heartbeat e a cada desconexão. Enquanto você está online, o status ao vivo é servido; quando você fica offline, o último timestamp gravado responde no lugar dele. É a única peça durável de um sistema no resto efêmero — o status expira, o visto por último permanece.'
  - question: 'Qual intervalo de heartbeat usar em presença?'
    answer: 'Trinta segundos é o padrão comum em produção, com o timeout de offline em duas a três vezes o intervalo (60 a 90 segundos). Intervalos mais curtos reduzem o atraso de detecção, mas multiplicam a carga de escrita linearmente com sua população online; o intervalo também precisa ficar abaixo dos timeouts de idle da infraestrutura, ou os proxies matam as conexões quietas antes.'
  - question: 'Detecção por conexão ou por heartbeat — qual é melhor?'
    answer: 'As duas, para falhas diferentes. Eventos de conexão (abrir/fechar) dão transições instantâneas, mas não pegam mortes silenciosas — um aparelho que perde energia não envia frame de fechamento e deixa uma conexão zumbi. A expiração do heartbeat limita essa defasagem ao custo de um atraso de detecção. Sistemas de produção usam eventos de conexão como caminho rápido e o TTL do heartbeat como fonte da verdade.'
  - question: 'Como evitar que o status online fique piscando?'
    answer: 'Faça debounce da transição para offline: na desconexão, inicie um timer de carência — comumente de 30 a 60 segundos — e cancele se o usuário reconectar, publicando "offline" só quando o timer disparar. Usuários em elevadores e túneis de metrô reconectam o tempo todo; sem o período de carência, toda lista de contatos em que eles aparecem pisca junto.'
  - question: 'Como a presença escala para milhões de usuários?'
    answer: 'Respeitando a matemática do fan-out: toda mudança de status precisa chegar a todo observador, então N contatos vezes M transições explode rápido. As alavancas são um store em memória com TTL para o status, inscrever-se apenas nos usuários visíveis (a lista de conversas abertas, não a agenda inteira), buscar o visto por último sob demanda em vez de empurrá-lo, e limitar a transmissão de status em grupos grandes.'
  - question: 'Como funciona presença com vários dispositivos?'
    answer: 'Rastreie uma conexão ou heartbeat por dispositivo e faça merge: o usuário está online enquanto qualquer dispositivo estiver, e offline só quando o último se cala. A política de merge usual é "o mais disponível vence" — online no celular ganha de ocioso no desktop — com o visto por último tirado do dispositivo mais recente.'
  - question: 'Dá para esconder o status online?'
    answer: 'Deveria dar — visibilidade é feature de produto, não detalhe posterior: regras como todos, só contatos ou ninguém, normalmente com reciprocidade (esconda o seu e você deixa de ver o dos outros). Dados de presença são dados comportamentais; defina quem pode observar quem no modelo de permissões, não na interface.'
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: 'Bolinha verde sem infraestrutura'
  text: 'Construa presença no Back4app com o que a plataforma já roda: uma classe Presence, saves de heartbeat, uma subscription de Live Query para a sua lista de contatos e uma varredura agendada para os timeouts — permissões inclusas.'
  linkText: 'Comece grátis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-08-25'
translationKey: presence-online-status
---

**Presença é um sinal em tempo real que indica se um usuário está online, ausente ou offline, mantido fresco por conexões e heartbeats.** A bolinha verde é uma interface enganosamente simples sobre um problema distribuído genuinamente difícil: o servidor precisa *inferir* a ausência — dispositivos raramente anunciam a própria morte — e depois transmitir cada inferência para todos que estão observando, na escala de uma lista de contatos. Padronizado muito antes do chat moderno, nas [stanzas de presença do XMPP](https://datatracker.ietf.org/doc/html/rfc6121), o padrão tem fundamentos que não mudaram desde então.

## Principais pontos

| Pergunta | Resposta |
| --- | --- |
| Os estados | online · ausente/ocioso · offline — mais overrides definidos pelo usuário (não perturbe) |
| O mecanismo | Escritas de heartbeat + expiração por TTL; eventos de conexão como caminho rápido |
| Os números | Heartbeat de ~30 s · timeout de offline em 2–3× o intervalo · 30–60 s de carência contra oscilação |
| O companheiro | Visto por último — o timestamp durável por trás da bolinha efêmera |
| A parte difícil | Fan-out: N observadores × M mudanças de status, multiplicados pelo seu sucesso |

## O contrato do heartbeat

O padrão inteiro cabe em uma troca:

```text
cliente     a cada 30 s:  save { status: "online", lastActiveAt: now }
servidor    a cada 60 s:  varredura — lastActiveAt mais velho que 90 s?  → status: "offline"
                          (TTL = 2–3 × intervalo do heartbeat)
observador  status = "online"   → bolinha verde
            status = "offline"  → "visto às 12:41"  ← lastActiveAt, a parte durável
```

O mesmo contrato como código de SDK — heartbeat para fora, subscription para dentro:

**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 conexão vs. TTL de heartbeat

Os dois modelos de detecção, comparados com honestidade — nenhuma página de ranking faz isso em uma tabela só:

| | Ciclo de vida da conexão | Heartbeat + TTL |
| --- | --- | --- |
| Sinal de online | Socket aberto | Heartbeat fresco dentro da janela |
| Sinal de offline | Evento/frame de fechamento | Expiração do TTL — sem heartbeat por 2–3 intervalos |
| Velocidade de detecção | Instantânea no fechamento limpo | Atraso limitado (até o timeout) |
| Falhas silenciosas | **Não pega** — queda de energia não envia frame de fechamento | Pega — o silêncio é o sinal |
| Estado no servidor | Rastreamento por conexão | Escritas sem estado em um store com TTL |
| Como falha | Usuários "online" zumbis | Falso "offline" breve em batidas perdidas |

O [protocolo WebSocket](https://datatracker.ietf.org/doc/html/rfc6455) em si ensina a lição: ele traz frames de controle ping/pong justamente porque o TCP não revela um par que sumiu, e sua taxonomia de close codes reserva o 1006 para "fechamento anormal — nenhum frame de fechamento recebido", o caso zumbi. Presença de produção, portanto, empilha os dois modelos: eventos de conexão para transições instantâneas, expiração de heartbeat como a verdade que pega o que os eventos deixam passar.

## A máquina de estados

```mermaid
flowchart LR
  accTitle: Máquina de estados de presença com período de carência
  accDescr: Um usuário vai de offline para online ao conectar ou ao enviar heartbeat, de online para ausente quando o app vai para segundo plano ou a entrada fica ociosa, e caminha para offline por um período de carência que começa na desconexão ou em batidas perdidas e é cancelado se o usuário reconectar antes de ele terminar.
  OFF["offline<br/>(serve o visto por último)"] -->|"conecta / heartbeat"| ON["online"]
  ON -->|"aba oculta · entrada ociosa"| AW["ausente"]
  AW -->|"atividade retoma"| ON
  ON -->|"desconexão ou batidas perdidas"| GR["período de carência<br/>30–60 s"]
  AW -->|"desconexão ou batidas perdidas"| GR
  GR -->|"reconecta a tempo"| ON
  GR -->|"timer dispara → publica"| OFF
```

Dois refinamentos distinguem as implementações bem-acabadas. **Ausente é detectado no cliente:** o servidor não enxerga uma aba em segundo plano, mas o navegador sim — a [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) sinaliza abas ocultas, listeners de entrada sinalizam ociosidade, e um sinal de offline em melhor esforço no fechamento da aba viaja pela [Beacon API](https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API). **Offline tem debounce:** o estado de carência existe porque conexões móveis oscilam — publique "offline" a cada túnel e elevador e a lista de contatos de cada observador pisca em solidariedade. Atrase o anúncio, cancele na reconexão, e a oscilação colapsa em quietude. Mais uma distinção que vale roubar do protocolo que formalizou presença: presença *computada* (online, pela conexão) versus status *definido pelo usuário* (não perturbe) — o segundo sempre ganha o merge.

## Efêmero por design

Presença é um cache da realidade, não um registro: um status que chega atrasado é pior que status nenhum — o "online" de ontem é mentira hoje. Esse princípio guia as escolhas de implementação: os status vivem em stores rápidos com TTL em vez de tabelas duráveis, nunca são enfileirados para entrega offline e são o primeiro dado descartado sob backpressure. Só o `lastActiveAt` merece persistência. **Indicadores de digitação** são o princípio no extremo — micro-presença escopada a uma conversa, expirando em segundos, com debounce no remetente e descartada em vez de retentada; se "Ada está digitando…" não pode chegar agora, não deveria chegar nunca.

## O problema do fan-out

O muro de escala da presença é multiplicação, não armazenamento. Um exemplo real: 100.000 usuários simultâneos, cada um visível para 50 contatos, cada um mudando de estado modestos 20 vezes por hora — isso dá 100.000 × 50 × 20 = **100 milhões de eventos de presença por hora** para entregar, vindos de uma feature que guarda uma linha pequena por usuário. As alavancas, na ordem em que se puxa: **inscreva-se de forma estreita** — observe os usuários que estão na tela (a lista de conversas abertas), não o grafo inteiro de contatos; **puxe, não empurre, a cauda longa** — busque o visto por último quando um perfil abrir, em vez de transmiti-lo; **limite a transmissão em grupos** — acima de algumas centenas de membros, mostre presença na interação, não para a lista toda; e **agrupe transições** para que um usuário oscilando custe um evento com debounce, não trinta. O próprio fan-out roda sobre a maquinaria padrão de [pub/sub](/glossary/pt/padrao-pub-sub/) — presença é o caso de canto at-most-once desse padrão, em que descartar um evento velho é feature.

## Vários dispositivos e privacidade

Um usuário, três dispositivos, uma bolinha — presença por *usuário* é um merge sobre presença por *dispositivo*. Rastreie o heartbeat de cada dispositivo separadamente; o usuário está online enquanto qualquer um estiver, e a política de merge é "o mais disponível vence": online no celular ganha de ocioso no desktop, e o não-perturbe de qualquer dispositivo sobrepõe os demais. O visto por último reporta o dispositivo mais recente. Privacidade é a outra metade do design, e a metade que os blogs de engenharia pulam apesar de ser o que os usuários mais perguntam: regras de visibilidade (todos / contatos / ninguém), reciprocidade (esconda o seu, perca a visão do dos outros) e visto por último grosseiro ("recentemente" em vez de horário). Aplique as regras nas permissões da camada de dados — presença é dado comportamental, e um "online às 3 da manhã" vazado é uma divulgação real.

## Casos de uso comuns

- **Chat e mensageria** — a bolinha verde canônica, o visto por último e os indicadores de digitação.
- **Ferramentas de colaboração** — quem está no documento, presença de cursor, barras laterais de "ativos agora".
- **Suporte e marketplaces** — roteamento por disponibilidade de atendente, sinais de confiança do tipo "vendedor online".
- **Multiplayer e social** — lobbies, listas de amigos, atalhos para entrar junto com um amigo.
- **Ferramentas de força de trabalho** — estados de disponibilidade alimentando roteamento e dashboards de status.

## Como você deveria construir presença? Matriz de decisão

| Feature | Mecanismo |
| --- | --- |
| Bolinha verde nos contatos | Heartbeat + store com TTL, subscription de live query nos usuários visíveis |
| Transições instantâneas em um chat aberto | Eventos de conexão como caminho rápido no socket |
| Visto por último | Timestamp a cada heartbeat; busca sob demanda |
| Detecção de ausência | No cliente: listeners de visibilidade e ociosidade, reportados no heartbeat |
| Indicador de digitação | Micro-eventos por conversa, TTL de segundos, nunca persistidos |
| Presença em um grupo de 5.000 membros | Não transmita — mostre na interação, limite a lista |

## Limitações e trade-offs

- **Presença é probabilística.** Entre heartbeats, a bolinha é um palpite; sistemas honestos abraçam a defasagem limitada em vez de fingir verdade instantânea.
- **Frescor custa escritas.** Reduzir o intervalo de heartbeat pela metade dobra a carga de escrita de toda a população online — velocidade de detecção se compra em throughput.
- **Fan-out escala com o sucesso.** A feature é barata com 1.000 usuários e vira projeto de arquitetura com 10 milhões; defina o escopo das subscriptions antes que o crescimento force.
- **Oscilação é inerente.** Redes móveis garantem churn de reconexão; sem debounce, a presença amplifica ruído de rede em ruído de interface.
- **O formato é de vigilância.** Padrões de horário online revelam sono, trabalho e hábitos; controles de visibilidade e aplicação na camada de permissões são requisitos, não melhorias.

## Presença no Back4app

O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. Os blocos de construção acima mapeiam um a um: uma classe `Presence` guarda `status` e `lastActiveAt`; o heartbeat é um save periódico (as abas de código); observadores mantêm uma subscription de [Live Query](/glossary/pt/live-queries-tempo-real/) nos contatos que estão na tela, recebendo atualizações por push sobre a frota gerenciada de [WebSockets](/glossary/pt/websockets/) da plataforma; um job agendado no Cloud Code é a varredura de TTL, virando linhas velhas para offline e disparando o debounce; e permissões de classe mais ACLs implementam as regras de visibilidade na camada de dados, onde elas devem morar. Nada sob medida para operar — presença vira um exercício de modelagem de dados sobre infraestrutura que o backend já roda.
