> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pombo.digital/llms.txt
> Use this file to discover all available pages before exploring further.

# Cria uma notificação extrajudicial.

> Emite uma notificação extrajudicial certificada para o destinatário informado.

**Um modelo é obrigatório.** Não existe envio de texto livre: o conteúdo vem de `template_id` preenchido por `parameters`. O campo `message` da resposta é o texto já renderizado.

**Chame `GET /templates/{id}` antes.** É de lá que saem as chaves de `parameters`.

**É assíncrona:** a resposta confirma a criação, não a entrega. Acompanhe com `GET /notifications/{id}`.

**Consome um envio do saldo da organização.** Uma notificação emitida não pode ser cancelada. Envie um `Idempotency-Key` para que uma repetição não notifique a mesma pessoa duas vezes.

**Com anexo, tudo vai numa chamada só.** Sem anexo, `application/json` como sempre; com anexo, `multipart/form-data` com as partes `notification` e `file`. Não existe endpoint separado para subir o arquivo.

**`channel` aceita `WHATSAPP` ou `EMAIL`.** Qualquer outro valor é recusado com `400 Invalid request: channel must be WHATSAPP or EMAIL`.

**Um corpo JSON mal formado é recusado com `400`**, não com `500`: `{"code": 10400, "http_code": 400, "message": "Invalid request: malformed JSON"}`. Uma vírgula sobrando não custa envio nenhum.




## OpenAPI

````yaml /api-reference/openapi.yaml post /notifications
openapi: 3.1.0
info:
  title: API do Pombo.Digital
  version: 1.0.0
  description: >
    Envie notificações extrajudiciais certificadas por WhatsApp ou e-mail,
    acompanhe a entrega e baixe o laudo com carimbo do tempo ICP-Brasil.


    A credencial identifica a organização por si só: `organization_id` não é
    aceito em nenhuma requisição desta API.
servers:
  - url: https://pombo.digital/api/integrations/v1
    description: Produção
security:
  - bearerAuth: []
tags:
  - name: Notificações
    description: >-
      Criar uma notificação extrajudicial, acompanhar a entrega e baixar o
      laudo.
  - name: Modelos
    description: >-
      Modelos de mensagem. Um envio de WhatsApp exige um modelo aprovado pela
      Meta.
paths:
  /notifications:
    post:
      tags:
        - Notificações
      summary: Cria uma notificação extrajudicial.
      description: >
        Emite uma notificação extrajudicial certificada para o destinatário
        informado.


        **Um modelo é obrigatório.** Não existe envio de texto livre: o conteúdo
        vem de `template_id` preenchido por `parameters`. O campo `message` da
        resposta é o texto já renderizado.


        **Chame `GET /templates/{id}` antes.** É de lá que saem as chaves de
        `parameters`.


        **É assíncrona:** a resposta confirma a criação, não a entrega.
        Acompanhe com `GET /notifications/{id}`.


        **Consome um envio do saldo da organização.** Uma notificação emitida
        não pode ser cancelada. Envie um `Idempotency-Key` para que uma
        repetição não notifique a mesma pessoa duas vezes.


        **Com anexo, tudo vai numa chamada só.** Sem anexo, `application/json`
        como sempre; com anexo, `multipart/form-data` com as partes
        `notification` e `file`. Não existe endpoint separado para subir o
        arquivo.


        **`channel` aceita `WHATSAPP` ou `EMAIL`.** Qualquer outro valor é
        recusado com `400 Invalid request: channel must be WHATSAPP or EMAIL`.


        **Um corpo JSON mal formado é recusado com `400`**, não com `500`:
        `{"code": 10400, "http_code": 400, "message": "Invalid request:
        malformed JSON"}`. Uma vírgula sobrando não custa envio nenhum.
      operationId: createNotification
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateNotificationRequest'
            examples:
              whatsapp:
                summary: WhatsApp
                value:
                  template_id: PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                  channel: WHATSAPP
                  recipient_full_name: Maria Silva
                  recipient_tax_id: '12345678901'
                  recipient_tax_id_type: CPF
                  recipient_phone: '+5511999998888'
                  sender:
                    name: Black101 Cobranças LTDA
                    tax_id: '12345678000199'
                    tax_id_type: CNPJ
                  parameters:
                    '{{1}}': Maria
                    '{{2}}': R$ 1.200,00
                    '{{3}}': parcela em atraso
                    '{{4}}': '5'
                    '{{5}}': Black101 Cobranças LTDA
                    '{{6}}': 12.345.678/0001-99
                    '{{7}}': contato@black101.example.com
              email:
                summary: E-mail
                value:
                  template_id: PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                  channel: EMAIL
                  recipient_full_name: João Souza
                  recipient_tax_id: '98765432100'
                  recipient_tax_id_type: CPF
                  recipient_email: joao.souza@example.com
                  sender:
                    name: Sabiá Recuperação de Crédito ME
                    tax_id: '98765432000188'
                    tax_id_type: CNPJ
                  parameters:
                    '{{1}}': João
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/CreateNotificationMultipartRequest'
            encoding:
              notification:
                contentType: application/json
              file:
                contentType: application/pdf, image/png, image/jpeg
            examples:
              whatsapp_anexo:
                summary: WhatsApp com anexo (modelo DOCUMENT)
                description: >
                  A parte `notification` carrega o objeto abaixo; a parte `file`
                  carrega o PDF.
                value:
                  notification:
                    template_id: PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    channel: WHATSAPP
                    recipient_full_name: Maria Silva
                    recipient_tax_id: '12345678901'
                    recipient_tax_id_type: CPF
                    recipient_phone: '+5511999998888'
                    sender:
                      name: Black101 Cobranças LTDA
                      tax_id: '12345678000199'
                      tax_id_type: CNPJ
                    parameters:
                      '{{1}}': Maria
                  file: <conteúdo binário de contrato.pdf>
              email_anexo:
                summary: E-mail com anexo
                value:
                  notification:
                    template_id: PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    channel: EMAIL
                    recipient_full_name: João Souza
                    recipient_tax_id: '98765432100'
                    recipient_tax_id_type: CPF
                    recipient_email: joao.souza@example.com
                    sender:
                      name: Sabiá Recuperação de Crédito ME
                      tax_id: '98765432000188'
                      tax_id_type: CNPJ
                    parameters:
                      '{{1}}': João
                  file: <conteúdo binário de contrato.pdf>
      responses:
        '200':
          description: >
            **Repetição idempotente.** O mesmo `Idempotency-Key` com o mesmo
            corpo (**e, quando há anexo, o mesmo arquivo**) devolve `200` com o
            corpo original da primeira chamada. Nenhuma notificação nova foi
            criada e nenhum envio foi descontado.


            O `200` é o sinal de repetição: a primeira chamada devolve `201`.
          headers:
            Idempotent-Replayed:
              description: Presente apenas na repetição, sempre `true`.
              schema:
                type: string
                examples:
                  - 'true'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateNotificationResponse'
        '201':
          description: Notificação criada e aceita para envio.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateNotificationResponse'
              examples:
                enfileirada:
                  summary: Criada, aguardando envio
                  value:
                    notification_id: PBD_NOT_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    organization_id: PBD_ORG_01J8A1B2C3D4E5F6G7H8J9K0LM
                    template_id: PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    channel: EMAIL
                    recipient_full_name: João Souza
                    recipient_tax_id: '98765432100'
                    recipient_tax_id_type: CPF
                    recipient_phone: null
                    recipient_email: joao.souza@example.com
                    contact_id: PBD_CNT_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    line_id: null
                    message: Prezado(a) João, consta débito em aberto.
                    message_format: HTML
                    notification_status: PENDING
                    notification_delivery_status: ENQUEUED
                    sender:
                      name: Sabiá Recuperação de Crédito ME
                      tax_id: '98765432000188'
                      tax_id_type: CNPJ
                      contact_phone: null
                      contact_email: null
                    sender_phone: null
                    sender_email: ola@pombo.digital
                    created_by_user_id: PBD_APIKEY_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    idempotency_key: cobranca-2026-08-31-0001
                    created_at: '2026-08-27T14:03:11.482Z'
                    attachments: []
                com_anexo:
                  summary: Criada, com o anexo já verificado
                  description: >
                    O arquivo viajou na mesma chamada, então o `hash` já vem
                    aqui: a verificação não precisa de uma segunda requisição.
                  value:
                    notification_id: PBD_NOT_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    organization_id: PBD_ORG_01J8A1B2C3D4E5F6G7H8J9K0LM
                    template_id: PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                    channel: WHATSAPP
                    recipient_full_name: Maria Silva
                    recipient_tax_id: '12345678901'
                    recipient_tax_id_type: CPF
                    recipient_phone: '+5511999998888'
                    recipient_email: null
                    message: Prezada Maria, consta débito de R$ 1.200,00.
                    message_format: TEXT
                    notification_status: PENDING
                    notification_delivery_status: ENQUEUED
                    idempotency_key: cobranca-2026-08-31-0003
                    created_at: '2026-08-27T14:03:11.482Z'
                    attachments:
                      - attachment_id: PBD_ATCH_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                        filename: contrato.pdf
                        mime_type: application/pdf
                        size_bytes: 184320
                        hash: >-
                          9f2c1d7a4b6e8f0c2a4d6e8f0b1c3d5e7f9a1b3c5d7e9f1a3b5c7d9e1f3a5b7c
                        hash_algorithm: SHA-256
                        hash_encoding: hex
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '422':
          $ref: '#/components/responses/BusinessRuleRejected'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
      x-codeSamples:
        - lang: python
          label: Sem anexo
          source: |
            import requests

            URL = "https://pombo.digital/api/integrations/v1/notifications"
            headers = {
                "Authorization": "Bearer sk_live_...",
                "Idempotency-Key": "cobranca-2026-08-31-0001",
            }

            notification = {
                "template_id": "PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N",
                "channel": "WHATSAPP",
                "recipient_full_name": "Maria Silva",
                "recipient_tax_id": "12345678901",
                "recipient_tax_id_type": "CPF",
                "recipient_phone": "+5511999998888",
                "parameters": {"{{1}}": "Maria"},
            }

            requests.post(URL, headers=headers, json=notification)
        - lang: python
          label: Com anexo
          source: >
            import json, requests


            URL = "https://pombo.digital/api/integrations/v1/notifications"

            headers = {
                "Authorization": "Bearer sk_live_...",
                "Idempotency-Key": "cobranca-2026-08-31-0001",
            }


            notification = {
                "template_id": "PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N",
                "channel": "WHATSAPP",
                "recipient_full_name": "Maria Silva",
                "recipient_tax_id": "12345678901",
                "recipient_tax_id_type": "CPF",
                "recipient_phone": "+5511999998888",
                "parameters": {"{{1}}": "Maria"},
            }


            # O mesmo objeto de sempre, agora como uma parte, com o arquivo ao
            lado.

            # Nao defina Content-Type: a biblioteca monta o boundary.

            with open("contrato.pdf", "rb") as pdf:
                requests.post(
                    URL,
                    headers=headers,
                    files={
                        "notification": (None, json.dumps(notification), "application/json"),
                        "file": ("contrato.pdf", pdf, "application/pdf"),
                    },
                )
        - lang: curl
          label: Sem anexo
          source: >
            curl -X POST https://pombo.digital/api/integrations/v1/notifications
            \
              -H "Authorization: Bearer sk_live_..." \
              -H "Idempotency-Key: cobranca-2026-08-31-0001" \
              -H "Content-Type: application/json" \
              -d '{"template_id":"PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N","channel":"WHATSAPP","recipient_full_name":"Maria Silva","recipient_tax_id":"12345678901","recipient_tax_id_type":"CPF","recipient_phone":"+5511999998888","parameters":{"{{1}}":"Maria"}}'
        - lang: curl
          label: Com anexo
          source: >
            # Nao passe -H "Content-Type": o -F monta o boundary.

            curl -X POST https://pombo.digital/api/integrations/v1/notifications
            \
              -H "Authorization: Bearer sk_live_..." \
              -H "Idempotency-Key: cobranca-2026-08-31-0001" \
              -F 'notification={"template_id":"PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N","channel":"WHATSAPP","recipient_full_name":"Maria Silva","recipient_tax_id":"12345678901","recipient_tax_id_type":"CPF","recipient_phone":"+5511999998888","parameters":{"{{1}}":"Maria"}};type=application/json' \
              -F 'file=@contrato.pdf;type=application/pdf'
        - lang: javascript
          label: Sem anexo
          source: >
            const URL =
            "https://pombo.digital/api/integrations/v1/notifications";

            const headers = {
              Authorization: "Bearer sk_live_...",
              "Idempotency-Key": "cobranca-2026-08-31-0001",
            };


            const notification = {
              template_id: "PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N",
              channel: "WHATSAPP",
              recipient_full_name: "Maria Silva",
              recipient_tax_id: "12345678901",
              recipient_tax_id_type: "CPF",
              recipient_phone: "+5511999998888",
              parameters: { "{{1}}": "Maria" },
            };


            await fetch(URL, {
              method: "POST",
              headers: { ...headers, "Content-Type": "application/json" },
              body: JSON.stringify(notification),
            });
        - lang: javascript
          label: Com anexo
          source: >
            const URL =
            "https://pombo.digital/api/integrations/v1/notifications";

            const headers = {
              Authorization: "Bearer sk_live_...",
              "Idempotency-Key": "cobranca-2026-08-31-0001",
            };


            const notification = {
              template_id: "PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N",
              channel: "WHATSAPP",
              recipient_full_name: "Maria Silva",
              recipient_tax_id: "12345678901",
              recipient_tax_id_type: "CPF",
              recipient_phone: "+5511999998888",
              parameters: { "{{1}}": "Maria" },
            };


            // O mesmo objeto de sempre, agora como uma parte, com o arquivo ao
            lado.

            // Nao defina Content-Type: o FormData monta o boundary.

            const form = new FormData();

            form.append(
              "notification",
              new Blob([JSON.stringify(notification)], { type: "application/json" })
            );

            form.append("file", pdfFile, "contrato.pdf");


            await fetch(URL, { method: "POST", headers, body: form });
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: >
        Chave de idempotência do envio. Letras, dígitos, sublinhados e hifens.


        Com a **mesma chave**:


        - **tudo igual**, mesmo corpo e, se houver, mesmo arquivo: `200` com o
        corpo original e o cabeçalho `Idempotent-Replayed: true`

        - **qualquer diferença**, no corpo ou no arquivo: `409
        IDEMPOTENCY_KEY_REUSED`


        **O arquivo faz parte da chave**, para que dois documentos diferentes
        nunca sejam confundidos com a mesma notificação. Na prática: para
        repetir um envio com anexo, reenvie os mesmos bytes.


        **A chave nunca é liberada.** Para repetir um envio que falhou, use uma
        chave nova.


        Até 255 caracteres. Uma chave mal formada é recusada com `400`, sem
        `error_code`, mas não antes de tudo: o formato do corpo, o `channel`,
        `file_transfer_id`, `attachment_id` e `organization_id` são conferidos
        primeiro, e num `multipart/form-data` as partes já foram lidas antes
        disso.
      schema:
        type: string
        pattern: ^[A-Za-z0-9_-]+$
        examples:
          - cobranca-2026-08-31-0001
  schemas:
    CreateNotificationRequest:
      type: object
      required:
        - template_id
        - channel
        - recipient_full_name
        - recipient_tax_id
        - recipient_tax_id_type
        - parameters
        - sender
      properties:
        template_id:
          type: string
          description: O modelo a usar. Em WhatsApp precisa estar `APPROVED`.
          examples:
            - PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N
        channel:
          type: string
          enum:
            - WHATSAPP
            - EMAIL
          description: Em maiúsculas.
        recipient_full_name:
          type: string
          minLength: 1
        recipient_tax_id:
          type: string
          pattern: ^([0-9]{11}|[0-9]{14})$
          description: CPF (11 dígitos) ou CNPJ (14 dígitos), só dígitos.
        recipient_tax_id_type:
          type: string
          enum:
            - CPF
            - CNPJ
          description: Um valor fora de `CPF` e `CNPJ` é recusado com `400`.
        recipient_phone:
          type: string
          pattern: ^\+[0-9]{10,15}$
          description: >
            Formato internacional, começando por `+`. Ausente num envio de
            WhatsApp, a chamada é recusada com `400` e `error_code` igual a
            `INVALID_RECIPIENT`.
          examples:
            - '+5511999998888'
        recipient_email:
          type: string
          format: email
          description: >
            Ausente num envio de e-mail, a chamada é recusada com `400` e
            `error_code` igual a `INVALID_RECIPIENT`.
        parameters:
          type: object
          additionalProperties: true
          description: >
            As variáveis do modelo. **As chaves são o marcador literal**, entre
            chaves duplas, como `"{{1}}"`. Não é o índice `1` e não é o nome da
            variável.


            As chaves vêm de `variables` em `GET /templates/{id}`. Envie `{}` se
            o modelo não tiver variáveis. Uma chave faltando é recusada com `400
            INVALID_PARAMETERS: Missing required parameter: {{1}}`.
          examples:
            - '{{1}}': Maria
        sender:
          $ref: '#/components/schemas/Sender'
    CreateNotificationMultipartRequest:
      type: object
      required:
        - notification
        - file
      properties:
        notification:
          $ref: '#/components/schemas/CreateNotificationRequest'
          description: >
            O mesmo objeto aceito em `application/json`, enviado como uma parte
            de `Content-Type: application/json`. **Envie esta parte primeiro:**
            ela é validada enquanto o arquivo ainda está subindo.
        file:
          type: string
          format: binary
          description: >
            O documento. `application/pdf`, `image/png` ou `image/jpeg`. Até 20
            MB para PDF e 5 MB para imagem.


            **Um anexo por notificação.** A parte é singular: para mandar vários
            documentos, junte-os em um único PDF antes de enviar.


            **Em `WHATSAPP` o par com o modelo é obrigatório nos dois
            sentidos:** um modelo `DOCUMENT` ou `IMAGE` exige esta parte, e um
            modelo `TEXT` a recusa, com `400` nos dois desencontros. O tipo
            também precisa casar: um modelo `DOCUMENT` recusa uma imagem e um
            modelo `IMAGE` recusa um PDF, igualmente com `400`.


            Essa conferência depende de o modelo já ter sido aprovado pela Meta:
            um modelo de WhatsApp que ainda não recebeu o identificador da Meta
            passa direto por ela. Na prática isso não muda nada, porque um envio
            de WhatsApp exige `template_status: APPROVED` de qualquer forma.


            **Em `EMAIL` é sempre opcional, e aceito com qualquer
            `template_type`.** O envio por e-mail não consulta o tipo do modelo,
            então um modelo `TEXT` com anexo simplesmente leva o anexo.


            **Duas partes, e só essas duas.** Uma terceira parte qualquer
            (`encoding`, `filename`, `mime_type` ou qualquer outro nome) é
            recusada com `400 Invalid request: multipart/form-data accepts
            exactly one notification part and one file part`. O nome do arquivo
            e o tipo saem do próprio cabeçalho da parte `file`, e não há corpo
            em base64.
    CreateNotificationResponse:
      allOf:
        - $ref: '#/components/schemas/Notification'
        - type: object
          description: A notificação devolvida pelo envio.
          properties:
            attachments:
              type: array
              description: >
                O anexo que viajou nesta chamada, já validado, guardado e com a
                sua impressão digital. Vazio quando o envio não levou arquivo.


                **Confira aqui, sem uma segunda chamada.** Como o arquivo entra
                na mesma requisição, o `hash` já existe quando o envio responde:
                compare-o com o resumo do arquivo que você tinha e a verificação
                termina aqui.
              items:
                $ref: '#/components/schemas/NotificationAttachment'
    Sender:
      type: object
      description: >
        Quem aparece como remetente da notificação. Permite enviar em nome
        próprio ou em nome de um cliente. Em ambos os casos o Pombo mantém o
        registro de qual credencial emitiu.


        **Não existe remetente padrão da organização nesta API:** o `sender` é
        declarado em cada envio.
      required:
        - name
        - tax_id
        - tax_id_type
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 200
          description: >
            Razão social ou nome do remetente. No máximo 200 caracteres: acima
            disso a chamada é recusada com `400 INVALID_SENDER` e a mensagem
            `sender.name must be at most 200 characters`.


            **Só texto simples.** Os caracteres `<`, `>` e `"` e os caracteres
            de controle (quebras de linha incluídas) são recusados com `400
            INVALID_SENDER` e a mensagem `sender.name must be plain text`. O
            nome é reproduzido literalmente no rodapé do e-mail e no laudo, e
            uma quebra de linha forjaria uma segunda linha de rodapé.
        tax_id:
          type: string
          pattern: ^([0-9]{11}|[0-9]{14})$
          description: >
            CPF (11 dígitos) ou CNPJ (14 dígitos), só dígitos.


            **A quantidade de dígitos não basta:** o documento é conferido pelo
            dígito verificador (MOD-11) e precisa corresponder ao `tax_id_type`
            declarado: um CNPJ declarado como `CPF` é recusado. A recusa é `400
            INVALID_SENDER`, com a mensagem `sender.tax_id must be a valid CPF`
            ou `sender.tax_id must be a valid CNPJ`.
        tax_id_type:
          type: string
          enum:
            - CPF
            - CNPJ
        contact_phone:
          type:
            - string
            - 'null'
          pattern: ^\+[0-9]{10,15}$
          description: >
            Opcional. O telefone para onde o destinatário deve responder,
            impresso no rodapé da mensagem. Não é o número de origem: esse é
            `sender_phone`, e o Pombo é quem o resolve.


            Formato internacional, começando por `+`, com 10 a 15 dígitos. Fora
            desse formato a chamada é recusada com `400 INVALID_SENDER` e a
            mensagem `sender.contact_phone must be in format +CODE_PHONE with
            10-15 digits (e.g., +5511999998888)`.


            Ausente, a linha de contato do rodapé **é omitida por inteiro**: não
            há queda para o telefone da organização.
          examples:
            - '+5511999998888'
        contact_email:
          type:
            - string
            - 'null'
          format: email
          description: Opcional. Devolvido na resposta da notificação.
    Notification:
      type: object
      description: >
        Os campos comuns a uma notificação. O envio e a consulta devolvem esta
        base com acréscimos: os dois acrescentam `attachments`, porque o arquivo
        viaja no próprio envio, e a consulta acrescenta `events`, que só existem
        depois.
      properties:
        notification_id:
          type: string
        organization_id:
          type: string
          description: >-
            A organização dona da conta e do saldo. Não confunda com `sender`,
            que é em nome de quem a notificação sai.
        template_id:
          type: string
        channel:
          type: string
          enum:
            - WHATSAPP
            - EMAIL
        recipient_full_name:
          type: string
        recipient_tax_id:
          type: string
        recipient_tax_id_type:
          type: string
          enum:
            - CPF
            - CNPJ
        recipient_phone:
          type:
            - string
            - 'null'
        recipient_email:
          type:
            - string
            - 'null'
        contact_id:
          type: string
          description: >
            O contato do destinatário no Pombo. Os contatos são unificados pelo
            `tax_id`, então o mesmo CPF devolve o mesmo `contact_id` nos dois
            canais. É por isso que dados divergentes produzem `409
            CONTACT_CONFLICT`.
          examples:
            - PBD_CNT_01J9Z2Q8XK3M7WPTV6RB4CYH0N
        line_id:
          type:
            - string
            - 'null'
          description: A linha de WhatsApp usada no envio. `null` em `EMAIL`.
          examples:
            - PBD_WALN_01J9Z2Q8XK3M7WPTV6RB4CYH0N
        parameters:
          type: object
          additionalProperties: true
          description: >
            As variáveis exatamente como você as enviou, devolvidas para que o
            registro se descreva sozinho.
          examples:
            - '{{1}}': Maria
        message:
          type: string
          description: >
            A mensagem já renderizada, com `parameters` aplicados ao modelo. É o
            artefato que o carimbo do tempo cobre. Existe só na resposta: não há
            campo `message` de envio.
        content_sha256:
          type:
            - string
            - 'null'
          description: >
            O resumo `SHA-256` da mensagem renderizada, em hexadecimal. É a
            impressão digital do texto que consta no laudo: o equivalente, para
            a mensagem, do que `hash` é para o anexo.
          examples:
            - 3b1c8d6e4f2a0b9c7d5e3f1a8c6d2b0e9f7a5c3d1e8f6b4a2c0d9e7f5a3b1c8d
        message_format:
          type: string
          description: Se `message` é HTML ou texto simples.
        notification_status:
          type: string
          enum:
            - DRAFT
            - PENDING
            - PROCESSING
            - COMPLETED
            - FAILED
          description: >
            O processamento interno no Pombo. **Não indica entrega.** Uma
            mensagem já lida foi observada aqui como `PENDING`. Para saber se a
            pessoa recebeu, use `notification_delivery_status`.
        notification_delivery_status:
          $ref: '#/components/schemas/NotificationDeliveryStatus'
        sender:
          $ref: '#/components/schemas/Sender'
        sender_phone:
          type:
            - string
            - 'null'
          description: >
            O número de origem do WhatsApp, resolvido pelo Pombo a partir do
            pool de linhas. `null` em `EMAIL`. É o endereço de onde a mensagem
            realmente saiu, e é o que vai certificado no laudo. Não é
            `sender.contact_phone`, que é para onde o destinatário responde.
          examples:
            - '5511952134898'
        sender_email:
          type:
            - string
            - 'null'
          description: >
            O endereço de origem do e-mail, resolvido pelo Pombo. `null` em
            `WHATSAPP`. É o endereço de onde a mensagem realmente saiu, e é o
            que vai certificado no laudo. Não é `sender.contact_email`, que é
            para onde o destinatário responde.


            Se a sua organização não tem endereço próprio verificado (o caso
            comum), a mensagem sai do endereço compartilhado do Pombo, e é ele
            que aparece aqui.
          examples:
            - ola@pombo.digital
        created_by_type:
          type: string
          description: O tipo de autor do envio, que qualifica `created_by_user_id`.
        created_by_user_id:
          type: string
          description: >
            **O nome engana.** Quando o envio parte de uma credencial de API,
            este campo traz o identificador da credencial, `PBD_APIKEY_...`, e
            não um identificador de usuário.
          examples:
            - PBD_APIKEY_01J9Z2Q8XK3M7WPTV6RB4CYH0N
        idempotency_key:
          type:
            - string
            - 'null'
          description: >
            A chave que você enviou no cabeçalho `Idempotency-Key`, devolvida
            aqui para que o registro se descreva sozinho. **Só existe na
            resposta:** no envio a chave vai no cabeçalho, nunca no corpo.
        batch_id:
          type:
            - string
            - 'null'
          description: >-
            O lote a que a notificação pertence, quando veio de um envio em
            massa.
        batch_item_id:
          type:
            - string
            - 'null'
          description: O item do lote correspondente a esta notificação.
        created_at:
          type: string
          format: date-time
        sent_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Quando o Pombo entregou a mensagem ao provedor. `null` até o envio
            ocorrer.
        delivered_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            Quando o provedor confirmou a entrega ao destinatário. `null`
            enquanto não houver confirmação.
        failed_at:
          type:
            - string
            - 'null'
          format: date-time
          description: Quando o envio falhou em definitivo. `null` quando não houve falha.
        created_by_email:
          type:
            - string
            - 'null'
          description: Sempre `null` quando a chamada vem de uma credencial de API.
        created_by_name:
          type:
            - string
            - 'null'
          description: O nome da credencial que emitiu a notificação.
    NotificationAttachment:
      type: object
      description: >
        Um anexo da notificação, com a impressão digital do arquivo.


        **É aqui que você confere o que foi notificado.** O `hash` é a impressão
        digital registrada no laudo: calcule o mesmo resumo do arquivo que você
        enviou e compare, e você prova que o documento anexado é o documento que
        você tinha.
      properties:
        attachment_id:
          type: string
          description: >
            O identificador do arquivo guardado, **opaco**: é o mesmo que
            aparece no laudo, e serve para citar um anexo específico num chamado
            de suporte. Não há endpoint para resolvê-lo: tudo o que você precisa
            conferir já está nos campos ao lado.
        filename:
          type: string
          description: >
            O nome já normalizado pelo Pombo, e o nome que o destinatário
            recebeu. Compare com o que você enviou: pode não ser igual.
        mime_type:
          type: string
          enum:
            - application/pdf
            - image/png
            - image/jpeg
        size_bytes:
          type: integer
        hash:
          type: string
          description: A impressão digital do arquivo, a mesma que consta no laudo.
          examples:
            - 9f2c1d7a4b6e8f0c2a4d6e8f0b1c3d5e7f9a1b3c5d7e9f1a3b5c7d9e1f3a5b7c
        hash_algorithm:
          type: string
          description: >-
            Literalmente `SHA-256`, com hífen e em maiúsculas. Compare a string
            exata.
          examples:
            - SHA-256
        hash_encoding:
          type: string
          examples:
            - hex
    Error:
      type: object
      description: >
        O envelope de erro, com quatro campos.


        **Ramifique pelo status HTTP e por `error_code`. Nunca por `code`.** O
        `code` vale `10000 + http_code` em um caminho e uma string arbitrária em
        outro, então não acrescenta nada ao status e muda de tipo entre
        operações vizinhas.
      required:
        - message
      properties:
        code:
          description: >
            **Não ramifique por este campo.** O tipo muda entre operações
            vizinhas: um `404` de modelo traz `10404` e um `404` de notificação
            traz `"NOT_FOUND"`. Para tratar erros em código, use o status HTTP.
          oneOf:
            - title: Número, na validação genérica
              type: integer
              description: >-
                `10000 + http_code`. Um `400` genérico traz `10400`, um `404`
                traz `10404`.
              examples:
                - 10400
            - title: Texto, nos erros específicos
              type: string
              description: Um identificador do erro, como os listados em Erros.
              examples:
                - IDEMPOTENCY_KEY_REUSED
        http_code:
          type: integer
          description: >
            **Opcional.** O status HTTP repetido no corpo. Presente quando o
            erro é devolvido diretamente, ausente quando é lançado e tratado
            pelo tratador geral. Nunca exija este campo no cliente.
        message:
          type: string
          description: >-
            Descrição da falha, para diagnóstico. Em inglês ou em português. Não
            é estável.
        error_code:
          type: string
          enum:
            - MISSING_REQUIRED_FIELD
            - INVALID_SENDER
            - ORGANIZATION_ID_FORBIDDEN
            - INVALID_RECIPIENT
            - TEMPLATE_CONSTANT_FORBIDDEN
            - FILE_TRANSFER_NOT_AVAILABLE_ON_THIS_LANE
            - IDEMPOTENCY_KEY_REUSED
          description: >
            O campo a usar para ramificar. **Opcional na prática:** alguns `400`
            igualmente específicos não o trazem, como o de
            `recipient_tax_id_type` fora do enum e o de `Idempotency-Key` mal
            formado. Quando estiver ausente, use o status HTTP.
    InternalError:
      type: object
      description: O corpo do `500` é reduzido e **não** traz `http_code`.
      properties:
        code:
          type: string
          examples:
            - INTERNAL_ERROR
        message:
          type: string
          examples:
            - Internal Server Error
        request_id:
          type: string
          description: >
            O mesmo valor do cabeçalho `x-request-id`. **Registre-o:** é por ele
            que o suporte encontra a requisição exata.
          examples:
            - req_01M1C5ZKGBGRNW6QHXTP2EM7Y7
    NotificationDeliveryStatus:
      type: string
      enum:
        - PENDING
        - ENQUEUED
        - SENT
        - DELIVERED
        - READ
        - FAILED
      description: >
        **O campo que responde se a mensagem chegou.** Caminho normal: `PENDING`
        → `ENQUEUED` → `SENT` → `DELIVERED` → `READ`.


        No envio, um `EMAIL` foi observado em `ENQUEUED` e um `WHATSAPP` em
        `SENT`.


        **Nunca retrocede**, e `FAILED` é terminal. Você pode não observar todos
        os estados intermediários. Em e-mail não espere `READ`.
  responses:
    BadRequest:
      description: Requisição inválida. Repetir sem alterar nada devolve o mesmo erro.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            json_malformado:
              summary: 'Sem error_code: corpo JSON mal formado'
              value:
                code: 10400
                http_code: 400
                message: 'Invalid request: malformed JSON'
            canal_invalido:
              summary: 'Sem error_code: channel fora dos dois canais aceitos'
              value:
                code: 10400
                http_code: 400
                message: 'Invalid request: channel must be WHATSAPP or EMAIL'
            campo_faltando:
              summary: Campo obrigatório faltando
              value:
                code: 10400
                http_code: 400
                message: 'Invalid request: Missing required fields: template_id'
                error_code: MISSING_REQUIRED_FIELD
            organization_id_proibido:
              summary: organization_id enviado
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: organization_id is not accepted on this
                  endpoint: the API key decides the organization
                error_code: ORGANIZATION_ID_FORBIDDEN
            anexo_por_id:
              summary: 'Sem error_code: attachment_id enviado no envio'
              description: >
                O primeiro erro de quem vem do fluxo antigo de duas chamadas.
                Nesta API o arquivo viaja na própria requisição, como a parte
                `file` de um `multipart/form-data`.
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: attachment_id is not available on the
                  integrations lane; use multipart/form-data with a file part
            transferencia_de_arquivo:
              summary: file_transfer_id enviado no envio
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: file_transfer_id is not available on the
                  integrations lane
                error_code: FILE_TRANSFER_NOT_AVAILABLE_ON_THIS_LANE
            constante_de_modelo:
              summary: language_code ou template_category_type em POST /templates
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: language_code must not be sent: it is set by
                  the server
                error_code: TEMPLATE_CONSTANT_FORBIDDEN
            remetente_invalido:
              summary: sender ausente ou não é objeto
              value:
                code: 10400
                http_code: 400
                message: 'Invalid request: sender is required and must be an object'
                error_code: INVALID_SENDER
            destinatario_invalido:
              summary: Campo de contato do canal ausente
              value:
                code: 10400
                http_code: 400
                message: 'Invalid request: recipient_phone is required for this channel'
                error_code: INVALID_RECIPIENT
            telefone_invalido:
              summary: Telefone fora do formato
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: recipient_phone must start with '+' (e.g.,
                  +5511999998888)
            tipo_de_documento_invalido:
              summary: 'Sem error_code: tipo de documento fora do enum'
              value:
                code: 10400
                http_code: 400
                message: 'Invalid request: recipient_tax_id_type must be CPF or CNPJ'
            chave_de_idempotencia_invalida:
              summary: 'Sem error_code: Idempotency-Key mal formada'
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: Idempotency-Key may only contain letters,
                  digits, underscores and hyphens
            arquivo_grande_demais:
              summary: 'Sem error_code: anexo acima do limite'
              description: >
                O anexo grande demais é `400`, **não `413`**. Os limites vêm do
                tipo do arquivo: 20 MB para PDF, 5 MB para imagem.
              value:
                code: 10400
                http_code: 400
                message: 'Invalid request: File size exceeds maximum allowed (20MB)'
            partes_fora_de_ordem:
              summary: 'Sem error_code: parte notification ausente ou depois do arquivo'
              description: >
                A parte `notification` é validada enquanto o arquivo ainda sobe,
                então precisa vir **antes** da parte `file`.
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: The notification part is required and must
                  come before the file part
            partes_demais:
              summary: 'Sem error_code: uma terceira parte no multipart'
              value:
                code: 10400
                http_code: 400
                message: >-
                  Invalid request: multipart/form-data accepts exactly one
                  notification part and one file part
            variaveis_invalidas:
              summary: Variáveis não correspondem ao modelo
              value:
                code: INVALID_PARAMETERS
                http_code: 400
                message: 'Missing required parameter: {{1}}'
    Unauthorized:
      description: >
        Credencial ausente, inválida ou revogada. A resposta é **idêntica** nos
        três casos: o Pombo não informa qual deles ocorreu, para que não se
        possa descobrir por tentativa quais credenciais já existiram.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalida:
              summary: Credencial não aceita
              value:
                code: 10401
                http_code: 401
                message: Invalid API key
    PaymentRequired:
      description: >
        Envios insuficientes. É o único erro que se resolve **sem alterar a
        requisição**: compre envios e repita.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            texto:
              summary: Recusado na camada de crédito
              value:
                code: INSUFFICIENT_CREDITS
                http_code: 402
                message: Insufficient credits to perform this operation
            numerico:
              summary: Recusado na camada HTTP
              value:
                code: 10402
                http_code: 402
                message: Insufficient credits to perform this operation
    NotFound:
      description: >
        Recurso não encontrado. Um recurso de **outra organização** também
        responde `404`, e não `403`, porque a distinção revelaria que o
        identificador existe. Vale para os dois `GET` e também para o
        `template_id` de um envio.


        O `404` de modelo em `POST /notifications` é devolvido direto pela rota,
        então **não traz `request_id` no corpo**, ao contrário dos erros que
        passam pelo tratador geral. O cabeçalho `x-request-id` está sempre lá:
        use o cabeçalho.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            numerico:
              summary: Código numérico
              value:
                code: 10404
                http_code: 404
                message: Template not found
            texto:
              summary: Código em texto
              value:
                code: NOT_FOUND
                http_code: 404
                message: Notification not found
    Conflict:
      description: >
        Duas causas, distinguidas por `code` e `error_code`.


        **`CONTACT_CONFLICT`.** Os dados do destinatário divergem de um contato
        já cadastrado. Os contatos são unificados pelo `tax_id`. Corrija a
        divergência antes de repetir: a mesma requisição devolve o mesmo
        conflito. O corpo traz `conflict_type`, `existing_contact` e
        `input_data` para você reconciliar.


        **`IDEMPOTENCY_KEY_REUSED`.** O `Idempotency-Key` já foi usado com um
        corpo diferente, **ou com o mesmo corpo e outro arquivo**, que conta
        igual. Use uma chave nova. Este corpo é lançado, não devolvido, então
        vem **sem `http_code`**.
      content:
        application/json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Error'
              - type: object
                properties:
                  conflict_type:
                    type: string
                    enum:
                      - tax_id_phone_mismatch
                      - phone_tax_id_mismatch
                      - name_mismatch
                  existing_contact:
                    type: object
                    description: O contato já cadastrado no Pombo.
                    additionalProperties: true
                  input_data:
                    type: object
                    description: Os dados que você enviou, para comparação.
                    additionalProperties: true
          examples:
            telefone_diferente:
              summary: Mesmo CPF, telefone diferente
              value:
                code: CONTACT_CONFLICT
                http_code: 409
                message: >-
                  Contato com este CPF já existe com um número de telefone
                  diferente
                conflict_type: tax_id_phone_mismatch
                existing_contact:
                  contact_id: PBD_CNT_01J9Z2Q8XK3M7WPTV6RB4CYH0N
                  name: Maria Silva
                  tax_id: '12345678901'
                  phone: '+5511999998888'
                input_data:
                  name: Maria S. Silva
                  phone: '+5511888887777'
                  tax_id: '12345678901'
                  tax_id_type: CPF
            chave_reutilizada:
              summary: Idempotency-Key reutilizada com outro corpo
              value:
                code: 10409
                message: Idempotency-Key already used with a different request
                error_code: IDEMPOTENCY_KEY_REUSED
    PayloadTooLarge:
      description: >
        A **mensagem renderizada** excede o limite de certificação. **Nada foi
        enviado e nada foi cobrado**: o Pombo recusa antes de registrar, em vez
        de certificar um texto truncado.


        É o único `413` desta API. **Um arquivo grande demais é `400`, não
        `413`.** Veja `400`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            grande:
              summary: Mensagem grande demais
              value:
                code: MESSAGE_TOO_LARGE_TO_CERTIFY
                http_code: 413
                message: >-
                  A mensagem tem 82134 caracteres e excede o limite de 60000
                  para certificação. Reduza o conteúdo (ou o HTML colado nos
                  dados personalizados) e envie novamente. O envio não foi
                  realizado.
    BusinessRuleRejected:
      description: >
        Recusado por regra de negócio. O `code` diz qual. Em `POST /templates` o
        único caso é `10422`, conteúdo recusado pela política; os demais são do
        envio.


        `SENDER_CHOICE_REQUIRED` significa que a organização tem mais de um
        endereço de envio utilizável. O Pombo não escolhe entre eles, porque o
        endereço de origem é certificado no laudo.


        A contagem é dos endereços dedicados **verificados** da organização,
        mais o endereço compartilhado do Pombo. Com exatamente um utilizável, é
        ele que envia; com mais de um, o envio é recusado.


        Na prática: uma organização **sem** endereço dedicado verificado envia
        pelo endereço compartilhado do Pombo, e esse é o caso comum. Uma
        organização que verificou **um** endereço próprio passa a ter dois
        utilizáveis e recebe o `422`.


        **A saída é falar com o suporte do Pombo**, que ajusta quais endereços
        ficam utilizáveis para a organização. Nenhuma chamada desta API resolve
        isso.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            modelo_nao_aprovado:
              summary: Modelo não aprovado
              value:
                code: TEMPLATE_NOT_APPROVED
                http_code: 422
                message: >-
                  Template PBD_TPL_01J9Z2Q8XK3M7WPTV6RB4CYH0N is not approved
                  (current status: PENDING)
            escolha_de_remetente:
              summary: Mais de um endereço de envio utilizável
              value:
                code: SENDER_CHOICE_REQUIRED
                http_code: 422
                message: Escolha o endereço de envio desta notificação.
            politica_de_conteudo:
              summary: Conteúdo recusado pela política, em POST /templates
              description: >
                Só em `POST /templates`, na criação do modelo. O corpo não diz
                qual regra falhou: reveja o texto.
              value:
                code: 10422
                http_code: 422
                message: Content policy violation detected
    TooManyRequests:
      description: >
        Limite de requisições excedido. São três contagens independentes, em
        janelas de um minuto: 50 por credencial, 200 por organização, 300 por
        IP.


        **Espere o que o `Retry-After` manda esperar**, em segundos. Os
        cabeçalhos `RateLimit-Limit`, `RateLimit-Remaining` e `RateLimit-Reset`
        vêm em toda resposta, não só no `429`.
      headers:
        Retry-After:
          description: Quantos segundos esperar antes de repetir.
          schema:
            type: integer
            examples:
              - 43
        RateLimit-Limit:
          description: O teto da janela.
          schema:
            type: integer
            examples:
              - 200
        RateLimit-Remaining:
          description: Quantas requisições ainda cabem na janela.
          schema:
            type: integer
            examples:
              - 0
        RateLimit-Reset:
          description: Quantos segundos faltam para a janela virar.
          schema:
            type: integer
            examples:
              - 43
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            limite:
              summary: Limite excedido
              value:
                code: RATE_LIMIT_EXCEEDED
                http_code: 429
                message: Too many requests. Please try again later.
    InternalError:
      description: >
        Erro interno. Em `POST /notifications`, um `500` **não garante que nada
        foi enviado**: consulte antes de repetir.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/InternalError'
          examples:
            interno:
              summary: Erro interno
              value:
                code: INTERNAL_ERROR
                message: Internal Server Error
                request_id: req_01M1C5ZKGBGRNW6QHXTP2EM7Y7
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >
        Sua credencial de API no cabeçalho `Authorization: Bearer sk_live_...`.
        Crie e revogue credenciais no painel, em Conta → API Keys. O segredo
        aparece uma única vez, na criação.


        A credencial identifica a organização: `organization_id` não é aceito em
        nenhuma requisição.

````