> ## Documentation Index
> Fetch the complete documentation index at: https://docs.octasend.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Widget In-App

> Instale o sino de notificações da OctaSend no seu produto, verifique a identidade dos usuários e envie notificações in-app pela API

O canal `in_app` mostra notificações **dentro do seu produto**: um sino com o número de não lidas, uma lista com o histórico e um aviso na tela quando chega uma notificação nova. Não há credenciais de provedor para configurar — a infraestrutura fica do lado da OctaSend.

Para funcionar, três partes precisam usar o **mesmo identificador de usuário**:

1. o widget, instalado no seu site e iniciado com o `userId` do usuário logado;
2. uma comunicação com o canal In-App, criada no painel;
3. a chamada ao `send-message` com `recipient.user_id` igual ao `userId` do widget.

## Antes de começar

* **Canal liberado:** o canal In-App precisa estar liberado para a sua conta. Se ele não aparecer em **Configurações → Canais**, fale com a OctaSend.
* **Guia no painel:** em **Configurações → Canais → In-app (dentro da aplicação)** você encontra o guia de integração com o script já montado.
* **Canal habilitado:** na mesma página, a chave **Canal habilitado** precisa estar ligada. Com ela desligada, os envios In-App não são feitos: o canal volta como `skipped` e aparece em **Logs** com o motivo `Canal in-app desabilitado`.
* **OAuth Client:** crie um em **Configurações → OAuth Clients**. O widget usa só o `client_id`, que é público e pode ficar no código do seu site. Veja [Autenticação](https://docs.octasend.com.br/api-reference/autenticacao.md).

> [!WARNING]
>
> Nunca coloque o `client_secret` nem o segredo de verificação de identidade no site. O widget não precisa deles.

## Instalar o widget

**1. Adicione o script**

Cole no `<head>` ou antes do fechamento do `<body>`:

```html theme={null}
<script src="https://api.octasend.com.br/functions/v1/in-app-widget"></script>
```

**2. Inicialize depois do login**

Chame `OctaSendInApp.init` quando souber quem é o usuário logado:

```html theme={null}
<script>
  OctaSendInApp.init({
    clientId: 'SEU_CLIENT_ID',
    userId:   'id-do-usuario-no-seu-sistema',
    userHash: 'HASH_CALCULADO_NO_SEU_SERVIDOR', // veja "Verificação de identidade"
    theme:    'auto',     // 'light' | 'dark' | 'auto'
    locale:   'pt-BR',    // 'pt-BR' | 'en' | 'es'
    // target: '#meu-sino',                        // opcional
    // onClick: (notificacao) => { console.log(notificacao) },
  })
</script>
```

**3. Desconecte no logout**

Quando o usuário sair, remova o widget da página:

```javascript theme={null}
OctaSendInApp.disconnect()
```

### Opções do `init`

| Opção | Obrigatória | Descrição |
| - | - | - |
| `clientId` | Sim | `client_id` do OAuth Client. O nome `client_id` também é aceito |
| `userId` | Sim | Identificador do usuário **no seu sistema** (ex.: `user_42`). Use o mesmo valor em `recipient.user_id` ao enviar |
| `userHash` | Com a verificação exigida | Assinatura do `userId` calculada no seu servidor. Veja **Verificação de identidade**, abaixo |
| `target` | Não | Seletor CSS ou elemento onde o sino é montado (ex.: `'#meu-sino'`, dentro do seu cabeçalho). Sem ele, o sino flutua no canto superior direito da página |
| `theme` | Não | `light`, `dark` ou `auto` (padrão). `auto` segue o tema claro ou escuro do sistema do usuário |
| `locale` | Não | Idioma dos textos do widget: `pt-BR` (padrão), `en` ou `es` |
| `onClick` | Não | Função chamada quando o usuário clica numa notificação. Recebe a notificação com `id`, `title`, `body`, `url`, `category`, `icon_url`, `read_at` e `created_at` |

* Sem `clientId` ou sem `userId`, o widget não é montado e registra `[OctaSendInApp] clientId e userId são obrigatórios` no console do navegador.
* Se o seletor de `target` não existir na página, o widget não é montado e o `init` falha com o erro `OctaSendInApp: target "..." não encontrado`.
* Chamar `init` de novo (por exemplo, ao trocar de usuário) substitui a instância anterior.

## Como o widget funciona

* Ao iniciar, carrega as notificações do usuário dos **últimos 30 dias**, até 100.
* Notificações novas chegam **em tempo real**, sem recarregar a página. Se a aba estiver visível, aparece um aviso no canto superior direito, que some sozinho depois de 5 segundos.
* O sino mostra quantas notificações não foram lidas; acima de 99, mostra `99+`.
* Clicar numa notificação marca como lida, chama o seu `onClick` (se houver) e, se a notificação tiver link, abre o link na mesma aba.
* Quando há não lidas, a lista mostra a opção **Marcar todas como lidas**.
* Se o usuário estiver offline no momento do envio, a notificação fica guardada e aparece como não lida na próxima vez que ele abrir o seu produto.
* A OctaSend guarda as **100 notificações mais recentes** de cada usuário; as mais antigas são apagadas.
* O widget renova a própria autenticação sozinho antes de ela expirar — não é preciso chamar `init` de novo.
* O visual roda isolado (Shadow DOM): o CSS do seu site não interfere no widget, e o do widget não interfere no seu site.

## Verificação de identidade

O `clientId` e o `userId` ficam no código do seu site. A verificação de identidade garante que o sino só mostre as notificações de um usuário quando o seu próprio sistema confirmou quem ele é: o seu servidor assina o `userId` com um segredo da conta, e o site envia essa assinatura, o `userHash`, junto com o `userId`.

O mesmo segredo e a mesma exigência valem para o registro de aparelhos do [Web Push](https://docs.octasend.com.br/guias/web-push.md).

> [!NOTE]
>
> Só usuários **MASTER** da conta veem o segredo, geram um novo e ligam ou desligam a exigência. Um usuário **PADRÃO** vê na seção só um aviso para pedir a um usuário MASTER da conta. O papel de cada pessoa é definido em [Configurações → Usuários](https://docs.octasend.com.br/plataforma/configuracoes.md).

**1. Gere o segredo**

Em **Configurações → Canais → In-app (dentro da aplicação)**, na seção **Verificação de identidade**, clique em **Gerar segredo**. O segredo aparece mascarado, com um botão para copiar. Guarde-o só no seu servidor, por exemplo numa variável de ambiente.

**2. Calcule o userHash no seu servidor**

O `userHash` é o HMAC-SHA256 do `userId`, com o segredo como chave, em hexadecimal. Calcule sobre o `userId` exatamente como o site vai enviar, sem espaços no início ou no fim:

```javascript Node.js theme={null}
const crypto = require('crypto')

const userHash = crypto
  .createHmac('sha256', process.env.OCTASEND_IN_APP_SECRET)
  .update(userId)
  .digest('hex')
```

```php PHP theme={null}
<?php
$userHash = hash_hmac('sha256', $userId, getenv('OCTASEND_IN_APP_SECRET'));
```

```python Python theme={null}
import hashlib, hmac, os

user_hash = hmac.new(
    os.environ['OCTASEND_IN_APP_SECRET'].encode(),
    user_id.encode(),
    hashlib.sha256,
).hexdigest()
```

**3. Envie o userHash para o site**

Entregue o valor à página junto com o `userId` do usuário logado e passe no `init`:

```javascript theme={null}
OctaSendInApp.init({ clientId: 'SEU_CLIENT_ID', userId: 'user_42', userHash: 'HASH_CALCULADO_NO_SEU_SERVIDOR' })
```

Com a [tag única](https://docs.octasend.com.br/plataforma/octatag.md), passe no `identify`:

```javascript theme={null}
octasend('identify', { userId: 'user_42', userHash: 'HASH_CALCULADO_NO_SEU_SERVIDOR' })
```

**4. Ligue a exigência**

Quando o seu site já estiver enviando o `userHash` para todos os usuários, ligue **Exigir verificação de identidade** na mesma seção. A chave só fica disponível depois que o segredo é gerado.

### Como a verificação decide

| Situação | Exigência desligada (padrão) | Exigência ligada |
| - | - | - |
| Sem `userHash` | Aceito | Recusado |
| `userHash` certo | Aceito | Aceito |
| `userHash` errado (de outro `userId` ou de um segredo antigo) | Recusado | Recusado |

* Um `userHash` enviado é sempre conferido, mesmo com a exigência desligada. Enviado antes de existir um segredo, ele nunca confere.
* Com `userHash`, o `userId` é usado exatamente como veio. Um `userId` com espaço no início ou no fim é recusado.
* **Gerar novo segredo** invalida na hora todo `userHash` calculado com o anterior. Até o seu servidor usar o segredo novo, o sino fica vazio e o web push não registra aparelhos. O painel pede confirmação antes de trocar.
* Quando a verificação recusa, o sino aparece sem notificações e nenhuma notificação daquele usuário é carregada. No script `in-app-widget`, o console do navegador registra `[OctaSendInApp] connect falhou:` com o código do erro (tabela abaixo).

> [!WARNING]
>
> Ligue **Exigir verificação de identidade** só depois que o seu site enviar o `userHash`. Com a exigência ligada, quem abrir o seu produto sem `userHash` fica com o sino vazio.

> [!TIP]
>
> Use em `userId` um identificador opaco, como um UUID, e não um número sequencial ou o e-mail. E não envie pelo canal In-App informações sigilosas, como senhas, códigos de acesso ou dados de pagamento.

Se você revogar o OAuth Client usado no widget, o sino deixa de carregar. Crie um novo client e troque o `clientId` no seu site.

## Conteúdo da notificação

O conteúdo vem do template In-App da comunicação, no editor do painel. O envio usa sempre a versão **publicada** do template: alterações salvas com **Salvar rascunho** só valem depois de **Publicar**.

| Campo no editor | No widget |
| - | - |
| **Título** | Título da notificação. Se ficar vazio, vale o nome da comunicação |
| **Corpo** | Texto da notificação |
| **URL ao clicar** | Link aberto na mesma aba quando o usuário clica |
| **Categoria** | **Info** (padrão), **Sucesso**, **Aviso** ou **Erro** — define a cor e o ícone |
| **URL do ícone** | Imagem que substitui o ícone da categoria |
| **URL da imagem** | Fica guardada com a notificação, mas não aparece no sino |

As `{{variáveis}}` funcionam no título, no corpo e nas URLs.

## Enviar uma notificação

Use o [`send-message`](https://docs.octasend.com.br/api-reference/enviar-mensagens) com `recipient.user_id`. Para disparar só o In-App de uma comunicação multicanal, passe `channels: ["in_app"]`:

```http theme={null}
POST https://api.octasend.com.br/functions/v1/send-message
Authorization: Bearer at_f4a92b...
Content-Type: application/json

{
  "communication_slug": "nova-feature",
  "recipient": { "user_id": "user_42" },
  "variables": { "feature": "Relatórios avançados" },
  "channels": ["in_app"]
}
```

A resposta traz `"status": "sent"` para o canal `in_app` quando a notificação foi gravada. Se o usuário estiver com o seu produto aberto, ela aparece no widget na hora. Com a chave **Canal habilitado** desligada, o canal volta como `skipped`, com o erro `Canal in-app desabilitado`.

## Endpoint de autenticação do widget

O widget chama sozinho o endpoint [`POST /in-app-auth`](https://docs.octasend.com.br/api-reference/in-app-auth) — você só precisa dele se for construir uma interface própria. Ele troca `client_id` + `user_id` (e o `user_hash`, quando houver) por um token de 15 minutos, que só lê e marca como lidas as notificações daquele usuário.

```http theme={null}
POST https://api.octasend.com.br/functions/v1/in-app-auth
Content-Type: application/json

{
  "client_id": "client_a3f8b2...",
  "user_id": "user_42",
  "user_hash": "4f1d8a..."
}
```

```json theme={null}
{
  "jwt": "eyJhbGciOiJIUzI1...",
  "expires_in": 900,
  "supabase_url": "https://api.octasend.com.br",
  "supabase_anon_key": "eyJhbGciO..."
}
```

| Campo | Obrigatório | Descrição |
| - | - | - |
| `client_id` | Sim | `client_id` público do OAuth Client |
| `user_id` | Sim | Identificador do usuário no seu sistema |
| `user_hash` | Com a verificação exigida | HMAC-SHA256 do `user_id` com o segredo de verificação, em hexadecimal |

| HTTP | `error` | Causa |
| - | - | - |
| 400 | `missing_client_id_or_user_id` | `client_id` ou `user_id` ausente |
| 400 | `user_id_has_surrounding_whitespace` | `user_hash` enviado com um `user_id` que tem espaço no início ou no fim |
| 401 | `invalid_client_id` | `client_id` inexistente ou OAuth Client revogado |
| 401 | `user_hash_required` | A conta exige verificação de identidade e o `user_hash` não veio |
| 401 | `invalid_user_hash` | O `user_hash` não confere: foi calculado para outro `user_id`, com um segredo já trocado, ou a conta ainda não gerou o segredo |
| 403 | `tenant_inactive` | Conta inativa |
| 403 | `in_app_channel_not_enabled` | Canal In-App não liberado para a conta |
| 429 | `rate_limited` | Mais de 60 chamadas por minuto do mesmo IP. O header `Retry-After` indica quantos segundos esperar |

## Com a tag única (OctaTag)

Se o seu site usa a [tag única da OctaSend](https://docs.octasend.com.br/plataforma/octatag.md), o sino de notificações também pode ser entregue por ela, sem o script acima — módulo opcional, fale com a OctaSend para ativar. Nesse caso:

* o usuário vem do `identify`: `octasend('identify', { userId, userHash })`. O `userHash` segue as mesmas regras da seção **Verificação de identidade**;
* as opções ficam em **Configurações → Canais → In-app (dentro da aplicação)**, no card **Configuração da tag**: tema, idioma e **Intervalo de atualização (ms)**, de 10.000 a 120.000 (padrão 20.000);
* a tag busca novidades nesse intervalo, em vez de receber em tempo real;
* se a conta tem domínios autorizados cadastrados na tag, o sino só carrega nos sites da lista;
* enquanto o script antigo (`in-app-widget`) estiver na página, é ele que mostra as notificações e a tag não desenha um segundo sino. Retire o script antigo depois de instalar a tag.

Quando a tag não consegue carregar as notificações, o sino fica vazio e a resposta da tag, visível na aba de rede do navegador, traz `{"ok": false, "error": "..."}` com o motivo:

| `error` | Causa |
| - | - |
| `sem_userHash` | A conta exige verificação de identidade e o `identify` veio sem `userHash` |
| `userHash_invalido` | O `userHash` não confere com o `userId` |
| `userId_com_espacos` | `userHash` enviado com um `userId` que tem espaço no início ou no fim |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.