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

# NPS e pesquisas

> Uma pergunta, feita num momento que você escolheu, a uma audiência que você já definiu — com um intervalo que pertence à pessoa e não à pesquisa.

Uma pesquisa é uma pergunta. O que a torna usável num produto em vez de irritante
está inteiro na aritmética em volta: quem é perguntado, quando, com que frequência
alguém pode ser perguntado, e o que uma resposta repetida faz.

## Dois tipos

`nps` é a pergunta com escala fixa de 0 a 10 e aritmética fixa — é o tipo do qual a
tela de resultados consegue calcular um número. `custom` é qualquer outra coisa, onde
o verbatim costuma ser a resposta.

O tipo **não pode mudar depois de criado**: as notas já dadas foram dadas na escala
antiga.

## Quando ela dispara

Uma pesquisa carrega até cinco gatilhos.

| Tipo                 | Dispara                                                                                                   |
| -------------------- | --------------------------------------------------------------------------------------------------------- |
| `event`              | Na primeira vez que um dos fatos que este sistema publica sobre um contato é verdade sobre ele            |
| `segment_entered`    | Quando alguém entra na **audiência da própria pesquisa** — então exige `segment_id`, e é recusado sem ele |
| `days_after_created` | N dias depois de o contato existir                                                                        |

Os dois primeiros são casados por um consumidor conforme os fatos chegam, em
segundos. O terceiro é **varrido**, porque nada é publicado quando o trigésimo dia de
alguém chega — não há fato, já que nada aconteceu.

`days_after_created` só conta aniversários que caem **depois de a pesquisa ter sido
escrita**. Ligar "pergunte no dia 30" não questiona sua base inteira de uma vez, que é
o que uma implementação ingênua faz na primeira execução e não dá para desfazer.

**Uma pessoa é perguntada uma vez por ocasião, para sempre**: a primeira vez que
aquele fato é verdade sobre ela, a primeira vez que ela entra naquela audiência, o
seu N-ésimo dia. Reentrar num segmento não é nova ocasião — sair e voltar é ruído de
churn e não um momento.

Uma pesquisa **recorrente** ("pergunte o NPS de todo mundo a cada trimestre") ainda
não existe, e um intervalo longo deliberadamente não a imita. Ela é um quarto tipo de
gatilho com um período na identidade, e até existir a resposta honesta é que isso não
pode ser perguntado.

## O intervalo pertence à pessoa

`cooldown_days` (padrão 7) é por quanto tempo uma entrega fecha a porta para
**qualquer** pesquisa daquele contato — não para aquela pesquisa.

É essa a promessa inteira. "Ninguém é pesquisado duas vezes por semana" não é verdade
numa janela por pesquisa: três pesquisas perguntando no máximo uma vez por dia cada
estariam todas dentro do próprio limite e a pessoa seria perguntada três vezes. `0` é
permitido e significa sem intervalo, o que é uma resposta real para um ambiente
rodando exatamente uma pesquisa.

Reivindicar uma entrega é **uma só instrução**, e a contagem de linhas é a decisão. A
audiência, a identificação, a atividade e a janela são todas checadas dentro de um
INSERT, porque um SELECT seguido de INSERT são duas fotografias de uma tabela — e duas
entregas correndo sobre o mesmo contato encontrariam as duas nada pendente e as duas
escreveriam.

## A leitura do widget

```ts theme={null}
const surveys = await userkit.listPendingSurveys();
await userkit.answerSurvey(id, { score: 9, comment: "…" });
```

```tsx theme={null}
import { Survey } from "@userkit/react";

<Survey />;                          {/* modal central — o padrão */}
<Survey position="bottom-right" />;  {/* cartão no canto, sem backdrop */}
<Survey position="inline" />;        {/* no fluxo da página */}
```

### Onde o cartão é desenhado

`position` aceita `center` (padrão), `bottom-right`, `bottom-center`, `bottom-left`
ou `inline`.

**Só o `center` toma a página.** Ele escurece o fundo, leva o foco para dentro do
cartão e fecha no Esc ou num clique fora. Os dois cantos não escurecem nada, de
propósito: uma pergunta no canto é uma que a pessoa pode terminar a frase antes de
responder, e escurecer o produto para perguntar sobre o produto é o que faz um NPS de
cinco segundos parecer um pedágio. Eles são anunciados a um leitor de tela como
diálogo, mas não como modal — porque o resto do seu produto continua mesmo
disponível.

`inline` é o componente no seu próprio layout, que é o que ele fazia antes de haver
escolha — certo para uma tela de configurações ou qualquer lugar onde você já abriu
espaço para ele.

As três posições flutuantes saem da árvore do componente por portal, então nenhum
`overflow` ou contexto de empilhamento de um ancestral consegue recortá-las. Os
cantos ficam onde um launcher de suporte costuma ficar, então se você também desenha
o `<HelpWidget />` à direita, ponha a pesquisa à esquerda.

**Um diálogo no centro por vez, e há uma ordem.** Três coisas se abrem sozinhas no
meio da página — um termo pendente, uma novidade não lida e esta pesquisa — e cada
uma descobre que quer a tela quando a resposta dela volta. Sem regra, as três
aparecem juntas, cada uma dizendo `aria-modal="true"` a um leitor de tela. O centro
passa a caber uma: a ordem padrão é `agreements` → `whatsNew` → `survey` (a condição
que o tenant declarou, depois o que ele publicou, depois o favor que ele está
pedindo), e **quem espera não perde nada** — a pesquisa só é gasta quando é
respondida, então ela aparece assim que a de cima sair, ou na próxima visita.

Quem já está na tela não é trocado por alguém de ordem maior que chegou depois:
puxar o cartão da mão de quem está lendo é pior do que a ordem sair imperfeita numa
visita. E um diálogo que a pessoa PEDIU — o seletor de metas do guia, por exemplo —
fura a fila, porque ele responde a um clique.

A fila é só do `center`. Um cartão no canto ou no fluxo não disputa palco com
ninguém. Para trocar a ordem:

```ts theme={null}
import { setCenterPriority } from "@userkit/react";

setCenterPriority(["survey", "agreements", "whatsNew"]);
```

**Esc e o clique fora são "agora não", nunca uma resposta.** Eles escondem o cartão
nesta página e não gastam nada, então a pessoa é perguntada de novo na próxima visita
— um reflexo não é uma recusa.

Num app Next, importe do `@userkit/nextjs` — é o mesmo componente, e em
[modo proxy](/pt-br/customer-auth/session-tokens) a leitura e a resposta são
encaminhadas pelos handlers como o resto da superfície do contato, com a sessão num
cookie httpOnly da sua própria origem.

```json theme={null}
{
  "surveys": [
    { "id": "…", "kind": "nps", "question": "Quanto você nos recomendaria?", "delivered_at": "2026-08-01T08:00:00Z" }
  ]
}
```

`pending` nomeia uma **ocasião**, não uma pesquisa. Nada aqui pede uma — a API decide
quem é perguntado, a partir de um gatilho, uma audiência e o intervalo — que é por que
não existe `requestSurvey` e por que uma lista vazia é a resposta comum.

Uma entrega não respondida fica pendente por **14 dias**. Uma pergunta que ninguém
respondeu em duas semanas não é uma pergunta que a pessoa ainda deve, e um widget que
abre uma pesquisa de março é um widget que as pessoas aprendem a dispensar sem ler. No
máximo cinco são devolvidas.

Responder **gasta a entrega**: uma segunda chamada responde `not_found`, que é
literalmente "você não foi perguntado isso" — a mesma coisa que uma ocasião retirada ou
vencida diz. Um widget clicado duas vezes escreve uma resposta, porque a escrita só
acontece se a entrega puder ser marcada como respondida por um UPDATE levando
`responded_at IS NULL`.

Uma pesquisa `nps` **exige a nota**. Uma escala respondida só com uma frase é um
verbatim sem nada para dividir, e deixar passar colocaria uma resposta no numerador de
uma taxa da qual ela não faz parte. Qualquer outro tipo aceita nota, comentário, ou os
dois.

Desligar uma pesquisa também faz suas entregas pendentes deixarem de ser pendentes.

## Resultados

```http theme={null}
GET /v1/organization/surveys/{id}/results
```

```json theme={null}
{
  "delivered": 412,
  "responses": 96,
  "scored": 94,
  "promoters": 51,
  "passives": 28,
  "detractors": 15,
  "score": 38,
  "trend": [{ "week": "2026-07-27", "scored": 12, "promoters": 7, "detractors": 2, "score": 41 }],
  "verbatims": [{ "score": 9, "comment": "…" }]
}
```

`score` é promotores menos detratores sobre `scored`, em porcentagem, e é **`null`
quando ninguém pontuou — nunca `0`**. Zero é um NPS real e um número sobre o qual
alguém agiria; "ainda sem respostas" e "um resultado genuinamente neutro" não podem
ser desenhados iguais.

`delivered` é o denominador da sua *taxa* de resposta; `scored` é o denominador da
nota. Eles diferem sempre que alguém responde uma pesquisa `custom` só com palavras. A
tendência é semanal sobre os últimos 180 dias, em semanas UTC — a mesma meia-noite que
[todo o resto](/pt-br/guides/analytics) usa.

## A nota alcança o resto do produto por JOIN

A leitura de um contato leva `latest_survey_score`, e isso é um **JOIN** e não uma
coluna no contato. Deliberadamente: uma cópia é um segundo lugar que pode discordar,
alargaria a tabela mais quente do plano de clientes, e guardaria exatamente a única
resposta que o JOIN sempre consegue produzir. É `null` quando ninguém respondeu.

É isso que permite uma nota baixa ficar ao lado de sinais de churn na mesma tela sem
ninguém construir um pipeline.

`survey.response_recorded` é entregável como [webhook](/pt-br/guides/webhooks), que é o
gancho para "encaminhe um detrator a um humano hoje".

## O que ainda não está aqui

**Entrega por e-mail.** Tudo acima é in-app: uma pesquisa é entregue a um widget que a
pessoa abriu, e não existe versão dela por e-mail.

O que existe ao lado são as [campanhas](/pt-br/guides/campaigns), que podem enviar um
template com a categoria `surveys` — um público, uma sequência, um limite e um link de
descadastro. Isso é uma mensagem *sobre* perguntar; não cria uma entrega de pesquisa, e
uma resposta a ela não é uma resposta nos resultados acima.

## Permissões

`engagement:manage` — owner e admin por padrão, compartilhada com checklists e o
changelog.
