> ## Documentation Index
> Fetch the complete documentation index at: https://developers.pontua.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Autenticação

> Como gerar, usar, rotacionar e revogar tokens de API. Inclui detalhes do JWT, claims e padrões de wrapping em 4 linguagens.

A API Pontua usa **tokens JWT assinados** como API Keys. Toda requisição
autenticada envia o token no header `Authorization` no formato Bearer:

```http theme={null}
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRJZCI6...
```

Sem esse header, ou com token inválido/expirado, a resposta será **`401 Unauthorized`**.

## Anatomia do token

O token é um **JWT (JSON Web Token)** com três partes separadas por `.`:

```
{header}.{payload}.{signature}
```

### Payload (claims)

Você pode decodificar o payload (sem validar a assinatura) em
[jwt.io](https://jwt.io) durante debug. Conteúdo típico:

```json theme={null}
{
  "tenantId": 416141,
  "nickname": "API.1",
  "iat": 1714060800,
  "exp": 1761350400
}
```

| Claim      | Descrição                                                                                 |
| ---------- | ----------------------------------------------------------------------------------------- |
| `tenantId` | ID interno da unidade de negócio. Resolve qual banco será consultado em todas as chamadas |
| `nickname` | Identificador da API Key (ex.: `API.1`) — aparece em logs de auditoria                    |
| `iat`      | Issued at (timestamp Unix da geração)                                                     |
| `exp`      | Expira em (timestamp Unix do fim da validade)                                             |

<Note>
  **O token NÃO contém escopo nem permissões granulares hoje.** Quem tem o
  token pode tudo que a API Pontua pública permite naquele tenant. Se você
  precisa de leitura-only para um sistema downstream, faça um proxy no seu
  lado que filtre antes de propagar.
</Note>

## Como gerar um token

<Steps>
  <Step title="Acesse o dashboard">
    Entre em [oi.pontua.com.br](https://oi.pontua.com.br) com sua conta
    administrativa. Navegue até **Gestão de Pessoas → Configurações → API**.
  </Step>

  <Step title="Crie um novo token">
    Clique em **Criar novo token** e preencha:

    * **Nome identificador** — descritivo por integração (ex.: `[PROD] erp-totvs`,
      `[HML] script-relatorio-mensal`). Vai aparecer em logs de auditoria.
    * **Validade** — entre 7 dias e 5 anos:

    | Validade           | Caso de uso                                                        |
    | ------------------ | ------------------------------------------------------------------ |
    | 7 dias             | Scripts one-shot, testes manuais, POCs                             |
    | 30 dias            | Homologações, integrações experimentais                            |
    | 45 / 90 / 180 dias | Integrações em evolução, ciclos de revisão                         |
    | 1 ano              | Integrações estáveis com rotação anual programada                  |
    | 5 anos             | Sistemas críticos onde rotação manual é inviável (use com cautela) |
  </Step>

  <Step title="Copie o token IMEDIATAMENTE">
    O valor aparece **uma única vez**. Cole em cofre seguro (1Password,
    Doppler, AWS Secrets Manager, HashiCorp Vault).
  </Step>
</Steps>

## Padrão de wrapper HTTP autenticado

Recomendamos encapsular a lógica de auth em uma função reutilizável:

<CodeGroup>
  ```javascript Node.js theme={null}
  class PontuaClient {
    constructor({ token, baseUrl = 'https://api.pontua.com.br' }) {
      if (!token) throw new Error('PONTUA_API_TOKEN obrigatório')
      this.token = token
      this.baseUrl = baseUrl
    }

    async fetch(path, init = {}) {
      const response = await fetch(`${this.baseUrl}${path}`, {
        ...init,
        headers: {
          Authorization: `Bearer ${this.token}`,
          Accept: 'application/json',
          ...init.headers,
        },
      })

      if (response.status === 401) {
        const erro = await response.json()
        throw new TokenInvalidoError(erro.message)
      }

      if (!response.ok) {
        const erro = await response.json()
        throw new PontuaApiError(response.status, erro)
      }

      return response.json()
    }
  }

  const pontua = new PontuaClient({ token: process.env.PONTUA_API_TOKEN })
  const { resultados } = await pontua.fetch('/colaborador?pagina=0&limite=10')
  ```

  ```python Python theme={null}
  import os
  import requests

  class PontuaClient:
      def __init__(self, token: str = None, base_url: str = "https://api.pontua.com.br"):
          self.token = token or os.environ.get("PONTUA_API_TOKEN")
          if not self.token:
              raise ValueError("PONTUA_API_TOKEN obrigatório")
          self.base_url = base_url

      def request(self, method: str, path: str, **kwargs):
          headers = kwargs.pop("headers", {})
          headers["Authorization"] = f"Bearer {self.token}"
          headers.setdefault("Accept", "application/json")

          resp = requests.request(method, f"{self.base_url}{path}",
                                  headers=headers, timeout=30, **kwargs)

          if resp.status_code == 401:
              raise TokenInvalidoError(resp.json().get("message"))

          if not resp.ok:
              raise PontuaApiError(resp.status_code, resp.json())

          return resp.json()

  pontua = PontuaClient()
  data = pontua.request("GET", "/colaborador", params={"pagina": 0, "limite": 10})
  ```

  ```go Go theme={null}
  package pontua

  import (
      "encoding/json"
      "fmt"
      "io"
      "net/http"
      "os"
  )

  type Client struct {
      Token   string
      BaseURL string
      HTTP    *http.Client
  }

  func NewClient() *Client {
      token := os.Getenv("PONTUA_API_TOKEN")
      if token == "" {
          panic("PONTUA_API_TOKEN obrigatório")
      }
      return &Client{
          Token:   token,
          BaseURL: "https://api.pontua.com.br",
          HTTP:    &http.Client{Timeout: 30 * time.Second},
      }
  }

  func (c *Client) Do(req *http.Request, out interface{}) error {
      req.Header.Set("Authorization", "Bearer "+c.Token)
      req.Header.Set("Accept", "application/json")
      req.URL, _ = url.Parse(c.BaseURL + req.URL.Path + "?" + req.URL.RawQuery)

      resp, err := c.HTTP.Do(req)
      if err != nil { return err }
      defer resp.Body.Close()

      if resp.StatusCode == 401 {
          body, _ := io.ReadAll(resp.Body)
          return fmt.Errorf("token inválido: %s", body)
      }
      if resp.StatusCode >= 400 {
          body, _ := io.ReadAll(resp.Body)
          return fmt.Errorf("HTTP %d: %s", resp.StatusCode, body)
      }
      return json.NewDecoder(resp.Body).Decode(out)
  }
  ```

  ```ruby Ruby theme={null}
  require 'net/http'
  require 'json'

  class PontuaClient
    BASE_URL = 'https://api.pontua.com.br'

    def initialize(token: ENV['PONTUA_API_TOKEN'])
      raise 'PONTUA_API_TOKEN obrigatório' if token.nil? || token.empty?
      @token = token
    end

    def request(method, path, body: nil, params: {})
      uri = URI("#{BASE_URL}#{path}")
      uri.query = URI.encode_www_form(params) unless params.empty?

      klass = { get: Net::HTTP::Get, post: Net::HTTP::Post,
                patch: Net::HTTP::Patch, delete: Net::HTTP::Delete }[method]
      req = klass.new(uri)
      req['Authorization'] = "Bearer #{@token}"
      req['Accept'] = 'application/json'
      req['Content-Type'] = 'application/json' if body
      req.body = body.to_json if body

      resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |h| h.request(req) }

      raise "Token inválido: #{resp.body}" if resp.code == '401'
      raise "HTTP #{resp.code}: #{resp.body}" if resp.code.to_i >= 400

      JSON.parse(resp.body)
    end
  end

  pontua = PontuaClient.new
  data = pontua.request(:get, '/colaborador', params: { pagina: 0, limite: 10 })
  ```
</CodeGroup>

## Armazenamento seguro

<CardGroup cols={2}>
  <Card icon="vault" title="Faça">
    * Cofre de secrets (1Password, Doppler, AWS SM, HashiCorp Vault)
    * Variável de ambiente em runtime (`PONTUA_API_TOKEN`)
    * Token diferente por ambiente (PROD/HML/PRE/CI)
    * Token diferente por integração (revogar uma sem afetar outras)
    * Restringir leitura do secret aos serviços que precisam
  </Card>

  <Card icon="ban" title="Não faça">
    * ❌ Comitar em repositório git (mesmo privado)
    * ❌ Enviar por e-mail, Slack ou Jira
    * ❌ Usar em código frontend (qualquer JS no browser expõe o token)
    * ❌ Tokens de 5 anos para scripts de desenvolvedor individual
    * ❌ Compartilhar um único token entre times / integrações
    * ❌ Logar o token em arquivos de log ou stdout
  </Card>
</CardGroup>

## Rotação zero-downtime

Recomendamos rotacionar tokens **no mínimo a cada 12 meses**. Para sistemas
críticos, rotação trimestral é mais segura.

<Steps>
  <Step title="Gere o novo token (mantém o antigo ativo)">
    Dashboard ou via API: `POST /api-key`. Não revogue o antigo ainda.
  </Step>

  <Step title="Atualize o secret manager com o novo">
    Sem deploy ainda. Apenas o secret muda.
  </Step>

  <Step title="Faça deploy progressivo">
    Os pods/instances vão pegar o novo token gradualmente. Monitore logs
    de erro 401 — se aumentar, faça rollback do secret antes de revogar.
  </Step>

  <Step title="Aguarde 24h">
    Tempo suficiente para todas as caches/sessions/cron jobs pegarem o novo.
  </Step>

  <Step title="Revogue o antigo">
    `PATCH /api-key/{id}` com `status: INATIVO`. Daquele momento em diante,
    chamadas com o token antigo recebem 401.
  </Step>
</Steps>

## Erros de autenticação

Todos retornam **`401 Unauthorized`** com envelope `{ service, method, message }`.

| Mensagem                | Causa típica                                             | Ação                                                                  |
| ----------------------- | -------------------------------------------------------- | --------------------------------------------------------------------- |
| `Token inválido`        | Malformado, assinatura corrompida, ou de ambiente errado | Cheque que `Bearer ` tem espaço, e que o token é do ambiente correto  |
| `Token expirado`        | Atingiu `exp` (claim)                                    | Gere novo token, atualize secret                                      |
| `Acesso não autorizado` | Header `Authorization` ausente ou token revogado         | Confirme que está enviando o header (cuidado com proxies que filtram) |

```json theme={null}
{
  "service": "AuthCoreGuard",
  "method": "canActivate",
  "message": "Token expirado"
}
```

## Revogação imediata (token vazou)

<Warning>
  Se um token vazou (commitado em repo público, mostrado em screen sharing,
  enviado em email errado), **revogue imediatamente** — não espere a rotação
  programada.
</Warning>

1. Dashboard → **Gestão de Pessoas → Configurações → API**
2. Localize o token pelo nome identificador
3. Clique em **Revogar**
4. Token desativado em até 30 segundos. Chamadas subsequentes falham com 401.
5. **Gere novo token** e atualize secret antes de re-deployar.

A revogação é **irreversível** — um token revogado nunca volta a funcionar.

## Auditoria e observabilidade

Toda ação realizada via API fica registrada com o **`nickname` do token**
que a originou. Você pode consultar a trilha em **Auditoria** no dashboard,
filtrando por "Origem: API".

Eventos auditáveis incluem:

* Criação de colaborador, ajuste de registro de ponto, geração de relatório
* Mudanças em escalas, turnos, banco de horas
* Tentativas de revogação ou modificação de tokens

Para suporte, sempre mencione:

* Nome do token (`nickname`)
* Timestamp aproximado (BRT)
* `service`, `method`, `message` da resposta de erro (se houver)

## Veja também

* [Multi-tenant](/conceitos/multi-tenant) — como o `tenantId` no token resolve a UN
* [Referência: Api Keys](/referencia/api-keys) — gerenciar tokens via API
* [Erros](/conceitos/erros) — outros 4xx além de 401
