Visão geral
O SDK Web do Notti registra o navegador do visitante no Notti e entrega o service worker que exibe a notificação e reporta o engajamento (received / opened / clicked). Um push web é enviado como qualquer outro push: pelo dashboard, pela API ou pelo MCP, para um segmento, para external user ids, para ids de dispositivo ou para todos os dispositivos, e web é mais uma plataforma ao lado de ios e android. Ele usa o protocolo Web Push (RFC 8030) com VAPID (RFC 8292) e criptografia de payload aes128gcm (RFC 8291), então o Notti fala com o serviço de push do fabricante do navegador e nenhum projeto Firebase está envolvido.
O SDK é um pacote TypeScript sem dependências. Ele fica em zeeplabs/zeep-notti-web e está publicado no npm como notti-web-sdk. Está em 0.x, então a API ainda pode mudar entre versões menores.
Por padrão o SDK fala com o Notti (https://app.zeepnotti.app), então Notti.init só precisa do app id e da client key.
Requisitos
- Um site servido por
https(localhosté permitido em desenvolvimento). Os navegadores só expõem service workers e push em contexto seguro. - Um App do Notti com web push habilitado (próxima seção) e seu app id e client key. Nunca coloque a REST key em uma página web.
- Um backend do Notti que inclua web push. Um backend mais antigo responde
404 web_push_not_enablede o SDK informanot_enabled. - Um navegador com Service Workers e Push API: Chrome, Edge e Firefox no desktop e no Android, Safari 16 ou superior no macOS, e Safari no iOS e iPadOS 16.4 ou superior somente depois que o site for adicionado à Tela de Início (veja Suporte por navegador).
Habilitar o web push no App
No dashboard abra Configurações → Push e In-App → Web e habilite o web push. Somente owners e developers podem. O Notti gera um par de chaves VAPID uma única vez e o SDK do navegador lê a chave pública; a chave privada nunca sai do Notti. Defina o subject como um endereço mailto: ou https: que você controla: os serviços de push o usam para falar com você se seus pushes se comportarem mal. O subject é obrigatório e o Notti nunca o preenche por você, porque ele é enviado à Apple, ao Google e à Mozilla a cada push: prefira uma caixa de função a um endereço pessoal.
Desabilitar o web push mantém o par de chaves e os navegadores registrados, mas os pushes para eles falham até você habilitar de novo. Ainda não há rotação de chave no dashboard. Se uma chave mudar no futuro, o SDK percebe no próximo init e registra uma nova assinatura sem pedir de novo ao visitante.
Instalação
npm install notti-web-sdkO pacote traz o SDK (módulo ES, CommonJS e tipos) e o service worker dist/notti-sw.js, também exportado como notti-web-sdk/sw. Você copia o worker de node_modules/notti-web-sdk/dist/notti-sw.js para o seu site na próxima seção. Para compilar a partir do código-fonte, clone o repositório e rode pnpm install && pnpm build.
Servir o service worker
Os navegadores exigem que o worker seja um arquivo na sua própria origem. Copie dist/notti-sw.js para o seu site de modo que ele seja servido como /notti-sw.js:
- Sirva a partir da raiz. Um worker controla as páginas do seu caminho e abaixo dele, então
/notti-sw.jscobre o site inteiro, enquanto/assets/notti-sw.jscobre só/assets/. Para servir de um subcaminho e ainda cobrir o site, envie o headerService-Worker-Allowed: /. - Use o arquivo que acompanha a mesma versão do SDK que você importa. O worker carrega uma string de versão e o SDK a compara a cada registro: quando diferem, o SDK pede ao navegador que busque o arquivo de novo e registra um aviso no console, porque seu host pode ainda estar servindo uma cópia antiga.
- Sirva como JavaScript e evite cache longo (
Cache-Control: no-cache), para que uma nova versão chegue aos visitantes. - Não embrulhe, não minifique de novo nem edite. Se o seu site já tem um service worker, carregue o do Notti a partir do seu com
importScripts('/notti-sw.js')e defina a opçãoserviceWorkerPathcom o seu próprio worker, para que o SDK registre o seu e não o arquivo do Notti (que o substituiria). Mantenha os dois arquivos na mesma origem. Um registro tem uma única assinatura de push, então o serviço de push é o que este SDK assinou. A partir do SDK 0.1.1 o worker do Notti age só nas notificações que trazem os ids do Notti e deixa as suas próprias notificações em paz.
Inicializar
Chame init quando a página carregar. Ele nunca pede permissão e nunca lança exceção:
import { Notti } from 'notti-web-sdk';
const { supported, reason } = await Notti.init({
appId: 'YOUR_APP_ID',
clientKey: 'YOUR_CLIENT_KEY',
host: 'https://notti.example.com', // opcional, o padrão é https://app.zeepnotti.app
serviceWorkerPath: '/notti-sw.js', // opcional, este é o padrão
});O init verifica o navegador primeiro e depois carrega a chave pública do App. Quando supported é false, reason diz o motivo:
reason | Significado |
|---|---|
insecure_context | A página não é servida por https (nem localhost). |
ios_requires_home_screen | Aba de navegador no iOS ou iPadOS: o site precisa ser adicionado à Tela de Início antes. |
no_service_worker | O navegador não tem suporte a Service Worker. |
no_push_manager | O navegador não tem Push API ou Notification. |
not_enabled | O web push não está habilitado para este App. |
request_failed | A chave pública não pôde ser carregada (rede, chave inválida, erro do servidor). |
invalid_options | appId, clientKey ou host ausente ou inválido. |
Use o motivo para decidir o que sua interface mostra, por exemplo uma dica para instalar o site no iOS.
Se o visitante já ativou as notificações pelo SDK e a permissão continua concedida, o init também confere a assinatura do navegador e registra uma nova quando o endpoint ou a chave do App mudou. Ele nunca pede permissão e nunca assina um visitante que não aceitou.
Pedir permissão
Chame requestPermission a partir de um gesto do usuário, como um clique. Os navegadores ignoram ou bloqueiam o pedido caso contrário, e o Safari exige isso:
button.addEventListener('click', async () => {
const result = await Notti.requestPermission();
// { permission: 'granted' | 'denied' | 'default', registered: boolean, deviceId?, reason? }
});Quando o visitante concede, o SDK cria (ou reaproveita) a assinatura push e registra um Device com platform: "web". Chamar de novo, ou recarregar a página, não cria duplicata. Quando o visitante nega ou dispensa o pedido, nada é registrado e o SDK nunca pergunta de novo por conta própria.
Se registered for false apesar de a permissão estar concedida, reason é not_initialized (chame init antes), not_enabled, subscribe_failed (o navegador recusou ou o worker não estava pronto) ou register_failed (o Notti não pôde ser alcançado; o SDK tenta de novo no próximo init).
Notti.getSubscription() retorna { deviceId, endpoint } do navegador registrado, ou null.
Identidade e tags
Elas se comportam como nos SDKs mobile:
await Notti.identify('user-42');
await Notti.setTags({ plan: 'pro', beta: true });
await Notti.setTags({ beta: null }); // null remove a chave
await Notti.removeTags(['plan']);
await Notti.logout();identifyvincula o navegador ao seu próprio id de usuário, para que um push endereçado aexternal_user_idso alcance. Um id vazio não faz nada.setTagsmescla nas tags já definidas e envia o mapa completo resultante. Os valores são strings, números ou booleanos; qualquer outra coisa é ignorada.logoutdesvincula o navegador do usuário (external_user_id: null). As tags e a assinatura são mantidas.- Chamadas feitas antes de o navegador ser registrado são guardadas e enviadas junto com o registro. Uma chamada que falha é repetida no próximo
init. Nenhum desses métodos lança exceção.
O id de usuário é definido pelo navegador e não é verificado. A Client key é pública, então quem a tiver pode registrar um navegador com qualquer external_user_id e passaria a receber os pushes que você envia para esse usuário. Não coloque num push nada que só o próprio usuário possa ler, ou envie para ids de dispositivo. Os registros feitos com uma Client key são limitados a 120 por minuto para cada IP de cliente. Passando disso a API responde 429, e o SDK tenta de novo no próximo init.
O que o service worker faz
- Exibe todo push. O worker sempre chama
showNotification, mesmo para um payload que não consegue ler (nesse caso mostra uma notificação genérica com o hostname do seu site como título). É de propósito: um navegador pode revogar a permissão de um site que recebe um push e não mostra nada. - Reporta o engajamento. Reporta
receiveduma vez para cada notificação que exibiu, eclickedeopenedquando o visitante clica. Dispensar uma notificação não reporta nada. As requisições de evento são repetidas algumas vezes e depois descartadas, e nunca atrasam a notificação. - Abre o destino do clique. Veja abaixo.
- Registra de novo em
pushsubscriptionchange. Quando o navegador troca a assinatura e a permissão continua concedida, o worker registra a nova.
Destino do clique
O destino do clique é a chave url do data da notificação:
{
"title": "Spring sale",
"body": "20% off today",
"data": { "url": "/offers" }
}- Um caminho (
/offers) abre no seu site. Se já houver uma janela nessa página, ela recebe o foco; senão uma nova é aberta. - Uma URL
httpsabsoluta em outra origem é aberta em uma nova janela e nunca dá foco a uma existente. - Sem
url, ou com uma URL que não éhttp(s), abre a raiz do seu site.
Limites e o que não é suportado
- Tamanho do payload. O Web Push permite 4096 bytes no total, o que deixa 3993 bytes para a mensagem que o Notti monta com título, corpo, URL da imagem, tag e
data. Uma maior falha aquela Delivery comopayload_too_large, sem nova tentativa, e não afeta as Deliveries de iOS e Android da mesma notificação. - Notificações data-only. Uma notificação sem título nem corpo é ignorada para Devices web (
web_data_only_unsupported). Push silencioso não existe na web: o navegador precisa mostrar algo a cada push. - Imagem. A
imageé repassada ao navegador e ignorada onde não é suportada. O Safari não a exibe. - Collapse e prioridade. O collapse id, a prioridade e o TTL de uma notificação viram os headers
Topic,UrgencyeTTLda requisição de push. - Navegadores que sumiram. Quando um serviço de push responde
404ou410, o visitante cancelou a assinatura ou revogou a permissão: a Delivery falha de forma permanente e o Device deixa de ser alvo. - Limitação de taxa. Quando um serviço de push responde
429comRetry-After, o Notti espera esse tempo (até 5 minutos) antes de tentar de novo e desiste depois de cerca de 20 tentativas.
Suporte por navegador
| Navegador | Situação |
|---|---|
| Chrome (desktop) | Validado contra um serviço de push real. |
| Edge, Firefox (desktop) | Deve funcionar, ainda não validado. |
| Chrome, Firefox (Android) | Deve funcionar, ainda não validado. |
| Safari no macOS 13 (Safari 16) ou superior | Deve funcionar, ainda não validado. A permissão precisa vir de um gesto do usuário. |
| Safari no iOS e iPadOS 16.4 ou superior | Deve funcionar só como web app na Tela de Início, ainda não validado. |
"Deve funcionar" significa que o navegador implementa o padrão Web Push que o Notti segue; o Notti ainda não verificou naquele navegador. Veja Limitações conhecidas.
iOS e iPadOS. O push só funciona para um site que o visitante adicionou à Tela de Início, e o site precisa de um web app manifest cujo display seja standalone ou fullscreen:
{
"name": "My site",
"start_url": "/",
"display": "standalone",
"icons": [{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }]
}<link rel="manifest" href="/manifest.webmanifest" />Em uma aba normal do navegador o init retorna ios_requires_home_screen; mostre ao visitante como usar Compartilhar → Adicionar à Tela de Início e peça a permissão de dentro do app instalado. O Safari revoga a permissão quando um push não é exibido na hora, mais um motivo para o worker sempre exibi-lo.
Notas para self-hosted
- Sua instância do Notti precisa ser alcançável por
httpsa partir dos navegadores dos seus visitantes, e seu site também precisa serhttps. - O servidor envia cada push ao serviço de push do fabricante do navegador, então precisa de saída
https. Hosts conhecidos sãofcm.googleapis.com,updates.push.services.mozilla.com,*.push.apple.come*.notify.windows.com; o Notti não mantém lista de permissão, porque os fabricantes mudam. Ele só segue endpointshttpsem endereços públicos e nunca segue redirecionamentos, então um firewall que bloqueie esse tráfego faz as Deliveries web falharem. - Os navegadores chamam o Notti a partir da origem do seu site. As rotas que o SDK usa (chave pública, registro e atualização de dispositivo, eventos de notificação) respondem a requisições cross-origin de qualquer origem e autenticam com a client key no header
Authorization, nunca com cookie. Coloque um proxy na frente do Notti somente se ele repassar requisiçõesOPTIONSe esses headers. - Se o Notti receber
403de um serviço de push, as credenciais VAPID foram rejeitadas; o owner recebe o mesmo e-mail de alerta de uma credencial FCM ou APNs ausente, no máximo uma vez por dia por App.
Limitações conhecidas
Estes pontos ainda não foram confirmados em dispositivos reais, então trate-os como em aberto:
Só o Chrome no desktop foi validado contra um serviço de push real. Os outros navegadores da tabela acima seguem o mesmo padrão, mas o Notti ainda não os verificou.
Códigos de erro dos serviços de push do FCM e do Edge. Só
404,410,429,5xxe os motivos do403da Apple estão documentados. O Notti trata401ou403de outro fabricante como falha de credencial e qualquer resposta desconhecida como passível de nova tentativa.Limite exato de payload da Apple. A Apple diz "4 KB"; o Notti aplica os 3993 bytes padrão em todo lugar.
Dar foco ou abrir uma janela a partir do clique em uma notificação no iOS. Há bugs relatados em web apps da Tela de Início; o comportamento na versão atual do iOS ainda será confirmado. Um clique sempre é reportado como
clicked.pushsubscriptionchangeno iOS. Os navegadores divergem sobre se ele dispara. O SDK também confere a assinatura a cadainit, então uma mudança é percebida na próxima vez que o visitante abrir o seu site.A linha antiga do Device de uma assinatura substituída permanece até que um push para ela responda
404ou410.O SDK não tem build para tag de script (CDN); use um bundler.