# Integrar o Maxtra Fiscal com ajuda de IA

> Guia para quem **não é programador** ou está começando, e quer que o próprio sistema (loja, ERP, CRM, planilha com automação) emita NFS-e sozinho. Uma ferramenta de IA que escreve código, como ChatGPT, Claude, Cursor ou Copilot, faz a parte técnica com você. Você cuida das decisões e dos testes.
>
> Se você já programa, vá direto ao [guia completo da API](API.md).

---

## O que você vai precisar

1. **Acesso ao painel do Maxtra Fiscal**, com a empresa cadastrada e o certificado A1 enviado.
2. **O sistema que vai emitir as notas**, e alguém com acesso ao código dele. Pode ser você, com a ajuda da IA.
3. **Uma ferramenta de IA que escreve código.** Funciona melhor quando ela consegue abrir links ou quando você anexa os arquivos da documentação.

Não precisa entender de nota fiscal eletrônica para começar. O Maxtra Fiscal valida, assina e transmite a nota. O seu sistema só precisa dizer **para quem**, **qual serviço** e **qual valor**.

## Palavras que vão aparecer

| Termo | O que significa |
|---|---|
| **NFS-e** | Nota fiscal de serviço eletrônica. |
| **Tomador** | O cliente que contratou o serviço e recebe a nota. |
| **Homologação** | Ambiente de teste. As notas não têm validade fiscal e não consomem a franquia do plano. **Comece sempre por aqui.** |
| **Produção** | Ambiente real. As notas valem para a prefeitura. |
| **Chave de API** | Uma senha que permite ao seu sistema emitir notas em nome da empresa. Começa com `fiscal_`. |
| **Idempotency-Key** | Um código único por nota, por exemplo o número do pedido. Se a internet cair e o sistema tentar de novo com o mesmo código, o Maxtra Fiscal devolve a mesma nota em vez de emitir outra. |
| **Referência externa** | O identificador do pedido no seu sistema. Ele volta junto com a nota, para você saber de qual pedido ela é. |
| **Webhook** | Um aviso automático que o Maxtra Fiscal envia ao seu sistema quando a nota é autorizada, rejeitada ou cancelada. |

## Passo 1. Crie a chave de teste no painel

1. No painel, abra **API**.
2. Em **Chaves de acesso**, clique em **Criar chave**.
3. Dê um nome (por exemplo, "Loja virtual"), escolha o ambiente **Homologação** e a permissão **Operador: emitir e consultar**.
4. Copie a chave. **Ela aparece uma única vez.**
5. Na mesma tela, copie o **Endereço base**. É o endereço que o seu sistema vai chamar.

> **Segurança:** não cole a chave em conversas com a IA. Peça que o código leia a chave de uma variável de ambiente (`MAXTRA_API_KEY`) e coloque o valor só no servidor. Se a chave vazar, revogue-a no painel e crie outra.

## Passo 2. Entregue o guia para a sua IA

A sua ferramenta de IA não conhece o Maxtra Fiscal de antemão. Ela aprende lendo o nosso guia, então o jeito mais simples é **anexar um único arquivo** na conversa:

- **`maxtra-fiscal-guia-para-ia.md`**: este passo a passo junto com o guia completo da API. Baixe no site do Maxtra Fiscal, na seção **Integração**.

Se a sua ferramenta abre links, ela também pode ler os mesmos textos direto do servidor, em `{ENDERECO}/api/docs/iniciantes.md`, `{ENDERECO}/api/docs/guia.md` e `{ENDERECO}/api/docs/openapi.yaml`, onde `{ENDERECO}` é o endereço do seu painel.

## Passo 3. Use o pedido pronto

Anexe o arquivo, copie o texto abaixo, troque o que está entre colchetes e envie para a sua ferramenta de IA:

```text
Anexei o arquivo maxtra-fiscal-guia-para-ia.md. Ele explica a API do Maxtra Fiscal, que emite NFS-e.
Leia o arquivo inteiro antes de escrever código.

Quero integrar meu sistema para emitir a nota automaticamente.
Não sou programador experiente: explique cada passo e me diga onde colar cada arquivo.

Meu sistema: [descreva: linguagem, framework ou plataforma, por exemplo "loja em WordPress" ou "API em Node.js com Express"].
Quando emitir: [por exemplo, "quando o pedido for marcado como pago"].
Dados que tenho de cada cliente: [nome, CPF/CNPJ, e-mail, endereço...].

Regras obrigatórias:
1. Use o ambiente de homologação (cabeçalho X-Ambiente: homologacao).
2. Leia a chave de API da variável de ambiente MAXTRA_API_KEY e o endereço de MAXTRA_BASE_URL. Nunca escreva a chave no código.
3. Envie Idempotency-Key com o identificador do pedido e repita a mesma chave em toda nova tentativa.
4. Envie referencia_externa com o identificador do pedido.
5. Trate as respostas 200 (autorizada), 202 (processando), 422 (rejeitada) e 502/504 (incerto: consulte antes de tentar de novo).
6. Valores em dinheiro vão como texto, por exemplo "1500.00".

Comece me dizendo quais dados do serviço eu preciso confirmar com meu contador.
```

## Passo 4. Teste em homologação

1. Rode o código que a IA escreveu e faça um pedido de teste.
2. No painel, abra **Notas fiscais** com o ambiente **Homologação** selecionado. A nota deve aparecer lá.
3. Se ela for **rejeitada**, copie a mensagem de erro e cole na conversa com a IA. Os erros mais comuns são código de serviço, NBS, alíquota ou endereço do tomador incompletos. O [guia completo](API.md) tem a lista de erros frequentes.
4. Faça o mesmo pedido de novo, com o mesmo identificador, e confira que **não** aparece uma segunda nota.

Os dados fiscais do serviço (código, NBS, alíquota e regime) não são calculados pelo Maxtra Fiscal. Confirme esses valores com o seu contador.

## Passo 5. Receba os avisos automáticos (opcional)

Para o seu sistema saber na hora quando a nota foi autorizada, peça à IA:

```text
Agora crie a rota que recebe os webhooks do Maxtra Fiscal, conforme a seção "Webhooks" do guia.
Verifique a assinatura X-Maxtra-Signature com o segredo da variável MAXTRA_WEBHOOK_SECRET,
ignore eventos repetidos pelo X-Maxtra-Event-Id e marque o pedido como "nota emitida"
quando chegar nota.autorizada.
```

Depois cadastre o endereço dessa rota no painel, em **API → Webhooks**, e use o botão **Enviar evento de teste** para conferir.

## Passo 6. Vá para produção

1. O responsável pela empresa marca **Permitir emissões em produção nesta empresa** em **Configurações**.
2. Envie o certificado A1 (e, se o município exigir, o token da prefeitura) também no ambiente **Produção**.
3. Crie uma **nova chave** no ambiente **Produção**. A chave de homologação não funciona em produção.
4. Troque `X-Ambiente` para `producao` e a variável `MAXTRA_API_KEY` para a nova chave.
5. Emita uma primeira nota real de valor baixo e confira no painel.

## Precisa de ajuda?

Fale com a equipe do Maxtra Fiscal. Ao pedir ajuda, envie a mensagem de erro e o identificador do pedido (a `Idempotency-Key`). **Nunca envie a chave de API.**

---

<!-- Guia completo da API -->

# Maxtra Fiscal — Guia de integração da API

> Guia para quem vai integrar um sistema (ERP, plataforma de vendas, CRM) ao Maxtra Fiscal para emitir e acompanhar NFS-e.
> Idioma: português (pt-BR). Especificação formal: [`docs/openapi.yaml`](openapi.yaml) (OpenAPI 3.1).
> O guia também é lido no painel em **API → Documentação**. Índice para ferramentas de IA: [`docs/llms.txt`](llms.txt). Publicados pela API em `/api/docs/guia.md`, `/api/docs/openapi.yaml` e `/api/docs/llms.txt`.

---

## 1. Visão geral

O Maxtra Fiscal emite NFS-e no padrão nacional (DPS 1.01) por dois caminhos, definidos por empresa:

| Emissor | Quando usar | Credenciais |
|---|---|---|
| **EL · Prefeitura de Eusébio/CE** | Prestadores de Eusébio (código IBGE 2304285) | Token municipal + certificado A1 |
| **Emissor Nacional (SEFIN Nacional)** | Municípios conveniados ao Emissor Nacional | Somente certificado A1 (mTLS) |

O sistema integrado envia os dados da nota em JSON. O Maxtra Fiscal valida pelos XSDs oficiais, assina com o A1 da empresa, transmite, acompanha o processamento e avisa por **webhook** quando a nota é autorizada, rejeitada ou cancelada.

### Endereço base

Todas as rotas de uma empresa partem de:

```text
{ORIGEM}/api/tenants/{tenantId}
```

- `ORIGEM` é o endereço do servidor (em desenvolvimento local, `http://127.0.0.1:5173` pelo Vite ou `http://127.0.0.1:3001` direto na API).
- `tenantId` é o identificador da empresa, exibido em **API → Endereço base** no painel.

### Ambientes

Cada requisição informa o ambiente no cabeçalho `X-Ambiente`:

| Valor | Uso |
|---|---|
| `homologacao` (padrão) | Testes. Notas sem validade fiscal. |
| `producao` | Notas com validade fiscal. Exige liberação no servidor (`ALLOW_FISCAL_PRODUCTION=true`) **e** pelo responsável da empresa em Configurações. |

Credenciais, numeração, notas, webhooks e preferências de e-mail são separados por ambiente.

---

## 2. Autenticação

Sistemas externos usam **chave de API**:

```http
X-API-Key: fiscal_SUA_CHAVE
X-Ambiente: homologacao
```

- A chave é criada em **API → Chaves de acesso** por um responsável ou administrador e aparece **uma única vez**. O servidor guarda apenas o hash.
- Cada chave vale para **uma empresa e um ambiente**. Usar outra empresa retorna `404`; outro ambiente, `403 AMBIENTE_NAO_PERMITIDO`.
- O painel mostra o último uso de cada chave. Revogar tem efeito imediato.

### Perfis e permissões

| Perfil da chave | Pode |
|---|---|
| `operator` | Validar e emitir DPS, consultar, cancelar, baixar XML/PDF, enviar e-mail, cadastrar tomadores, ler serviços e modelos |
| `viewer` | Somente consultar: notas, XML, PDF, tomadores, serviços e modelos |

Permissão extra, concedida na criação e só para chaves `operator`:

| Permissão | Libera |
|---|---|
| `webhooks` | Cadastrar, alterar, testar e remover webhooks da empresa e do ambiente da chave |

Rotas de gestão (credenciais, certificado, membros, chaves de API, dados da empresa e preferências de e-mail) **não** aceitam chave de API: exigem sessão de usuário no painel.

---

## 3. Convenções

### Formato

- Corpo e respostas em JSON UTF-8 (`Content-Type: application/json`).
- **Valores monetários são strings** com ponto decimal: `"100.00"`. Os cálculos internos usam aritmética decimal.
- Datas: `AAAA-MM-DD`. Data e hora: ISO 8601 **com offset**, por exemplo `2026-10-15T12:00:00-03:00` (o layout fiscal não aceita sufixo `Z` em `data_emissao`).
- CPF, CNPJ, CEP, NBS e códigos aceitam pontuação; o servidor remove pontos, barras, traços e espaços. Letras ou dígitos a mais são rejeitados, nunca truncados.
- Campos desconhecidos são **rejeitados** (`422 DADOS_INVALIDOS`). Isso evita que um erro de digitação passe sem aviso.

### Erros

Todos os erros têm o mesmo formato:

```json
{
  "codigo": "DADOS_INVALIDOS",
  "mensagem": "Confira os campos informados.",
  "problemas": [
    { "campo": "tomador.endereco", "mensagem": "Informe o endereço completo do tomador identificado por CNPJ (E1358)." }
  ]
}
```

| HTTP | Significado |
|---|---|
| `200` | Sucesso. Em `/nfse`: nota **autorizada**. |
| `201` | Recurso criado. |
| `202` | Aceito, ainda processando (DPS recebida, autorização pendente). |
| `204` | Sucesso sem corpo. |
| `400` | JSON malformado. |
| `401` | Chave ausente, inválida ou revogada. |
| `403` | Perfil, permissão ou ambiente não permitido; produção bloqueada. |
| `404` | Recurso não encontrado **nesta empresa e ambiente**. |
| `409` | Conflito: idempotência, documento já enviado, cadastro duplicado. |
| `413` | Corpo ou arquivo acima do limite. |
| `422` | Validação de dados ou **DPS rejeitada** pelo emissor. |
| `429` | Limite de requisições. Respeite `Retry-After` quando presente. |
| `502` / `504` | Falha ou timeout com o emissor. O resultado fica **incerto**: consulte antes de reenviar. |
| `503` | Integração incompleta (sem A1/token) ou serviço indisponível. |

### Limites

| Escopo | Limite |
|---|---|
| Por chave de API | 240 requisições por minuto |
| Por empresa (somando todas as chaves e usuários) | 120 requisições por minuto |
| Operações simultâneas por chave | 8 |
| Consulta de CEP | 30 por minuto |

As respostas trazem os cabeçalhos `RateLimit` e `RateLimit-Policy` (IETF draft-8), com o restante da janela.

### Idempotência (obrigatória)

Emissão (`POST /nfse`, `POST /nfse/xml`) e cancelamento exigem o cabeçalho:

```http
Idempotency-Key: pedido-1001-emissao
```

- 8 a 128 caracteres: `A-Z a-z 0-9 _ . : -`.
- Mesma chave + mesmos dados → devolve a **resposta original** sem nova transmissão, com `Idempotent-Replayed: true`.
- Mesma chave + dados diferentes → `409 IDEMPOTENCIA_CONFLITO`.
- Use um valor derivado do seu pedido (`pedido-{id}-emissao`). Em timeout de rede, **repita com a mesma chave**: nunca há emissão em dobro.

### Paginação

Listas retornam `{ "items": [...], "total": 42, "page": 1, "limit": 12 }`. Parâmetros: `page` e `limite` (até 100).

---

## 4. Ciclo de vida da nota

```text
POST /nfse ──► enviando ──► processando ──► autorizada ──► cancelada
                   │              │
                   │              └──► rejeitada (corrigir e reenviar)
                   └──► incerto (timeout/falha: consultar, nunca reenviar às cegas)
```

| Status | Significado | O que fazer |
|---|---|---|
| `enviando` | Reserva gravada antes da transmissão | Aguarde. Se persistir, consulte. |
| `processando` | DPS recebida, sem XML definitivo | Aguarde o webhook ou consulte |
| `autorizada` | NFS-e com chave e XML definitivo conferidos | Baixe XML/PDF, envie ao cliente |
| `rejeitada` | Erros devolvidos pelo emissor em `erros` | Corrija os dados e reenvie (ver 6.5) |
| `incerto` | Transmissão sem confirmação (timeout, resposta inválida) | Consulte o processamento; não reenvie |
| `cancelada` | Evento de cancelamento registrado | — |

### Acompanhamento automático

Você **não precisa fazer polling**. O servidor acompanha as DPS em `processando` ou `incerto`: consulta o emissor após 15 s, 30 s, 1 min, 2 min, 5 min, 10 min, 15 min e 30 min, depois a cada hora, por até 72 horas. A consulta só lê o processamento e **nunca retransmite** a DPS. Cada mudança gera webhook.

Se preferir consultar (ou para reconciliar), use `GET /notes/{idDps}` (lê o histórico local, sem chamar o emissor) ou `GET /dps/{idDps}/processamento` (consulta o emissor e atualiza o histórico). Intervalo mínimo recomendado: 15 segundos.

---

## 5. Referência rápida

Todas relativas a `/api/tenants/{tenantId}`, salvo indicação.

| Método | Caminho | Finalidade | Chave |
|---|---|---|---|
| GET | `/` | Dados da empresa | viewer |
| GET | `/dps/proximo-numero?serie=1` | Próximo número da série (sem reservar) | viewer |
| POST | `/dps/validar` | Gera e valida a DPS por JSON, sem transmitir | operator |
| POST | `/dps/validar-xml` | Valida XML completo, sem transmitir | operator |
| POST | `/nfse` | Valida, assina e transmite (JSON) | operator |
| POST | `/nfse/xml` | Assina e transmite XML completo | operator |
| GET | `/notes` | Histórico: `q`, `status`, `referencia`, `desde`, `ate`, `limite`, `page` | viewer |
| GET | `/notes/{idDps}` | Nota com status, XML e retorno do emissor | viewer |
| GET | `/dps/{idDps}/processamento` | Consulta o emissor e atualiza o status | viewer |
| GET | `/nfse/{chave}` | Consulta a NFS-e no emissor | viewer |
| GET | `/nfse/{chave}/xml` | XML autorizado | viewer |
| GET | `/nfse/{chave}/pdf` | DANFSe em PDF (`?download=1` para anexo) | viewer |
| POST | `/nfse/{chave}/email` | Envia PDF e XML por e-mail | operator |
| GET | `/nfse/{chave}/emails` | Histórico de e-mails da nota | viewer |
| POST | `/nfse/{chave}/cancelamento` | Cancela a NFS-e | operator |
| GET / POST | `/customers` | Lista ou cadastra tomadores | viewer / operator |
| GET / PUT / DELETE | `/customers/{id}` | Consulta, atualiza ou arquiva | viewer / operator |
| POST | `/customers/{id}/restore` | Restaura tomador arquivado | operator |
| GET / POST | `/services`, `/templates` | Serviços e modelos (escrita só no painel) | viewer |
| GET / POST | `/recurrences` | Lista ou cria notas recorrentes | viewer / operator |
| GET / PATCH / DELETE | `/recurrences/{id}` | Consulta, altera, pausa/retoma ou encerra | viewer / operator |
| GET | `/recurrences/{id}/execucoes` | Histórico mensal das emissões | viewer |
| POST | `/recurrences/{id}/emitir-agora` | Emite a nota do mês corrente já | operator |
| GET | `/exports/notas.zip?desde=&ate=` | ZIP com XMLs, DANFSe e CSV para o contador | viewer |
| GET / POST | `/webhooks` | Lista ou cadastra webhooks | `webhooks` |
| PATCH / DELETE | `/webhooks/{id}` | Altera (inclusive `ativo`) ou remove | `webhooks` |
| POST | `/webhooks/{id}/teste` | Envia `webhook.teste` agora | `webhooks` |
| POST | `/webhooks/{id}/segredo` | Gera novo segredo | `webhooks` |
| GET | `/webhooks/{id}/entregas` | Últimas 50 entregas | `webhooks` |
| POST | `/webhooks/{id}/entregas/{entregaId}/reenviar` | Reenvia uma entrega | `webhooks` |
| GET | `/api/cep/{cep}` | Endereço por CEP (fora do tenant) | viewer |
| GET | `/health` | Saúde do processo (público) | — |

---

## 6. Emitir NFS-e

### 6.1 Requisição

```http
POST /api/tenants/{tenantId}/nfse
X-API-Key: fiscal_...
X-Ambiente: homologacao
Idempotency-Key: pedido-1001-emissao
Content-Type: application/json
```

```json
{
  "referencia_externa": "PEDIDO-1001",
  "serie": 1,
  "data_emissao": "2026-10-15T12:00:00-03:00",
  "competencia": "2026-10-15",
  "tomador": {
    "documento": "12.345.678/0001-95",
    "nome": "Cliente Exemplo Ltda",
    "email": "financeiro@cliente.com.br",
    "endereco": {
      "codigo_municipio": "2304285",
      "cep": "61760-000",
      "logradouro": "Rua de Exemplo",
      "numero": "100",
      "bairro": "Centro"
    }
  },
  "servico": {
    "codigo_tributacao_nacional": "170601",
    "codigo_servico_municipal": "1706",
    "codigo_nbs": "114061900",
    "descricao": "Gestão de mídia paga — outubro/2026 (pedido 1001)",
    "codigo_municipio_prestacao": "2304285",
    "valor": "1500.00"
  },
  "tributacao": {
    "tributacao_issqn": 1,
    "tipo_retencao_issqn": 1,
    "aliquota_issqn": "2.01",
    "totais": { "percentual_simples_nacional": "6.00" }
  }
}
```

### 6.2 Campos

| Campo | Obrigatório | Descrição |
|---|---|---|
| `referencia_externa` | Recomendado | Seu identificador (pedido, contrato). Até 64 caracteres `A-Z a-z 0-9 _ . : / -`. Volta na nota, nos filtros e nos webhooks. |
| `serie` | Sim | 1 a 99999. |
| `numero` | Não | Omita para a sequência automática da empresa/ambiente/série. Informe para fixar o número (o próximo continua a partir dele). Obrigatório ao **corrigir** uma rejeitada (ver 6.5). |
| `data_emissao` | Sim | Data e hora com offset. |
| `competencia` | Sim | `AAAA-MM-DD`. |
| `tomador` | Não* | Dados do tomador. CNPJ exige `endereco` completo (rejeição E1358). |
| `tomador_id` | Não* | Id de um tomador de `/customers`, no lugar de `tomador`. Enviar os dois retorna `422 TOMADOR_DUPLICADO`. |
| `servico.codigo_tributacao_nacional` | Sim | cTribNac, 6 dígitos. |
| `servico.codigo_servico_municipal` | EL: sim | cIntContrib habilitado para o prestador no município. |
| `servico.codigo_tributacao_municipal` | Não | cTribMun, 3 dígitos (campo distinto do anterior). |
| `servico.codigo_nbs` | EL: sim | 9 dígitos. |
| `servico.descricao` | Sim | Até 2000 caracteres. Seja específico; muitos municípios exigem detalhamento. |
| `servico.codigo_municipio_prestacao` | Sim | IBGE, 7 dígitos. |
| `servico.valor` | Sim | Valor do serviço, string. |
| `servico.desconto_incondicionado` / `desconto_condicionado` | Não | Strings monetárias. |
| `tributacao.tributacao_issqn` | Sim | `1` = operação tributável (o formulário JSON cobre esse caso; para imunidade, exportação ou não incidência, use XML completo). |
| `tributacao.tipo_retencao_issqn` | Sim | `1` não retido · `2` retido pelo tomador · `3` retido pelo intermediário. |
| `tributacao.aliquota_issqn` | EL: sim | Percentual, `"2.01"`. |
| `tributacao.totais` | Sim | **Uma** modalidade: `{ "federal", "estadual", "municipal" }` (valores), `{ "percentual_simples_nacional" }` ou `{ "indicador_nao_informar": 0 }`. |
| `regime_tributario` | Não | Omitido = enquadramento salvo na empresa (Configurações). |
| `ibs_cbs` | Não | Grupo básico de IBS/CBS (reforma tributária). |

\* O tomador pode ser omitido quando o serviço permitir tomador não identificado.

### 6.3 Respostas

| HTTP | `status` | Significado |
|---|---|---|
| `200` | `autorizada` | Autorizada na hora (comum no Emissor Nacional). Vem `chave_acesso` e `xml`. |
| `202` | `processando` | Recebida pelo emissor (comum na EL). A autorização chega por webhook. |
| `422` | `rejeitada` | Erros em `erros[]` (`codigo`, `descricao`, `complemento`). |
| `502` / `504` | `incerto` | Sem confirmação. **Não reenvie com outra chave**: consulte `/dps/{id_dps}/processamento`. |

```json
{
  "ambiente": "homologacao",
  "status": "processando",
  "id_dps": "DPS230428525383122700014500001000000000000029",
  "chave_acesso": null,
  "erros": [],
  "xml": null
}
```

Guarde `id_dps`: é o identificador da DPS no Maxtra Fiscal e no emissor.

### 6.4 Validar sem transmitir

`POST /dps/validar` aceita o mesmo corpo, gera o XML e valida pelos XSDs **sem transmitir** e sem consumir número. Útil em testes de integração.

### 6.5 Corrigir uma DPS rejeitada

Com rejeição confirmada (`status: rejeitada`), envie os dados corrigidos com **a mesma série e o mesmo número** e uma **nova** `Idempotency-Key`. O sistema reconhece a correção pelo histórico e reutiliza o número, sem avançar a sequência. Dados idênticos, autorização, cancelamento ou estado incerto não liberam reenvio (`409 DOCUMENTO_JA_ENVIADO`).

### 6.6 XML completo

Para operações que o JSON não cobre (tributos federais, imunidade, exportação, substituição), envie a DPS pronta:

```json
{ "xml": "<DPS xmlns=\"http://www.sped.fazenda.gov.br/nfse\" versao=\"1.01\">…</DPS>", "referencia_externa": "PEDIDO-1001" }
```

O servidor descarta a assinatura recebida, confere prestador, município, ambiente e identificador e reassina com o A1 da empresa.

---

## 7. Consultar, baixar e enviar

| Objetivo | Chamada |
|---|---|
| Nota pela sua referência | `GET /notes?referencia=PEDIDO-1001` |
| Notas autorizadas de outubro | `GET /notes?status=autorizada&desde=2026-10-01&ate=2026-10-31&limite=100` |
| Detalhe com XML e retorno do emissor | `GET /notes/{idDps}` |
| XML autorizado | `GET /nfse/{chave}/xml` |
| PDF (DANFSe) | `GET /nfse/{chave}/pdf` (inline) ou `?download=1` |
| Enviar ao tomador | `POST /nfse/{chave}/email` com `{}` ou `{ "destinatarios": ["a@b.com"] }` |

### PDF (DANFSe)

O PDF segue o leiaute do DANFSe nacional (emitente, tomador, serviço, tributação municipal e federal, totais e QR code para a consulta pública) e é gerado a partir do XML autorizado. Em homologação ele sai com a marca "SEM VALIDADE JURÍDICA". **O documento fiscal válido é o XML.**

### E-mail

- O servidor envia PDF e XML anexados, com remetente "{empresa} via Maxtra Fiscal" e resposta para o e-mail da empresa.
- Envio automático ao autorizar: ative por empresa e ambiente em **Configurações → E-mail da nota** (desligado por padrão).
- Sem `destinatarios`, usa o e-mail do tomador que está na nota. Até 5 destinatários, 10 envios por nota por hora.
- Requer SMTP no servidor (`SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `EMAIL_FROM`). Sem isso: `503 EMAIL_NAO_CONFIGURADO`.

---

## 8. Cancelar

```http
POST /nfse/{chave}/cancelamento
Idempotency-Key: pedido-1001-cancelamento
```

```json
{
  "data_evento": "2026-10-16T09:30:00-03:00",
  "codigo_motivo": 1,
  "motivo": "Erro no preenchimento dos dados do tomador."
}
```

`codigo_motivo`: `1` erro na emissão · `2` serviço não prestado · `9` outros. `motivo`: 15 a 255 caracteres. A nota só fica `cancelada` quando o XML do evento retorna. Prazos de cancelamento são definidos pelo município.

---

## 9. Tomadores

Cadastro de clientes por empresa (compartilhado entre os ambientes). O CPF/CNPJ é único entre os tomadores ativos.

```http
POST /customers
```

```json
{
  "documento": "11.222.333/0001-81",
  "nome": "Cliente Exemplo Ltda",
  "email": "financeiro@cliente.com.br",
  "telefone": "(85) 99999-0000",
  "inscricao_municipal": "123456",
  "endereco": {
    "codigo_municipio": "3550308",
    "cep": "01310-100",
    "logradouro": "Avenida Paulista",
    "numero": "1000",
    "bairro": "Bela Vista"
  },
  "observacoes": "Não aparece na nota."
}
```

- `409 TOMADOR_JA_CADASTRADO` devolve `tomador_id` do existente; use `PUT /customers/{id}` para atualizar.
- Busque por documento exato com `GET /customers?documento=11222333000181` ou por texto com `?q=`.
- `DELETE` arquiva (recuperável com `/restore`). Tomador arquivado não pode ser usado em `tomador_id`.
- `GET /api/cep/{cep}` devolve `logradouro`, `bairro`, `municipio`, `uf` e `codigo_municipio` (IBGE) para preencher o endereço.

---

## 10. Notas recorrentes

Mensalidades de clientes fixos: a recorrência emite uma nota por mês, no dia escolhido, pelo **mesmo fluxo de `POST /nfse`** (validação, assinatura, transmissão, acompanhamento, webhooks e e-mail automático).

```http
POST /recurrences
```

```json
{
  "nome": "Mensalidade Padaria Central",
  "modelo_id": "7a1c2e4f-5b6d-4c8e-9f0a-1b2c3d4e5f60",
  "valor": "350.00",
  "dia": 5,
  "inicio": "2026-11",
  "fim": "2027-10"
}
```

- **Origem**: `modelo_id` (um modelo de nota de `/templates`) **ou** `servico_id` + `tomador_id` (um serviço de `/services` e um tomador de `/customers`). Com modelo, `tomador_id` é opcional e substitui o tomador do modelo; é obrigatório se o modelo não tiver tomador (`422 TOMADOR_OBRIGATORIO`).
- **Cada mês** usa os dados **atuais** do modelo, serviço e tomador, com `valor` no lugar do valor do serviço. `data_emissao` e `competencia` são o dia da emissão (mês corrente, horário de Fortaleza). `serie` padrão `1`, numeração automática.
- **Dia**: 1 a 31. Em meses mais curtos a nota sai no último dia (31 = sempre no último dia). Se o servidor estiver parado no dia, a nota sai assim que ele voltar, ainda no mesmo mês; meses passados nunca são emitidos depois.
- **Período**: `inicio` (padrão, o mês corrente) e `fim` opcional, ambos `AAAA-MM`.
- **Nunca duas vezes**: a emissão usa `Idempotency-Key` e `referencia_externa` iguais a `recorrencia:{id}:{AAAA-MM}`. Para achar a nota do mês: `GET /notes?referencia=recorrencia:{id}:2026-11`.
- **Validação na criação**: a nota do mês é montada e validada (XSD incluído) antes de salvar, sem transmitir. Erros vêm como em `POST /dps/validar`.
- **Produção**: criar, retomar e emitir em produção exigem a produção liberada no servidor e pela empresa (`403 PRODUCAO_BLOQUEADA`). Se ela for desligada depois, o mês fica `falhou` com esse código.

| Método | Caminho | Finalidade | Chave |
|---|---|---|---|
| GET | `/recurrences` | Lista (`?encerradas=1` para as encerradas) | viewer |
| POST | `/recurrences` | Cria e valida | operator |
| GET | `/recurrences/{id}` | Detalhe, com `proxima_emissao` e `ultima_execucao` | viewer |
| PATCH | `/recurrences/{id}` | Altera; `{"ativo": false}` pausa, `{"ativo": true}` retoma | operator |
| DELETE | `/recurrences/{id}` | Encerra (o histórico fica) | operator |
| GET | `/recurrences/{id}/execucoes` | Histórico por mês, com o status atual da nota | viewer |
| POST | `/recurrences/{id}/emitir-agora` | Emite o mês corrente já, ou tenta de novo um mês que falhou | operator |

Cada execução tem `estado`:

- `enviada`: a DPS entrou no fluxo de emissão. O resultado fiscal está em `nota.status` (`processando`, `autorizada`, `rejeitada`…). Uma rejeição **não** é reenviada sozinha: corrija o modelo, o serviço ou o tomador e emita pela tela de emissão.
- `falhou`: nada foi transmitido (por exemplo `INTEGRACAO_INCOMPLETA`, `CADASTRO_ARQUIVADO`, `PRODUCAO_BLOQUEADA`). `erro` traz `{codigo, mensagem, problemas?}`. Nova tentativa automática a cada hora, até 6 vezes no mês, ou por `emitir-agora`.
- `emitindo`: envio em curso.

Retomar uma recorrência pausada não emite os dias que passaram durante a pausa. `emitir-agora` repetido devolve a mesma execução, sem novo envio.

---

## 11. Exportação para o contador

```http
GET /exports/notas.zip?desde=2026-10-01&ate=2026-10-31
```

Devolve um ZIP (`application/zip`, em fluxo) com as notas **autorizadas e canceladas** emitidas no período, neste ambiente:

| Arquivo | Conteúdo |
|---|---|
| `xml/NFSe-{numero}-{chave}.xml` | XML autorizado de cada nota |
| `pdf/DANFSe-{numero}-{chave}.pdf` | DANFSe gerada do XML |
| `resumo.csv` | Uma linha por nota: `numero`, `chave_acesso`, `data_emissao`, `competencia`, `tomador`, `cpf_cnpj_tomador`, `valor_servico`, `iss`, `iss_retido`, `status` (Autorizada/Cancelada) e `observacao` |

- O CSV é UTF-8 com BOM, separado por `;`, com vírgula decimal e datas `dd/mm/aaaa`: abre direto no Excel em pt-BR.
- `desde` e `ate` (`AAAA-MM-DD`, inclusive) são obrigatórios e contam a data de emissão no horário de Fortaleza. Período de até **366 dias**: `422 PERIODO_LONGO`; datas ausentes ou invertidas: `422 PERIODO_INVALIDO`.
- Sem notas no período: `404 NENHUMA_NOTA`.
- Qualquer perfil, inclusive chaves `viewer`. Até 6 exportações por minuto por empresa (`429 LIMITE_EXPORTACAO`).
- No painel: **Notas fiscais → Exportar para o contador**.

---

## 12. Webhooks

### 12.1 Eventos

| Evento | Quando |
|---|---|
| `nota.status_alterado` | Qualquer mudança de status (inclui as abaixo) |
| `nota.autorizada` | NFS-e autorizada, com XML definitivo |
| `nota.rejeitada` | DPS rejeitada; erros em `dados.erros` |
| `nota.cancelada` | Cancelamento registrado |
| `webhook.teste` | Disparado por "Testar" no painel ou `POST /webhooks/{id}/teste` |

Assine apenas `nota.status_alterado` para receber todo o ciclo, ou os eventos específicos de que precisa.

### 12.2 Cadastro

Pelo painel (**API → Webhooks**) ou pela API, com uma chave que tenha a permissão `webhooks`:

```http
POST /webhooks
```

```json
{
  "url": "https://erp.maxtramidia.com.br/webhooks/maxtra-fiscal",
  "eventos": ["nota.autorizada", "nota.rejeitada", "nota.cancelada"],
  "descricao": "ERP Maxtra Mídia"
}
```

A resposta traz `segredo` (`whsec_…`) **uma única vez**. Guarde-o como variável de ambiente do seu sistema. Até 10 webhooks por empresa e ambiente.

### 12.3 Entrega

`POST` na sua URL com `Content-Type: application/json` e os cabeçalhos:

| Cabeçalho | Conteúdo |
|---|---|
| `X-Maxtra-Event` | Nome do evento |
| `X-Maxtra-Event-Id` | Id único do evento. **Use para deduplicar.** |
| `X-Maxtra-Delivery` | Id da tentativa de entrega |
| `X-Maxtra-Timestamp` | Unix timestamp (segundos) |
| `X-Maxtra-Signature` | `t=<timestamp>,v1=<hmac>` |
| `User-Agent` | `MaxtraFiscal-Webhooks/1.0` |

```json
{
  "id": "6f1c2c8e-0b5a-4f0e-9d52-1c7f0e3a9b10",
  "evento": "nota.autorizada",
  "criado_em": "2026-10-15T15:02:11.481Z",
  "ambiente": "producao",
  "empresa": { "id": "22349de0-…", "nome": "Maxtra Mídia Ltda", "documento": "12345678000195" },
  "dados": {
    "id": "b1d0…",
    "id_dps": "DPS2304285…",
    "referencia_externa": "PEDIDO-1001",
    "status": "autorizada",
    "status_anterior": "processando",
    "chave_acesso": "23042851253831227000145260000000000126100000002532",
    "total": "1500.00",
    "tomador": "Cliente Exemplo Ltda",
    "competencia": "2026-10-15",
    "emissor": "el_eusebio",
    "erros": [],
    "criado_em": "2026-10-15T15:01:40.010Z",
    "atualizado_em": "2026-10-15T15:02:11.470Z",
    "links": {
      "nota": "https://fiscal.exemplo.com.br/api/tenants/22349de0-…/notes/DPS2304285…",
      "xml": "https://fiscal.exemplo.com.br/api/tenants/22349de0-…/nfse/2304…/xml"
    }
  }
}
```

O webhook não carrega o XML nem o PDF: busque-os pelos links, com a sua chave de API.

### 12.4 Verificar a assinatura

`v1 = HMAC-SHA256(segredo, "<t>.<corpo bruto>")`, em hexadecimal. Verifique sempre sobre o **corpo bruto**, antes de qualquer `JSON.parse`, e recuse timestamps com mais de 5 minutos de diferença.

```js
import { createHmac, timingSafeEqual } from "node:crypto";

export function verificarMaxtra(rawBody, assinatura, segredo) {
  const { t, v1 } = Object.fromEntries(
    String(assinatura).split(",").map((p) => p.split("=")),
  );
  if (!t || !v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const esperado = createHmac("sha256", segredo).update(`${t}.${rawBody}`).digest("hex");
  return (
    esperado.length === v1.length &&
    timingSafeEqual(Buffer.from(esperado), Buffer.from(v1))
  );
}
```

### 12.5 Reenvio e garantias

- Responda **2xx em até 10 segundos**. Processe de forma assíncrona se precisar de mais tempo.
- Falhas (timeout, conexão, status fora de 2xx) são reenviadas após 1 min, 5 min, 30 min, 2 h, 6 h e 12 h. Depois da 7ª falha o webhook é **pausado**; reative no painel ou com `PATCH /webhooks/{id}` `{ "ativo": true }`. As entregas pendentes são retomadas.
- Entrega **ao menos uma vez**: o mesmo evento pode chegar mais de uma vez. Deduplique por `X-Maxtra-Event-Id`.
- A ordem não é garantida entre eventos diferentes; use `dados.atualizado_em` e `dados.status` como fonte da verdade.
- Os eventos são gravados na mesma transação da mudança de status: não se perdem se o servidor reiniciar.
- Em produção, o destino precisa ser **HTTPS público**. Endereços privados, loopback e redirecionamentos são recusados.

---

## 13. Receita: integrar um sistema Express + Prisma (Postgres)

Fluxo recomendado para um ERP que vende serviços (por exemplo, a Maxtra Mídia):

1. O pedido é pago (o seu gateway confirma o boleto/Pix/cartão).
2. O ERP chama `POST /nfse` com `referencia_externa` = id do pedido e `Idempotency-Key` derivada do pedido.
3. O Maxtra Fiscal acompanha e envia o webhook `nota.autorizada` (ou `nota.rejeitada`).
4. O ERP marca o pedido como faturado, guarda a chave e, se quiser, baixa PDF/XML.

> **Emitir somente após o pagamento** é decisão do sistema integrado: quem sabe que o pagamento foi confirmado é o ERP (ou o gateway). Chame `POST /nfse` nesse momento. Não é preciso registrar a venda antes no Maxtra Fiscal.

### 13.1 Variáveis de ambiente

```bash
MAXTRA_FISCAL_URL=https://fiscal.suaempresa.com.br
MAXTRA_TENANT_ID=22349de0-...
MAXTRA_API_KEY=fiscal_...
MAXTRA_AMBIENTE=homologacao
MAXTRA_WEBHOOK_SECRET=whsec_...
```

### 13.2 Modelo Prisma

```prisma
model FiscalInvoice {
  id                String   @id @default(cuid())
  orderId           String   @unique            // = referencia_externa
  idempotencyKey    String   @unique
  idDps             String?  @unique
  status            String   @default("pendente") // pendente | processando | autorizada | rejeitada | incerto | cancelada
  chaveAcesso       String?  @unique
  total             Decimal  @db.Decimal(12, 2)
  errors            Json?
  lastEventAt       DateTime?
  createdAt         DateTime @default(now())
  updatedAt         DateTime @updatedAt
}

model FiscalWebhookEvent {
  id         String   @id                       // X-Maxtra-Event-Id
  event      String
  receivedAt DateTime @default(now())
}
```

### 13.3 Cliente da API

```js
// src/fiscal/client.js
const base = `${process.env.MAXTRA_FISCAL_URL}/api/tenants/${process.env.MAXTRA_TENANT_ID}`;

export async function fiscal(path, { method = "GET", body, idempotencyKey } = {}) {
  const response = await fetch(base + path, {
    method,
    headers: {
      "X-API-Key": process.env.MAXTRA_API_KEY,
      "X-Ambiente": process.env.MAXTRA_AMBIENTE,
      ...(body ? { "Content-Type": "application/json" } : {}),
      ...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
    signal: AbortSignal.timeout(60_000),
  });
  const data = response.status === 204 ? null : await response.json().catch(() => null);
  return { status: response.status, data };
}
```

### 13.4 Emitir quando o pedido for pago

```js
// src/fiscal/emit.js
import { prisma } from "../db.js";
import { fiscal } from "./client.js";

// Data e hora no fuso do prestador (Ceará: -03:00), sem milissegundos.
const agora = () =>
  new Date(Date.now() - 3 * 3600_000).toISOString().slice(0, 19) + "-03:00";

export async function emitirNota(order) {
  const key = `pedido-${order.id}-emissao`;
  const invoice = await prisma.fiscalInvoice.upsert({
    where: { orderId: order.id },
    create: { orderId: order.id, idempotencyKey: key, total: order.total },
    update: {},
  });
  if (["processando", "autorizada"].includes(invoice.status)) return invoice;

  const { status, data } = await fiscal("/nfse", {
    method: "POST",
    idempotencyKey: invoice.idempotencyKey,   // repetir é seguro: nunca emite duas vezes
    body: {
      referencia_externa: order.id,
      serie: 1,
      data_emissao: agora(),
      competencia: agora().slice(0, 10),
      tomador_id: order.customer.fiscalCustomerId, // ou "tomador": { ... }
      servico: {
        codigo_tributacao_nacional: "170601",
        codigo_servico_municipal: "1706",
        codigo_nbs: "114061900",
        descricao: `Pedido ${order.number}: ${order.description}`,
        codigo_municipio_prestacao: "2304285",
        valor: order.total.toFixed(2),
      },
      tributacao: {
        tributacao_issqn: 1,
        tipo_retencao_issqn: 1,
        aliquota_issqn: "2.01",
        totais: { percentual_simples_nacional: "6.00" },
      },
    },
  });

  // 200 autorizada · 202 processando · 422 rejeitada/validação · 5xx incerto
  return prisma.fiscalInvoice.update({
    where: { id: invoice.id },
    data: {
      idDps: data?.id_dps ?? invoice.idDps,
      status: data?.status ?? (status >= 500 ? "incerto" : invoice.status),
      chaveAcesso: data?.chave_acesso ?? undefined,
      errors: data?.erros?.length ? data.erros : data?.problemas ?? undefined,
    },
  });
}
```

> `data_emissao` precisa de offset e não aceita `Z`. Gere no fuso do município do prestador.

### 13.5 Receber o webhook

```js
// src/fiscal/webhook.js
import express from "express";
import { Prisma } from "@prisma/client";
import { prisma } from "../db.js";
import { verificarMaxtra } from "./verify.js";

export const fiscalWebhook = express.Router();

// express.raw: a assinatura é calculada sobre o corpo bruto.
fiscalWebhook.post("/webhooks/maxtra-fiscal", express.raw({ type: "application/json", limit: "256kb" }), async (req, res) => {
  const raw = req.body.toString("utf8");
  if (!verificarMaxtra(raw, req.get("X-Maxtra-Signature"), process.env.MAXTRA_WEBHOOK_SECRET))
    return res.status(401).end();

  const event = JSON.parse(raw);
  try {
    // Deduplicação: o id do evento é chave primária.
    await prisma.fiscalWebhookEvent.create({ data: { id: event.id, event: event.evento } });
  } catch (e) {
    if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2002") return res.status(200).end();
    throw e;
  }
  res.status(200).end(); // responda rápido; o processamento segue abaixo

  if (event.evento === "webhook.teste") return;
  const nota = event.dados;
  await prisma.fiscalInvoice.updateMany({
    where: {
      orderId: nota.referencia_externa,
      // ignora eventos mais antigos que o último aplicado
      OR: [{ lastEventAt: null }, { lastEventAt: { lt: new Date(nota.atualizado_em) } }],
    },
    data: {
      status: nota.status,
      idDps: nota.id_dps,
      chaveAcesso: nota.chave_acesso,
      errors: nota.erros.length ? nota.erros : Prisma.JsonNull,
      lastEventAt: new Date(nota.atualizado_em),
    },
  });
  // Ex.: ao autorizar, marcar o pedido como faturado e notificar o cliente.
});
```

Registre o router **antes** de `app.use(express.json())`, ou o corpo bruto se perde.

### 13.6 Reconciliação (rede de segurança)

Uma vez por hora, compare pedidos `processando`/`incerto` com o histórico:

```js
const { data } = await fiscal(`/notes?referencia=${encodeURIComponent(order.id)}`);
const nota = data.items[0]; // status atual no Maxtra Fiscal
```

### 13.7 Baixar PDF ou XML

```js
const response = await fetch(`${base}/nfse/${chave}/pdf?download=1`, {
  headers: { "X-API-Key": process.env.MAXTRA_API_KEY, "X-Ambiente": process.env.MAXTRA_AMBIENTE },
});
const pdf = Buffer.from(await response.arrayBuffer());
```

---

## 14. Códigos de erro frequentes

| Código | Causa | Ação |
|---|---|---|
| `NAO_AUTORIZADO` | Chave ausente, inválida ou revogada | Confira `X-API-Key` |
| `AMBIENTE_NAO_PERMITIDO` | Chave de outro ambiente | Use a chave do ambiente certo |
| `PERMISSAO_INSUFICIENTE` | Perfil ou permissão da chave | Crie a chave com o perfil/permissão necessários |
| `DADOS_INVALIDOS` | Campo ausente, formato inválido ou campo desconhecido | Veja `problemas[]` |
| `DADOS_MUNICIPAIS_OBRIGATORIOS` | EL sem NBS, alíquota ou código municipal | Informe os campos indicados |
| `IDEMPOTENCIA_OBRIGATORIA` | Falta `Idempotency-Key` | Envie o cabeçalho |
| `IDEMPOTENCIA_CONFLITO` | Mesma chave com outros dados | Use nova chave para nova operação |
| `ENVIO_EM_ANDAMENTO` | Envio reservado e ainda sem resposta | Consulte o processamento |
| `DOCUMENTO_JA_ENVIADO` | DPS já transmitida e não rejeitada | Não reenvie; consulte |
| `INTEGRACAO_INCOMPLETA` | Sem A1 (ou token, na EL) neste ambiente | Configure no painel |
| `PRODUCAO_BLOQUEADA` | Produção não liberada | Libere no servidor e na empresa |
| `CONTA_SUSPENSA` | A conta (assinatura) da empresa está suspensa | Novas emissões em produção ficam bloqueadas; consultas, XML e PDF continuam. Regularize com o suporte |
| `TOMADOR_DUPLICADO` / `TOMADOR_ARQUIVADO` | `tomador` + `tomador_id` / tomador arquivado | Envie só um / restaure |
| `NOTA_NAO_AUTORIZADA` | Cancelamento ou e-mail de nota não autorizada | Aguarde a autorização |
| `EMAIL_NAO_CONFIGURADO` | Servidor sem SMTP | Configure o SMTP |
| `TOMADOR_OBRIGATORIO` | Recorrência com modelo sem tomador e sem `tomador_id` | Escolha um tomador |
| `RECORRENCIA_INATIVA` / `FORA_DO_PERIODO` | `emitir-agora` em recorrência pausada, encerrada ou fora do período | Retome ou ajuste `inicio`/`fim` |
| `PERIODO_INVALIDO` / `PERIODO_LONGO` | Exportação sem datas, invertida ou com mais de 366 dias | Ajuste `desde` e `ate` |
| `NENHUMA_NOTA` | Exportação sem notas autorizadas ou canceladas no período | Confira o período e o ambiente |
| `URL_INVALIDA` / `URL_NAO_PERMITIDA` | Webhook com URL inválida ou privada | Use HTTPS público |
| `LIMITE_API` / `LIMITE_EMPRESA` / `LIMITE_CONCORRENCIA` | Limites de taxa | Aguarde e reduza a frequência |
| `COMUNICACAO_PREFEITURA` | Falha ou timeout com o emissor | Estado incerto: consulte antes de reenviar |

---

## 15. Da homologação para a produção

1. Integre e teste tudo em `homologacao` (emissão, rejeição, correção, cancelamento, webhook, PDF).
2. No painel, em **produção**: envie o A1 (e o token, na EL), confira o enquadramento e peça ao responsável para liberar produção.
3. No servidor: `ALLOW_FISCAL_PRODUCTION=true`, `NODE_ENV=production`, `APP_ORIGIN` com HTTPS.
4. Crie uma **chave de produção** e um **webhook de produção** (HTTPS público). Chaves e webhooks não são compartilhados entre ambientes.
5. Troque `MAXTRA_AMBIENTE`, `MAXTRA_API_KEY` e `MAXTRA_WEBHOOK_SECRET` no sistema integrado.

## 16. Boas práticas de segurança

- Guarde chave de API e segredo do webhook em variáveis de ambiente ou cofre, nunca no front-end ou no repositório.
- Use uma chave por sistema integrado, com o menor perfil possível (`viewer` para relatórios).
- Revogue chaves sem uso; o painel mostra o último uso.
- Verifique a assinatura de todo webhook e deduplique por id do evento.
- Gere novo segredo de webhook se ele vazar (`POST /webhooks/{id}/segredo`); o anterior deixa de valer na hora.
