Notificações
Quando alguém responde um comentário, o autor recebe uma notificação. O Qorum guarda essas notificações, e o seu site pode mostrá-las no widget, no próprio sistema de notificação dele, ou nos dois.
Quando uma notificação nasce
- Quando uma resposta aparece: na hora em que é escrita ou, se foi para a fila, quando é aprovada.
- Quem recebe é a pessoa respondida. Numa resposta a outra resposta, recebe o autor da resposta, não o do comentário principal.
- Ninguém é notificado de uma resposta a si mesmo.
- Se a resposta for apagada, a notificação some das listas e da contagem.
Tipos
type | Quando |
|---|---|
reply | Responderam um comentário da pessoa. |
mention | Marcaram a pessoa com @usuario. Quem já foi avisado pela resposta não recebe duas. |
follow | Comentário novo numa conversa que a pessoa segue. Enquanto ela não lê, os avisos da mesma conversa viram um só, com count somando ("3 comentários novos"). O webhook sai só no primeiro de cada leva. |
Ninguém recebe aviso de quem bloqueou.
Como ela chega
O mesmo formato em todos os lugares: na API, na gerência, no feed e no webhook.
{
"id": 318,
"type": "reply",
"userId": 77, // quem recebe (id no seu site, ou "q:N" para conta Qorum)
"read": false,
"createdAt": "2026-11-08T21:00:00Z",
"actorId": 123, // quem respondeu
"actor": { "id": 123, "username": "ana_lima", "displayName": "Ana Lima", "avatar": "https://..." },
"comment": { "id": 82, "html": "Concordo, e...", "preview": "Concordo, e..." }, // a resposta
"inReplyTo": { "id": 81, "preview": "Acho que o melhor caminho é..." }, // o comentário de quem recebe
"thread": { "ref": "post-42", "title": "Meu post", "url": "https://meusite.com/post-42" },
"link": "https://meusite.com/post-42#qorum-82", // abre a página já na resposta
"commentId": 82, "preview": "Concordo, e..." // atalhos (iguais a comment.id e comment.preview)
}
Com isso dá para montar a frase completa: "Ana Lima respondeu ao seu comentário 'Acho que o melhor caminho é...' em Meu post", com link direto para a resposta. O link só existe se a conversa tem url (o widget manda sozinho).
Quatro jeitos de usar
| Jeito | Para quê | Você faz |
|---|---|---|
| Sino do widget | Mostrar dentro da área de comentários | Nada. Já vem pronto. |
| API com o login da pessoa | Um sino no topo do seu site ou no seu aplicativo | Chamar do navegador com o mesmo token do widget |
| API do seu servidor | Mostrar ou marcar como lida do seu backend | Chamar com a chave secreta, informando o usuário |
| Webhook e feed | Copiar para o sistema de notificação que o seu site já tem | Receber o aviso na hora e, por garantia, puxar o feed |
Com o login da pessoa
As mesmas rotas que o sino do widget usa. Mande a chave pública e o token da pessoa, igual à API pública.
/v1/notifications| Parâmetro | |
|---|---|
unread=1 | Só as não lidas. |
limit | Quantas, de 1 a 100. Padrão: 30. |
before | Para paginar: o valor de next da resposta anterior. |
{ "notifications": [ ... ], "unread": 3, "next": 290 } // next: null quando acabou
/v1/notifications/countSó o número de não lidas, leve para checar de tempos em tempos: {"unread": 3}.
/v1/notifications/read{"ids": [318, 317]} marca essas; corpo vazio marca todas. Devolve quantas foram marcadas em marked.
Exemplo: um sino no topo do seu site
// usa o mesmo token do login do seu site que você passa ao widget
async function qorum(caminho, opcoes = {}) {
const r = await fetch('https://useqorum.com' + caminho, {
...opcoes,
headers: { 'X-Qorum-Key': 'pk_SUA_CHAVE_PUBLICA', Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' },
});
return r.json();
}
async function atualizarSino() {
const { unread } = await qorum('/v1/notifications/count');
document.querySelector('#sino .numero').textContent = unread || '';
}
async function abrirSino() {
const { notifications } = await qorum('/v1/notifications?limit=20');
lista.innerHTML = '';
for (const n of notifications) {
const item = document.createElement('a');
item.href = n.link || '#';
item.className = n.read ? '' : 'nova';
item.textContent = `${n.actor.displayName || n.actor.username} respondeu: "${n.preview}"`;
lista.appendChild(item);
}
await qorum('/v1/notifications/read', { method: 'POST', body: '{}' });
atualizarSino();
}
setInterval(atualizarSino, 60000); // a cada minuto
atualizarSino();
Este pedido vem do navegador, então a página precisa estar num domínio liberado do site, como o widget.
Pelo seu servidor
Com a chave pública e a secreta, como na API de gerência. Você informa de quem são as notificações no endereço.
/v1/site/users/:userId/notificationsOs mesmos parâmetros e a mesma resposta de /v1/notifications.
/v1/site/users/:userId/notifications/count/v1/site/users/:userId/notifications/read// a pessoa leu as notificações no seu site: marque no Qorum também, para o sino do widget concordar
await gerencia('POST', '/users/77/notifications/read', { ids: [318, 317] });
:userId é o id da pessoa no seu site, o mesmo sub do token. Para contas Qorum, use q:N.
Copiar para o sistema de notificação do seu site
Se o seu site já tem notificações (um sino, e-mail, push no celular), copie as do Qorum para lá. O jeito mais seguro usa as duas coisas juntas:
- Webhook para chegar na hora
O evento
notification.createdchega no instante em que a resposta aparece, com a notificação inteira emnotification. - Feed para não perder nenhuma
De tempos em tempos (ou ao ligar o servidor), peça ao feed tudo o que veio depois da última notificação que você guardou. Ele cobre o que se perdeu se o seu servidor ficou fora do ar.
- Guarde pelo id
O
idé único: se a mesma notificação chegar pelo webhook e pelo feed, grave uma vez só.
/v1/site/notifications?since=290&limit=100Todas as notificações do site depois do id since, da mais antiga para a mais nova (até 500 por pedido). userId diz quem recebe cada uma.
{ "notifications": [ { "id": 291, "userId": 77, ... }, { "id": 292, "userId": 15, ... } ],
"last": 292, // guarde e mande em since no próximo pedido
"more": false } // true: ainda tem mais, peça de novo
Exemplo completo (Node)
// 1) na hora, pelo webhook (veja a conferência da assinatura em /docs/webhooks)
async function processar(aviso) {
if (aviso.event === 'notification.created') await guardar(aviso.notification);
}
// 2) por garantia, a cada 5 minutos, pelo feed
async function sincronizar() {
let desde = await ultimoIdGuardado(); // 0 na primeira vez
for (;;) {
const r = await gerencia('GET', `/notifications?since=${desde}&limit=500`);
for (const n of r.notifications) await guardar(n);
desde = r.last;
if (!r.more) break;
}
}
setInterval(sincronizar, 5 * 60 * 1000);
// grava uma vez só por id e entrega no sistema do seu site
async function guardar(n) {
const novo = await db.query(
'INSERT INTO notificacoes_qorum (id, usuario, dados) VALUES ($1, $2, $3) ON CONFLICT (id) DO NOTHING RETURNING id',
[n.id, String(n.userId), n]);
if (!novo.rowCount || String(n.userId).startsWith('q:')) return; // repetida, ou conta Qorum (não existe no seu banco)
await notificarUsuario(n.userId, {
titulo: `${n.actor.displayName || n.actor.username} respondeu você`,
texto: n.preview,
link: n.link,
});
}
O comentário respondido
Em inReplyTo vem o comentário da pessoa que recebeu a resposta, com um trecho do texto. Se esse comentário foi apagado, inReplyTo vem null, mas a notificação continua valendo enquanto a resposta existir.
O webhook comment.created também traz replyToCommentId e notifyUserId. Se você só quer saber quem avisar, ele basta. O notification.created é mais prático porque já vem pronto para mostrar.