Guia para desenvolvedores

API de identificação de visitantes

Identifique usuários logados via código para que eles nunca vejam o pedido de e-mail e suas conversas fiquem vinculadas automaticamente a um contato.

Quando usar

Se o seu site tem usuários logados (apps SaaS, painéis, portais de membros), você já sabe quem eles são. Use o comando identify para passar o e-mail deles ao Sonny, assim eles não precisam digitá-lo de novo.

  • Pule totalmente o pedido de e-mail para usuários logados
  • Vincule automaticamente as conversas do chat ao registro do contato
  • Veja o nome e o e-mail do visitante na sua caixa de entrada na hora
  • Continue o histórico em outros navegadores e dispositivos com a identidade verificada

Continuidade entre dispositivos

Identidade verificada do visitante

A identificação básica por e-mail é prática, mas o navegador pode alegar qualquer e-mail. A identidade verificada adiciona um JWT de curta duração assinado pelo seu servidor. Com isso, o Sonny pode usar com segurança o contato do cliente como dono do histórico de chat do site, e as mesmas conversas aparecem em outro navegador ou dispositivo.

Desligado

O padrão. Nada muda para as instalações e os visitantes existentes; o histórico continua vinculado ao ID de visitante do navegador.

Verificação opcional (recomendado)

JWTs válidos ganham histórico entre dispositivos. Visitantes sem JWT mantêm a experiência atual, anônima ou com identificação por e-mail.

Obrigatório para identificação

O chat anônimo continua funcionando, mas as chamadas de identify e de atributos personalizados exigem um JWT assinado válido.

O modo de verificação não torna opcionais os campos pré-chat obrigatórios. Veja como cada modo atende a um e-mail obrigatório em dados pré-chat obrigatórios.

1. Gere um segredo de assinatura

Abra Canais → seu canal → Chat ao vivo, encontre Identidade segura do visitante, escolha um modo e gere um segredo. O texto do segredo aparece uma única vez. Guarde-o no gerenciador de segredos do seu backend como SONNY_IDENTITY_SECRET. Nunca coloque o segredo de assinatura em código do navegador, em um código de instalação, em uma variável de ambiente pública ou no seu repositório de código.

2. Gere um JWT de curta duração no seu backend

Assine com HS256. O Sonny exige user_id, email, iat e exp. O user_id deve ser um ID estável do seu próprio banco de dados, não um endereço de e-mail. Os tokens podem durar no máximo 24 horas; 15 minutos é um bom padrão. Os relógios podem ter uma diferença de até 60 segundos.

Você também pode incluir um objeto traits assinado pelo servidor com contexto de atendimento confiável, como merchantName, subdomain, plan, platform e role. Os valores dos traits devem ser strings, números finitos, booleanos ou null. O Sonny os armazena na identidade do contato verificado, vinculada ao site. Atributos informados pelo navegador continuam não verificados e nunca são promovidos a esses traits assinados.

Node.js
import { SignJWT } from 'jose';

export async function createSonnyIdentityToken(user) {
  const secret = new TextEncoder().encode(process.env.SONNY_IDENTITY_SECRET);

  return new SignJWT({
    user_id: String(user.id),
    email: user.email,
    traits: {
      merchantName: user.merchant.name,
      subdomain: user.merchant.subdomain,
      plan: user.merchant.plan,
      platform: user.merchant.platform,
      role: user.role
    }
  })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime('15m')
    .sign(secret);
}
Ruby
require 'jwt'

def sonny_identity_token(user)
  now = Time.now.to_i
  payload = {
    user_id: user.id.to_s,
    email: user.email,
    traits: {
      merchantName: user.merchant.name,
      subdomain: user.merchant.subdomain,
      plan: user.merchant.plan,
      platform: user.merchant.platform,
      role: user.role
    },
    iat: now,
    exp: now + 15 * 60
  }

  JWT.encode(payload, ENV.fetch('SONNY_IDENTITY_SECRET'), 'HS256')
end
Python
import os
from datetime import datetime, timedelta, timezone
import jwt

def sonny_identity_token(user):
    now = datetime.now(timezone.utc)
    return jwt.encode({
        "user_id": str(user.id),
        "email": user.email,
        "traits": {
            "merchantName": user.merchant.name,
            "subdomain": user.merchant.subdomain,
            "plan": user.merchant.plan,
            "platform": user.merchant.platform,
            "role": user.role,
        },
        "iat": now,
        "exp": now + timedelta(minutes=15),
    }, os.environ["SONNY_IDENTITY_SECRET"], algorithm="HS256")
PHP
use Firebase\JWT\JWT;

function sonnyIdentityToken($user): string {
    $now = time();
    return JWT::encode([
        'user_id' => (string) $user->id,
        'email' => $user->email,
        'traits' => [
            'merchantName' => $user->merchant->name,
            'subdomain' => $user->merchant->subdomain,
            'plan' => $user->merchant->plan,
            'platform' => $user->merchant->platform,
            'role' => $user->role,
        ],
        'iat' => $now,
        'exp' => $now + (15 * 60),
    ], $_ENV['SONNY_IDENTITY_SECRET'], 'HS256');
}

3. Passe apenas o JWT para o widget

Retorne o JWT a partir de um endpoint protegido pela sessão normal da sua aplicação. Chame identify antes ou depois de init. O widget mantém o JWT apenas na memória: ele nunca é salvo em localStorage, cookies ou em uma URL.

JavaScript
// Fetch a short-lived JWT from your authenticated backend.
// Your signing secret never reaches this code.
const { userJwt } = await fetch('/api/sonny-identity').then((res) => res.json());

sonny('identify', { userJwt });
sonny('init', { siteId: 'YOUR_SITE_ID' });

// Sonny asks for a fresh token after expiry or an emergency key change.
window.addEventListener('sonny:identity-required', async () => {
  const { userJwt } = await fetch('/api/sonny-identity').then((res) => res.json());
  sonny('identify', { userJwt });
});

Renove, faça logout e rotacione com segurança

  • Renovação: renove o JWT antes que ele expire — cerca de 10 minutos para um token de 15 minutos — e chame sonny('identify', { userJwt }). Escute também sonny:identity-required como alternativa para expiração ou troca da chave de assinatura.
  • Logout: chame sonny('reset'). Isso descarta o JWT da memória, limpa a sessão do widget no navegador e inicia um novo visitante anônimo.
  • Troca de conta: chame sonny('reset') para o usuário A antes de buscar o JWT do usuário B e chamar sonny('identify', { userJwt }). Nunca leve o token de um usuário para outra sessão logada.
  • Rotação de rotina: o Sonny aceita o segredo anterior por 24 horas, dando tempo para você atualizar todas as instâncias do backend. Uma segunda rotação de rotina fica bloqueada até esse período de sobreposição terminar.
  • Suspeita de vazamento do segredo: escolha Substituir imediatamente. As duas chaves de assinatura antigas deixam de autenticar novas requisições na hora; os widgets conectados pedem um JWT novo à página.
  • Desligar a verificação: o Sonny descarta os segredos de assinatura atual e anterior e desconecta os widgets verificados. Gere um novo segredo antes de ativar a verificação de novo.

Conflitos de identidade não são mesclados automaticamente

Se um ID de usuário estável e um e-mail apontarem para contatos diferentes no Sonny, a verificação retorna um conflito em vez de combinar os registros dos clientes silenciosamente. Corrija o token ou mescle os contatos no Sonny e tente de novo.

Um chat vinculado apenas por um e-mail não assinado anterior não é tratado automaticamente como histórico verificado. Isso impede que um e-mail informado pelo navegador libere as conversas de outro cliente.

Login na central de ajuda

Conecte o login de clientes que você já tem às páginas de ajuda hospedadas ou em domínio próprio. Use o mesmo segredo de assinatura do site e o mesmo JWT de curta duração do widget. A identificação básica por e-mail e o login da equipe do Sonny não dão acesso de leitor verificado.

  1. Conclua a configuração da identidade verificada do visitante acima para este canal. Ative Verificação opcional ou Exigir comprovação para identificar em Canais → seu canal → Chat ao vivo.
  2. Implemente no seu app um endpoint autenticado que retorne { "userJwt": "SIGNED_TOKEN" }. Gere o token no seu servidor a partir do cliente logado e de traits confiáveis; nunca aceite um plano ou uma função solicitados pelo navegador.
  3. Em Canais → seu canal → Central de ajuda → Acesso à central de ajuda, escolha Clientes verificados e, se quiser, um Público de clientes. Informe a página de login do seu app em URL de login dos clientes e selecione Salvar acesso.
  4. Depois que o cliente fizer login, execute a troca abaixo, substituindo YOUR_SLUG pelo Endereço da central de ajuda. A URL de login sozinha não basta: o seu app precisa concluir essa troca.
JavaScript
// Run after your application authenticates the customer.
// This endpoint must use the server's session and trusted customer traits.
const response = await fetch('/api/sonny-identity');
if (!response.ok) throw new Error('Unable to sign in to the help center');
const { userJwt } = await response.json();
const signIn = new URL('https://www.usesonny.com/help/YOUR_SLUG/auth');
signIn.searchParams.set('token', userJwt);
signIn.searchParams.set('return', '/help/YOUR_SLUG');
window.location.assign(signIn);

Para um domínio próprio, defina signIn como https://help.example.com/auth e retorne para / ou /articles/getting-started. Os caminhos de retorno hospedados começam com /help/YOUR_SLUG. Os destinos de retorno precisam ficar no mesmo host.

A troca define um cookie seguro e HTTP-only e redireciona para remover o token da URL. A sessão de leitor expira junto com o JWT, em até 24 horas. Use tokens de curta duração e evite registrar a URL da troca em logs. O JWT pertence apenas a essa troca de login, não a links de artigos compartilhados.

Teste o login e o acesso

  1. Visite uma central privada sem estar logado. Siga o botão de login, entre no seu app e confirme que você volta para a central de ajuda no mesmo domínio hospedado ou próprio.
  2. Confira um artigo permitido e um negado na navegação, na busca e pela URL direta. Um cliente fora do público da central vê uma mensagem de acesso; um artigo negado retorna "não encontrado".
  3. Use Sair na central de ajuda e verifique se o conteúdo privado desaparece. Repita com um cliente que tenha traits diferentes.

Renove e saia

O widget envia o JWT atual automaticamente. Renove-o quando o Sonny pedir e chame sonny('reset') quando o cliente fizer logout. A identidade do widget e o cookie da central de ajuda são separados: redefinir o widget não encerra uma sessão na central de ajuda hospedada.

Para sair das páginas hospedadas, acesse /help/YOUR_SLUG/auth/logout; no seu domínio próprio, use /auth/logout. Ligue essa ação ao fluxo de logout do seu app se precisar encerrar as duas sessões. Credenciais inválidas ou expiradas têm acesso de visitante. Com a verificação de identidade desligada, todo leitor tem acesso de visitante.

Quando os traits do cliente mudarem, gere um JWT novo e identifique-o de novo no widget; repita a troca de login para as páginas hospedadas. Os cookies de leitor existentes mantêm os traits assinados anteriores até expirarem. Mudanças nas regras de público valem a partir da próxima requisição.

Crie públicos, visualize o acesso e resolva problemas de restrição

Identificação por e-mail não é autenticação

O e-mail informado pelo navegador melhora o contexto do atendimento, mas não autentica o visitante. Um visitante pode inspecionar e executar JavaScript na própria página, então e-mail, nome e atributos personalizados nunca liberam o histórico do Sonny de outro cliente. A identidade verificada exige o JWT assinado pelo servidor descrito acima. Mantenha a autorização das ações do seu produto dentro da sua aplicação logada.

Quando a verificação de visitantes está ativada, um selo Verificado na caixa de entrada significa que a conversa foi vinculada com um JWT assinado pelo servidor. Um selo Não verificado significa que nenhuma identidade assinada pelo servidor autenticou a conversa. Qualquer nome ou e-mail exibido é contexto de atendimento informado pelo visitante, não prova de identidade.

Exemplos de código

A chamada mais simples — basta passar o e-mail do usuário:

JavaScript
// Identify a logged-in user
sonny('identify', {
  email: 'jane@example.com'
});

Passe também o nome do usuário, para que os agentes o vejam na caixa de entrada:

JavaScript
// Identify with full name
sonny('identify', {
  email: 'jane@example.com',
  name: 'Jane Smith'
});

Adicione contexto útil para o atendimento na identificação, atualize-o depois ou acompanhe um valor que muda enquanto a página está aberta:

JavaScript
// Identify with support context
sonny('identify', {
  email: 'jane@example.com',
  name: 'Jane Smith',
  attributes: {
    plan: 'starter',
    seats: 5,
    trial: true
  }
});

// Update only the values that changed
sonny('setAttributes', {
  plan: 'growth',
  seats: 8,
  trial: null // Clears this property
});

// Keep a changing value in sync (checks every 10 seconds)
sonny('watchAttributes', () => ({
  monthly_usage: window.currentUsage
}), { interval: 10000 });

Exemplo completo com o código assíncrono. Observe que identify pode ser chamado antes de init — a identidade fica na fila e é enviada assim que o widget se conecta:

HTML
<!-- Sonny widget snippet -->
<script>
  (function(w,d,s,o,f,js,fjs){
    w['Sonny']=o;w[o]=w[o]||function(){
    (w[o].q=w[o].q||[]).push(arguments)};
    js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
    js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
  })(window,document,'script','sonny','https://www.usesonny.com/widget.js');

  // Identify before init — identity is queued and sent on connect
  sonny('identify', {
    email: 'jane@example.com',
    name: 'Jane Smith'
  });

  sonny('init', { siteId: 'YOUR_SITE_ID' });
</script>

Chame reset quando o usuário sair para limpar a identidade e iniciar uma nova sessão:

JavaScript
// Reset on logout — clears identity and starts a fresh session
sonny('reset');

Como funciona

  1. 01

    O widget carrega e se conecta

    O widget se conecta ao Sonny e envia junto qualquer identidade armazenada ao entrar.

  2. 02

    A identidade é enviada ao servidor

    O Sonny procura um contato com esse e-mail no seu espaço de trabalho e cria um, se ainda não existir.

  3. 03

    As conversas são vinculadas

    Todas as conversas não vinculadas da sessão de visitante do navegador atual são vinculadas ao contato identificado. A identificação não mescla o histórico entre navegadores ou dispositivos.

  4. 04

    O pedido de e-mail é pulado

    Como o visitante já está identificado, o pedido de e-mail dentro do widget não aparece — sem interrupções para o usuário.

Regras dos atributos personalizados

  • Envie no máximo 50 atributos por chamada.
  • Um valor pode ser string, número, booleano ou null. Strings são limitadas a 1000 caracteres. Passe null ou uma string vazia para limpar um valor salvo.
  • As chaves devem começar com uma letra e conter apenas letras, números e sublinhados, com no máximo 64 caracteres.
  • Estas chaves são reservadas e ignoradas: email, name, id, phone, createdAt, updatedAt.

Referência da API

sonny('identify', { email, name?, attributes? })
Identifica o visitante atual. Define o e-mail e, opcionalmente, o nome, pula o pedido de e-mail e envia a identidade ao servidor. Pode ser chamado antes ou depois de init.
  • emailstringEndereço de e-mail do visitante
  • namestring?Nome de exibição do visitante
  • attributesobject?Atributos personalizados a anexar ao contato (veja o guia de instalação do widget para as regras de chaves e valores)
sonny('identify', { userJwt, name?, attributes? })
Identifica com segurança o cliente logado atual a partir de um JWT gerado pelo servidor. O token fica na memória e só é enviado em requisições autenticadas ou payloads de socket.
  • userJwtstringUm JWT HS256 novo, gerado pelo seu backend autenticado
  • namestring?Nome de exibição do visitante
  • attributesobject?Atributos personalizados a anexar ao contato verificado
sonny('setAttributes', { ... })
Atualiza os atributos personalizados do visitante identificado. Só os valores alterados são enviados. Se o visitante ainda não estiver identificado, as atualizações aguardam e são enviadas depois que identify for executado.
  • attributesobjectPares de chave e valor a definir. Passe null como valor para limpar um atributo.
sonny('watchAttributes', getter, { interval? })
Chama sua função getter periodicamente e sincroniza automaticamente os atributos alterados. Útil quando valores como plano ou uso mudam enquanto a página está aberta.
  • getterfunctionUma função que retorna o objeto de atributos atual
  • intervalnumber?Com que frequência verificar, em milissegundos. Padrão 10000, mínimo 2000.
sonny('reset')
Limpa o ID do widget, o e-mail, o nome e o histórico local de conversas do navegador atual e inicia uma nova sessão de visitante. Não exclui o contato nem as propriedades salvas dele no Sonny. Use no logout.

Precisa de ajuda?

Consulte o guia de instalação do widget ou fale com a nossa equipe.

Documentos relacionados