> ## 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.

# Lista de espera

> Colete cadastros antes do produto abrir, e libere quando abrir — sem uma planilha no meio.

O modo lista de espera segura a porta: as pessoas se cadastram, nada emite
sessão, e você libera quando quiser.

O motivo de isso morar aqui e não num criador de formulários é que a lista é
feita de **contatos** desde o primeiro minuto. Atribuição de primeiro toque, o
merge de anônimo para identificado, a trilha de auditoria e o medidor de
contatos ativos já funcionam sobre contatos — então "converter a lista em contas
de verdade" é um timestamp, não uma migração. Ninguém nunca migra uma planilha
para dentro da própria autenticação.

<Note>
  É um interruptor por ambiente, então dá para ensaiar o lançamento em teste
  enquanto produção continua segurando a fila. Só existe em modo **hosted**: no
  federado quem decide quem existe é o seu backend, e uma trava nossa lá seria uma
  trava numa porta que não é nossa.
</Note>

## Ligando

No painel, em **API keys → Identidade dos contatos**, ou pela API:

```bash theme={null}
curl -s -X PATCH $API/v1/organization/environments/{id} \
  -H "Authorization: Bearer uk_st_…" \
  -H 'Content-Type: application/json' \
  -d '{ "waitlist_mode": true }'
```

O `GET /v1/config/{publishable_key}` passa a responder `"waitlist": true`, que é
o que permite uma tela de cadastro mudar de forma sem deploy.

## Coletando

```bash theme={null}
curl -s $API/v1/waitlist \
  -H 'Origin: https://example.com' \
  -H 'Content-Type: application/json' \
  -d '{
    "publishable_key": "uk_pk_live_…",
    "email": "grace@example.com",
    "name": "Grace Hopper",
    "anonymous_id": "anon_2f9c1b",
    "metadata": { "building": "uma API para compiladores" }
  }'
```

`202`, sempre — a mesma postura do resto da superfície hosted: este endpoint
nunca vira o lugar onde alguém descobre quais endereços estão na lista. Uma
confirmação vai para a caixa de entrada, sem link e sem credencial, porque ainda
não há nada para clicar.

Cadastrar duas vezes mantém o primeiro lugar na fila e as primeiras respostas.
As pessoas esquecem que já se cadastraram, e isso não pode custar a posição
delas.

<Note>
  Mande o `anonymous_id` que a sua página já tem (o `@userkit/js` faz isso
  sozinho) e a entrada herda a visita: os UTMs e o referrer que trouxeram essa
  pessoa até a landing page ficam no contato, e continuam lá no dia em que ela
  virar cliente pagante.
</Note>

### Com o SDK

O `<SignUp />` renderiza o formulário de lista sozinho enquanto o modo está
ligado — endereço, sem senha, sem botões sociais — porque ele lê a configuração
em vez de receber isso como prop. **Um formulário de cadastro publicado meses
antes do lançamento continua funcionando no dia do lançamento.**

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

// Lista hoje, cadastro normal no instante em que você virar a chave.
<SignUp />
```

Direto, quando o formulário é seu:

```ts theme={null}
await userkit.joinWaitlist({
  email: "grace@example.com",
  metadata: { building: "uma API para compiladores" },
});
```

### Ou pelo cadastro normal

O `POST /v1/contact-auth/signup` também entra na fila enquanto o modo está
ligado, e responde `{ "waitlist": true }` para um formulário próprio poder
dizer isso. A senha que a pessoa escolheu **fica guardada na entrada** e só é
anexada quando ela é liberada e o endereço é comprovado — nunca antes. Uma senha
num endereço que ninguém confirmou não comprova nada, que é o mesmo raciocínio
que fecha o roubo de conta pré-criada.

## A fila segura de verdade

Enquanto alguém está esperando, **nenhuma rota emite sessão para essa pessoa**:
nem a senha, nem magic link, nem código por e-mail, nem "continuar com o
Google". Todos respondem `403 waitlisted`.

Isso é exigido onde a sessão é emitida, e não em cada porta, pelo mesmo motivo
dos dois fatores. Uma fila que se contorna escolhendo outro botão é uma lista de
e-mails com passos a mais.

<Note>
  Visitantes anônimos nunca são segurados. O widget emite sessão de visitante a
  cada carregamento da landing page, e uma lista de espera cuja landing page não
  pode rodar o widget é uma lista sem atribuição — que é a maior parte do motivo
  de manter a lista aqui.
</Note>

## Liberando

```bash theme={null}
# alguns escolhidos
curl -s $API/v1/organization/waitlist/admit?environment=live \
  -H "Authorization: Bearer uk_st_…" \
  -H 'Content-Type: application/json' \
  -d '{ "entry_ids": ["3f9a…"] }'

# ou todo mundo, no dia em que você abre
curl -s $API/v1/organization/waitlist/admit?environment=live \
  -H "Authorization: Bearer uk_st_…" \
  -H 'Content-Type: application/json' \
  -d '{ "all": true }'
```

Liberar é o ato que transforma uma entrada numa conta utilizável:

<Steps>
  <Step title="A fila para de segurar">
    Todas as rotas de login passam a funcionar para aquela pessoa a partir daí.
  </Step>

  <Step title="O acesso vai por e-mail">
    Um único link que comprova o endereço, anexa a senha guardada e já entra com
    a pessoa. Um clique entre "chegou a sua vez" e estar dentro — qualquer coisa
    mais longa perde quem esperou.
  </Step>
</Steps>

É idempotente: uma entrada já liberada é ignorada, então uma segunda chamada não
manda nada.

<Warning>
  **Desligar o modo lista de espera não libera ninguém.** Os dois são separados de
  propósito: quem já está na fila tem uma senha guardada na entrada que nunca foi
  anexada, e virar só a chave deixaria essas pessoas descobrindo isso na tela de
  login. Libere todo mundo primeiro e depois desligue o modo — ou deixe ligado e
  vá liberando aos poucos.
</Warning>

## Lendo a fila

```bash theme={null}
curl -s "$API/v1/organization/waitlist?environment=live" \
  -H "Authorization: Bearer uk_st_…"
```

As entradas voltam na ordem em que chegaram, e essa ordem **é** a posição. Ler
exige `customers:read`; liberar exige `customers:write`, porque é o que cria a
conta.
