{"openapi":"3.1.0","info":{"title":"NotificAI API","version":"1.0.0","description":"API pública da NotificAI. Autenticação por chave de API (Bearer ntf_live_… / ntf_test_…), gerenciada em Desenvolvedores › Chaves de API."},"servers":[{"url":"/api/v1"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"ntf_{live|test}_..."}},"schemas":{"Envio":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tipo":{"type":"string","enum":["telegrama","ecarta","carta_registrada"]},"status":{"type":"string"},"destinatario":{"type":"object"},"preco_cliente_cents":{"type":"integer"},"correios_id":{"type":"string","nullable":true}}},"Erro":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}},"security":[{"bearerAuth":[]}],"paths":{"/envios":{"post":{"summary":"Cria um envio unitário","description":"Por padrão já debita a carteira e entra na fila de postagem (`confirmar` é `true` por padrão). Envie `confirmar: false` para só deixar em rascunho — nesse caso, confirme depois com `POST /envios/{id}/confirmar`. `simular: true` só calcula o preço, sem criar nada. Como o default DEBITA, esta chamada exige chave de ambiente de PRODUÇÃO (`ntf_live_…`): com chave de teste (`ntf_test_…`) o corpo com `confirmar` (explícito ou por default) recebe 403 `ambiente_teste`; `confirmar: false` e `simular: true` continuam liberados. O destinatário segue o contrato da API 426 dos Correios: `endereco.logradouro`, `endereco.numero`, `endereco.bairro`, `endereco.cidade` e `endereco.uf` são obrigatórios, cada campo tem limite de tamanho, a UF precisa existir e o `documento`, quando enviado, precisa ter dígito verificador válido. `opcoes.pre_datado` (aaaa-mm-dd) só é aceito a partir de amanhã, e o texto não pode passar de 10 páginas.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tipo","destinatario"],"properties":{"tipo":{"type":"string","enum":["telegrama","ecarta","carta_registrada"]},"destinatario":{"type":"object","description":"`{ nome, documento?, endereco: { logradouro, numero, complemento?, bairro, cidade, uf, cep } }` — validado com as regras da API 426 (422 `validacao` com `issues` apontando o campo)"},"corpo":{"type":"string","description":"Obrigatório se `template_id` não for enviado. Máximo de 10 páginas depois de renderizado"},"template_id":{"type":"string","format":"uuid"},"variaveis":{"type":"object"},"opcoes":{"type":"object","description":"pc, cc, ar, registrada, forma_envio_prova, email_prova e pre_datado (aaaa-mm-dd, a partir de amanhã)"},"etiqueta":{"type":"string"},"simular":{"type":"boolean"},"confirmar":{"type":"boolean","default":true,"description":"Debita a carteira e enfileira a postagem. Default true."}}}}}},"responses":{"200":{"description":"Simulação de preço, ou envio já existente (Idempotency-Key repetida)"},"201":{"description":"Envio criado (e confirmado, salvo `confirmar: false`)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tipo":{"type":"string","enum":["telegrama","ecarta","carta_registrada"]},"status":{"type":"string"},"destinatario":{"type":"object"},"preco_cliente_cents":{"type":"integer"},"correios_id":{"type":"string","nullable":true}}}}}}}},"401":{"description":"Chave ausente ou inválida"},"402":{"description":"Saldo insuficiente para confirmar — o envio fica em rascunho; recarregue e chame `POST /envios/{id}/confirmar`","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"403":{"description":"Escopo insuficiente, ou chave de ambiente de teste (`ntf_test_…`) tentando criar com `confirmar` (que debita)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"422":{"description":"Dados inválidos","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"429":{"description":"Limite de requisições excedido"}}},"get":{"summary":"Lista envios","parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"lote","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"cursor","in":"query","schema":{"type":"string"},"description":"created_at (ISO) do último item da página anterior"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"Lista paginada","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tipo":{"type":"string","enum":["telegrama","ecarta","carta_registrada"]},"status":{"type":"string"},"destinatario":{"type":"object"},"preco_cliente_cents":{"type":"integer"},"correios_id":{"type":"string","nullable":true}}}},"meta":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true}}}}}}}}}}},"/envios/{id}":{"get":{"summary":"Detalhe de um envio (com eventos e comprovantes)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}},"/envios/{id}/confirmar":{"post":{"summary":"Debita e enfileira um envio criado com `confirmar: false` (ou que ficou em 402 por saldo)","description":"Idempotente: chamar de novo depois que o envio já saiu de rascunho só devolve o estado atual. Exige chave de ambiente de PRODUÇÃO (`ntf_live_…`) — chave de teste (`ntf_test_…`) recebe 403 `ambiente_teste`, pois esta chamada debita dinheiro real.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Confirmado (ou já estava)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"tipo":{"type":"string","enum":["telegrama","ecarta","carta_registrada"]},"status":{"type":"string"},"destinatario":{"type":"object"},"preco_cliente_cents":{"type":"integer"},"correios_id":{"type":"string","nullable":true}}}}}}}},"402":{"description":"Saldo insuficiente","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"403":{"description":"Chave de ambiente de teste (`ntf_test_…`) não pode confirmar/debitar","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"404":{"description":"Envio não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}},"/lotes":{"post":{"summary":"Cria um lote de envios","description":"Cria em rascunho — nada é debitado aqui. Confirme com `POST /lotes/{id}/confirmar`. Cada `envios[].destinatario` segue o mesmo contrato da API 426 de `POST /envios` (logradouro, número, bairro, cidade e UF obrigatórios, limites de tamanho, UF válida, documento com dígito verificador) e nenhuma linha pode passar de 10 páginas; `opcoes.pre_datado` (aaaa-mm-dd) só a partir de amanhã. Erros de entrada voltam como 422 `validacao` com `issues` apontando `envios.<índice>.<campo>`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["nome","tipo","envios"],"properties":{"nome":{"type":"string"},"tipo":{"type":"string","enum":["telegrama","ecarta","carta_registrada"]},"opcoes":{"type":"object"},"etiqueta":{"type":"string"},"envios":{"type":"array","items":{"type":"object","required":["destinatario"],"properties":{"destinatario":{"type":"object","description":"`{ nome, documento?, endereco: { logradouro, numero, complemento?, bairro, cidade, uf, cep } }` — regras da API 426"},"corpo":{"type":"string"},"template_id":{"type":"string"},"variaveis":{"type":"object"}}}}}}}}},"responses":{"201":{"description":"Lote criado"},"422":{"description":"Dados inválidos"}}}},"/lotes/{id}/confirmar":{"post":{"summary":"Enfileira a confirmação do lote (job em background)","description":"Não debita nada nesta chamada — enfileira o job `confirmar_lote`, que processa em páginas de 100, debitando cada envio e marcando os sem saldo como `aguardando_pagamento`. Acompanhe o progresso com `GET /lotes/{id}` (a implementar) ou pelo painel. Exige chave de ambiente de PRODUÇÃO (`ntf_live_…`) — chave de teste (`ntf_test_…`) recebe 403 `ambiente_teste`, pois o job enfileirado aqui debita dinheiro real.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Confirmação enfileirada"},"403":{"description":"Lote aguardando aprovação de um admin, ou chave de ambiente de teste tentando confirmar","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}},"404":{"description":"Lote não encontrado","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}}}}}}}}},"/saldo":{"get":{"summary":"Saldo da carteira","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"saldoCents":{"type":"integer"}}}}}}}}}}},"/comprovantes/{id}/download":{"get":{"summary":"URL assinada (5 min) para baixar um comprovante","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Não encontrado"},"422":{"description":"Comprovante sem arquivo"}}}}}}