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

# Credenciais

> Quatro famílias de bearer, quatro superfícies, e um prefixo que as distingue.

Toda credencial que a API emite segue um formato só: bytes aleatórios de alta
entropia atrás de um prefixo que diz o que o token é, armazenada como hash SHA-256,
mostrada em texto puro exatamente uma vez.

O prefixo faz dois trabalhos. Ele permite reconhecer um token vazado — num log, num
git push, numa varredura de repositório — e impede que uma credencial de uma
superfície abra as rotas de outra.

## As quatro famílias

<CardGroup cols={2}>
  <Card title="uk_st_… — sessão de staff" icon="user">
    Uma pessoa logada no painel. Administra a organização. Vive 30 dias, revogável.
  </Card>

  <Card title="uk_sk_… — chave de API da organização" icon="server">
    Seu backend. Lê e escreve no plano de clientes, delimitado ao ambiente da
    chave.
  </Card>

  <Card title="uk_pk_… — chave publicável" icon="globe">
    Suas páginas. Pública por definição; a lista de origens é a fronteira dela.
  </Card>

  <Card title="uk_ct_… — sessão de contato" icon="user-check">
    Um dos *seus* usuários. Lê o próprio contato e mais nada.
  </Card>
</CardGroup>

As quatro viajam no header `Authorization: Bearer`, exceto a chave publicável, que
vai no **corpo** da requisição dos endpoints que a aceitam.

<Note>
  Nada mais é aceito como credencial. Nenhum parâmetro de query — cairia em logs de
  acesso e no histórico do navegador. Nenhum cookie — esta API não é autenticada por
  cookie, e é também por isso que o CORS nunca precisa de credenciais.
</Note>

## Apresentando a errada

Uma credencial bem formada de outra família recebe isso, em vez de um "inválido"
seco:

```json 401 theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "this endpoint expects a staff session token, not an organization API key"
  }
}
```

Essa diferença importa: um caso é erro de configuração, o outro é evento de
segurança, e os dois não deveriam ler igual nos seus logs.

## Sessões de staff

`uk_st_…`, emitidas por `/v1/auth/signup`, `/v1/auth/login` e
`/v1/auth/two-factor`. Válidas por 30 dias.

Longas porque são **revogáveis**. Sair, trocar a senha, redefinir a senha ou perder
o vínculo matam a sessão no servidor — ao contrário de um JWT autocontido, que
continua válido até expirar, não importa o que aconteça com a conta.

```bash theme={null}
curl -s $API/v1/session -H "Authorization: Bearer uk_st_…"
```

Um usuário pode ver onde está logado e sair de todos os outros lugares:

```bash theme={null}
curl -s $API/v1/account/sessions           -H "Authorization: Bearer uk_st_…"
curl -s -X DELETE $API/v1/account/sessions -H "Authorization: Bearer uk_st_…"
```

"Todos os outros" mantém a sessão que pediu — ninguém quer ser deslogado da aba em
que clicou.

## Chaves de API da organização

`uk_sk_…`, somente no servidor. Criadas no painel, mostradas uma vez, armazenadas
como hash.

```
uk_sk_live_a91c…    uk_sk_test_a91c…
```

O ambiente faz parte do prefixo e, mais importante, da linha armazenada: é de lá
que ele é resolvido a cada requisição. Veja
[Ambientes](/pt-br/concepts/environments).

Revogar carimba `revoked_at` em vez de deletar a linha, então o rastro de auditoria
de quais chaves existiram não desaparece junto.

<Warning>
  Uma chave no bundle do navegador é uma chave na mão de cada visitante. `uk_sk_`
  pertence ao seu servidor. Para páginas, use uma chave publicável.
</Warning>

## Chaves publicáveis

`uk_pk_…`, uma por ambiente, criada junto com ele, armazenada em **texto puro** —
porque uma chave publicável identifica e quase nada autoriza, e o painel precisa
conseguir mostrá-la de novo.

Ela fica no HTML da sua página por definição. O que impede um estranho que a leu de
inundar seu tenant com contatos-lixo é a **lista de origens permitidas** na linha
dela, mais um rate limit por chave no boot.

```bash theme={null}
curl -s -X PATCH $API/v1/organization/publishable-keys/{id} \
  -H "Authorization: Bearer $UK_SESSION" \
  -H "X-Organization-Id: org_4b1e…" \
  -H 'Content-Type: application/json' \
  -d '{ "allowed_origins": ["https://app.example.com"] }'
```

A comparação é exata sobre a origem normalizada — sem curingas, sem prefixos,
porque a lista é curta e a ambiguidade de qualquer coisa mais esperta é onde moram
os bypasses.

<Warning>
  Uma **lista vazia permite qualquer origem**. É o estado de começo de vida, e é a
  única coisa a arrumar antes de ir para produção.
</Warning>

Com a lista preenchida, um header `Origin` ausente também é recusado: navegadores
sempre mandam um em requisição cross-origin, então "sem Origin" significa "não é um
navegador" — exatamente quem a lista existe para barrar.

## Sessões de contato

`uk_ct_…`, a credencial do seu usuário, emitida pelo boot, por um magic link ou
código por e-mail resgatado, por login hosted ou por verificação de e-mail.
Válida por 30 dias.

Uma sessão de contato lê o próprio contato e mais nada. Esse escopo *é* todo o
modelo de autorização do `uk_ct_`.

```bash theme={null}
curl -s $API/v1/contact/me -H "Authorization: Bearer uk_ct_…"
```

Ela também carrega uma flag `verified` — se a identidade por trás dela foi
**comprovada**:

| Como a sessão foi emitida                   | `verified` |
| ------------------------------------------- | :--------: |
| Boot com hash HMAC válido                   |      ✓     |
| Magic link resgatado                        |      ✓     |
| Código por e-mail vindo da caixa de entrada |      ✓     |
| Login hosted com senha                      |      ✓     |
| Link de verificação de e-mail               |      ✓     |
| Boot só com `anonymous_id`                  |      ✗     |
| Boot com `external_id` e sem hash           |      ✗     |

Uma sessão não verificada é usável em desenvolvimento e fica marcada no painel, mas
é barrada de qualquer coisa por onde os dados de outra pessoa pudessem vazar.

Um contato consegue ver e encerrar as próprias sessões — `GET /v1/contact/sessions`,
`DELETE /v1/contact/sessions/{id}`, e `DELETE /v1/contact/sessions` para todas as
outras. Isso faz parte da defesa do token, não é um extra: ele vive no seu domínio
sem `httpOnly`.

### A única credencial não opaca

Tudo acima é opaco — uma string aleatória cujo significado vive no nosso banco. A
exceção é o **JWT de sessão**: cinco minutos, ES256, assinado por ambiente, emitido
a partir de uma sessão `uk_ct_…` em `POST /v1/contact/token` e verificado offline
pelo seu backend contra `GET /v1/jwks/{publishable_key}`.

Ele existe para a sua API não precisar chamar a nossa a cada requisição. E é curto
justamente porque não pode ser revogado. Veja
[Tokens de sessão](/pt-br/customer-auth/session-tokens).

## Tokens de uso único

Não são credenciais que você guarda, mas seguem o mesmo formato — um prefixo cada,
para que nenhum possa ser confundido com uma sessão:

| Prefixo   | O que é                                                            | Validade       |
| --------- | ------------------------------------------------------------------ | -------------- |
| `uk_2fa_` | Desafio de dois fatores, entre a senha e o código                  | 5 minutos      |
| `uk_rt_`  | Redefinição de senha de staff                                      | 1 hora         |
| `uk_inv_` | Convite para a organização                                         | 7 dias         |
| `uk_ml_`  | Magic link de contato                                              | 1 hora         |
| `uk_ec_`  | Desafio de código por e-mail — a metade que fica na página         | 10 minutos     |
| `uk_c2_`  | Desafio de dois fatores de contato                                 | 10 minutos     |
| `uk_cv_`  | Verificação de e-mail de contato                                   | 24 horas       |
| `uk_cr_`  | Redefinição de senha de contato                                    | 1 hora         |
| `uk_is_`  | Segredo de identidade do ambiente (chave HMAC, não é de uso único) | até rotacionar |
| `org_`    | Código público da organização — nem é segredo                      | para sempre    |

## O que é armazenado

|                                    | Armazenamento                                                    |
| ---------------------------------- | ---------------------------------------------------------------- |
| Tokens e chaves de API             | SHA-256. A entropia alta torna um hash rápido e sem sal o certo. |
| Senhas                             | bcrypt no custo padrão — lentidão deliberada.                    |
| Chaves publicáveis, códigos de org | Texto puro. Identificam; não autorizam.                          |
| Segredos TOTP e de identidade      | Cifrados em repouso com AES-256.                                 |

Um dump do banco não entrega nenhuma credencial funcionando.
