Pular para o conteúdo

Testar com segurança

A API Cotejar tem um único ambiente: https://api.cotejar.com.br. Não há sandbox nem base de testes com dados fictícios. Isso é intencional: uma regra regulatória testada contra dados inventados não prova nada sobre o seu catálogo.

O que existe é um protocolo em que nada muda sem confirmação explícita. As camadas abaixo vão de zero efeito a efeito controlado. Suba de uma para a próxima só quando a anterior passar.

Camada O que prova Altera o catálogo?
1. Leituras Chave, empresa e permissões Não
2. Erros provocados Tratamento de 401, 422, 428 e 412 Não
3. Ingestão sem aplicar Formato dos lotes, NCMs, recusas e idempotência Não
4. Webhooks Receptor, assinatura e deduplicação Não
5. CSV de exemplo Validação de tamanho, hash e estrutura Não
6. Primeira aplicação O fluxo completo Sim, de forma controlada

Leituras não têm efeito e mostram se a chave está na empresa certa.

GET/v1/contaconta:lerReferência
GET/v1/catalogoscatalogo:lerReferência
Terminal
curl -sS -H "Authorization: Bearer $COTEJAR_API_KEY" "$COTEJAR_API_BASE_URL/v1/conta"
curl -sS -H "Authorization: Bearer $COTEJAR_API_KEY" "$COTEJAR_API_BASE_URL/v1/catalogos"

Confira cnpj_raiz e escopos em /v1/conta, e anote o id do catálogo que vai receber os lotes.

Provoque cada recusa de propósito e confirme que o seu código registra o request_id e não trata a falha como sucesso. Nenhuma destas chamadas muda dados.

Terminal
curl -sS -H "Authorization: Bearer ctj_api_invalida" "$COTEJAR_API_BASE_URL/v1/conta"
{ "erro": { "codigo": "CREDENCIAL_INVALIDA", "mensagem": "credencial ausente, inválida, expirada ou revogada", "request_id": "b0b071f7-f965-443d-a0a4-4ad20a9219c2" } }

A ingestão tem três momentos: receber lotes, validar e aplicar. Só aplicar altera o catálogo. Até a validação, você exercita o formato real dos seus dados, com as NCMs reais, sem efeito nenhum sobre o que já está cadastrado.

  1. Inicie a ingestão no catálogo anotado na camada 1.

    POST/v1/catalogos/ingestoescatalogo:escreverReferência
    201 Created
    { "ingestao_id": "5306d957-04d7-44dd-9c63-e4d04d2e19fe", "estado": "recebendo_lotes", "repetida": false, "catalogo_id": "7c7b354f-07b6-4461-9240-263418c81cdb", "versao_catalogo": 1 }
  2. Envie um lote com produtos reais do ERP e repita o mesmo pedido com a mesma Idempotency-Key. O segundo envio prova que um timeout no seu lado não duplica o lote.

    PUT/v1/catalogos/ingestoes/{id}/lotes/{sequencia}catalogo:escreverReferência
    { "ingestao_id": "5306d957-04d7-44dd-9c63-e4d04d2e19fe", "sequencia": 1, "produtos_recebidos": 3, "repetido": false }
  3. Valide e leia a prévia. É aqui que aparecem NCM inexistente, linha sem NCM e outras recusas.

    POST/v1/catalogos/ingestoes/{id}/validarcatalogo:escreverReferência
    {
    "ingestao_id": "5306d957-04d7-44dd-9c63-e4d04d2e19fe",
    "estado": "pronta_para_aplicar",
    "hash_previa": "4bee6b75d2d85b7a1ed8f4dd9657f131c7d9b1dca79c0eac2c3c912091d131ae",
    "totais": { "lotes": 1, "produtos": 3, "incompletos": 0, "atributos": 0 },
    "guardas": []
    }
  4. Pare aqui. Não chame aplicar. A ingestão fica parada e o catálogo continua exatamente como estava.

Configurar, testar e reenviar webhooks exige plano pago ativo; sem ele, a API responde 409 ASSINATURA_INATIVA.

Antes de expor o endpoint, teste a verificação de assinatura com este vetor fixo. O seu código precisa aceitar exatamente estes valores e recusar qualquer byte diferente no corpo.

Entrada Valor
Segredo ctj_whsec_exemplo_de_teste_nao_use_em_producao_000000
Cotejar-Event-Id 3f2b8c1e-5d4a-4b7e-9c2f-1a6d8e0b4c71
Cotejar-Timestamp 1792000000
Cotejar-Signature v1=536f51b3d450c13da9a5c021f67bf7599fd4923d5fb32c910e01b728d7e5340a
Corpo (bytes exatos, sem espaços nem quebra de linha final)
{"id":"3f2b8c1e-5d4a-4b7e-9c2f-1a6d8e0b4c71","versao":1,"empresa":"12345678","ocorridoEm":"2026-10-14T12:26:40.000Z","dados":{"tipo":"webhook.teste","destinoId":"9b1c2d3e-4f50-4a61-8b72-93a4b5c6d7e8"}}

O instante do vetor é fixo. No teste, injete o relógio como 1792000030 (30 segundos depois) para passar na janela de cinco minutos. Os receptores de exemplo aceitam esse parâmetro.

Com o receptor publicado, use Enviar evento de teste em Integrações › Webhooks, ou chame a API com o ETag atual do destino:

POST/v1/webhook/testeswebhook:gerenciarReferência
Terminal
ETAG=$(curl -sS -D - -o /dev/null -H "Authorization: Bearer $COTEJAR_API_KEY" \
"$COTEJAR_API_BASE_URL/v1/webhook" | awk -F': ' 'tolower($1)=="etag" {print $2}' | tr -d '\r')
curl -sS -X POST "$COTEJAR_API_BASE_URL/v1/webhook/testes" \
-H "Authorization: Bearer $COTEJAR_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "If-Match: $ETAG" \
-H "Content-Type: application/json" \
-d '{}'
Cartão Destino do webhook: estado Ativo, assinatura Ativa, versão 1, destino https://example.com/cotejar/eventos, e os botões Atualizar destino e trocar segredo, Enviar evento de teste e Revogar destino.

O 202 só confirma que o teste entrou na fila. A prova de entrega é o histórico: o evento webhook.teste precisa passar de Pendente para Entregue, com o status HTTP que o seu receptor devolveu.

Histórico de eventos com um Teste de webhook pendente: ocorrido em 13/10/2026 13:24, zero tentativas, sem resposta HTTP ainda e o botão Ver tentativas.

Pela API, acompanhe em GET /v1/webhook/eventos e veja cada tentativa em GET /v1/webhook/eventos/{id}/tentativas. Testes e reenvios somados têm teto de 5 por minuto.

O CSV da análise só deve ser aceito depois de conferir tamanho, SHA-256, cabeçalho, quantidade de achados e a linha final conclusao. Para testar o seu validador sem rodar uma análise, use este arquivo, gerado pelo mesmo formatador da API:

Cabeçalho Valor esperado
Arquivo analise-exemplo.csv
X-Cotejar-Tamanho-Bytes 982
Content-Digest sha-256=:Ou+bAOwk3pO1PDkLEAYp4zBdEkqqgMAl/hq3npIjijY=:

O arquivo tem dois achados do tipo orfao. Um deles tem o código '=000124: o apóstrofo é a proteção contra fórmula em planilha e precisa ser preservado. Veja o validador pronto.

Quando as camadas anteriores passarem, aplique um lote pequeno com operacao: "mesclar". A mescla adiciona ou atualiza os produtos enviados e preserva todos os ausentes. Comece por produtos que já existem no catálogo, com os mesmos dados do ERP: a aplicação exercita o fluxo inteiro sem mudar o conteúdo.

Evite substituicao_catalogo até a integração estar estável: ela retira do catálogo escolhido os vínculos que não vierem nos lotes.

  • A chave de produção vem do gerenciador de segredos e nunca aparece em log.
  • Toda escrita gera e persiste a Idempotency-Key antes do primeiro envio e a reutiliza nas repetições.
  • 202 é acompanhado pelo Location até um estado final; timeout não vira sucesso.
  • 412 leva a uma nova leitura, nunca a uma repetição cega.
  • 429 e 503 respeitam Retry-After.
  • Lotes com recusa param antes de aplicar, e alguém é avisado.
  • O receptor de webhook confere a assinatura com o corpo bruto, recusa instantes fora da janela de cinco minutos e deduplica pelo Cotejar-Event-Id.
  • O CSV só é importado depois de conferir tamanho, hash, cabeçalho, contagem e conclusão.
  • Existe um lembrete para trocar a credencial antes de expira_em.
  • A credencial de homologação foi revogada.