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

# Enviar mensagem

> Envia uma mensagem de WhatsApp. Mande exatamente **um** conteúdo:
`templateName`, `body`, `interactive`, `location` ou `reaction`. Zero ou
mais de um responde `400 VALIDATION_ERROR`.

- Template aprovado (`templateName`) pode ser enviado a qualquer hora.
- Os demais conteúdos só passam com a janela de 24h aberta. Fora dela:
  `422 CONVERSATION_WINDOW_CLOSED`.

A resposta `202` traz o `id` da mensagem. O status final chega pelo webhook
`message.status_updated`.

`Idempotency-Key` (opcional): a mesma chave, para o mesmo usuário, devolve a
mensagem já criada, sem novo envio nem nova cobrança. A chave não expira e o
corpo **não** é comparado: reusar a chave com outro destinatário devolve a
mensagem antiga, sem erro. Use uma chave por intenção de envio, derivada de
um evento do seu sistema (ex. `pedido-12345-confirmacao`).

Permissão: `MESSAGES_SEND`.




## OpenAPI

````yaml /openapi.yaml post /v1/messages
openapi: 3.0.3
info:
  title: API da Arara
  description: >
    API REST da Arara para WhatsApp Business: mensagens, templates, campanhas,

    contatos, conversas, formulários, automações, smart links e carteira.


    Esta especificação cobre só os endpoints que aceitam **chave de API**

    (`Authorization: Bearer ara_live_...`). Rotas usadas apenas pelo painel

    (gestão de chaves, configuração do webhook, pagamentos e perfil pessoal)

    ficam de fora.


    ## Autenticação


    Header `Authorization: Bearer ara_live_...`. Crie a chave em

    https://app.ararahq.com/dashboard/apikeys. As chaves são sempre LIVE:

    mensagens são entregues de verdade e o custo sai da carteira.


    ## Permissões da chave


    Chave criada sem permissões explícitas tem acesso total (`ADMIN`). Chave

    com permissões restritas só alcança:


    - `READ`: `GET` em `/v1/messages`, `/v1/campaigns`, `/v1/templates`,
      `/v1/flows`, `/v1/automations` e `/v1/organizations/me/numbers`.
    - `MESSAGES_SEND`: escrita em `/v1/messages/**`, `POST /v1/media/upload` e
      `POST /v1/flows/{id}/test`.
    - `TEMPLATES_WRITE`: escrita em `/v1/templates/**` e `/v1/flows/**`.

    - `CAMPAIGNS_SEND`: escrita em `/v1/campaigns/**`.

    - `CONTACTS_WRITE`: escrita em `/v1/contacts/**`.

    - `AUTOMATIONS_MANAGE`: escrita em `/v1/automations/**`.

    - `WEBHOOKS_MANAGE`: escrita em `/webhook/**`.


    Qualquer outra rota exige `ADMIN`. Fora do escopo, a resposta é `403`.


    ## Rate limit


    Por usuário da chave: 1.200 leituras (`GET`) por minuto e 3.000 escritas por

    minuto. Toda resposta traz `X-RateLimit-Remaining`. Acima do limite: `429`

    com `Retry-After` e `error.code = RATE_LIMIT_EXCEEDED`. O envio tem limites

    próprios (`SEND_RATE_LIMITED`, `BATCH_BUSY`,
    `MARKETING_FREQUENCY_EXCEEDED`).


    ## Formato de erro


    ```json

    { "error": { "code": "ERROR_CODE", "message": "Descrição", "details": {} } }

    ```


    Exceções: falha de autenticação (`401`, `403` e `503` do filtro de chave)

    volta no formato padrão do Spring (`timestamp`, `status`, `error`, `path`,

    sem `code`), e alguns `404` e `403` voltam sem corpo.
  version: 2.1.0
  contact:
    name: AraraHQ
    url: https://ararahq.com
    email: oi@ararahq.com
servers:
  - url: https://api.ararahq.com
    description: Produção
security:
  - bearerAuth: []
tags:
  - name: Mensagens
    description: Envio, consulta e histórico de mensagens de WhatsApp, e upload de mídia.
  - name: Conversas
    description: >-
      Conversas com cada telefone, resposta dentro da janela de 24h e situação
      da janela.
  - name: Templates
    description: Criação, listagem, status de aprovação e métricas de templates.
  - name: Campanhas
    description: >-
      Envio do mesmo template para muitos contatos, com estimativa, agendamento,
      cancelamento e teste A/B.
  - name: Contatos
    description: Base de contatos da organização e direitos do titular (LGPD).
  - name: Opt-out
    description: Registro e consulta de quem pediu para não receber mensagens.
  - name: Formulários
    description: >-
      Formulários nativos do WhatsApp (Flows) e as respostas recebidas.
      Disponíveis do plano Voo para cima.
  - name: Automações
    description: >-
      Sequências de passos que rodam sozinhas para cada contato que entra por um
      gatilho. Exigem plano com recuperação de receita.
  - name: Smart Links
    description: Links wa.me rastreáveis, com QR code.
  - name: Números
    description: >-
      Números de WhatsApp da organização e o perfil comercial exibido no
      WhatsApp.
  - name: Carteira
    description: Saldo, extrato, recarga automática e preços.
  - name: Consulta de telefone
    description: Consulta de tipo de linha e de WhatsApp de um número (cobrada).
  - name: Recuperação
    description: Endpoint de recuperação de receita (carrinho abandonado, Pix, boleto).
  - name: Conta
    description: Identidade da chave, plano da organização e status da API.
paths:
  /v1/messages:
    post:
      tags:
        - Mensagens
      summary: Enviar mensagem
      description: >
        Envia uma mensagem de WhatsApp. Mande exatamente **um** conteúdo:

        `templateName`, `body`, `interactive`, `location` ou `reaction`. Zero ou

        mais de um responde `400 VALIDATION_ERROR`.


        - Template aprovado (`templateName`) pode ser enviado a qualquer hora.

        - Os demais conteúdos só passam com a janela de 24h aberta. Fora dela:
          `422 CONVERSATION_WINDOW_CLOSED`.

        A resposta `202` traz o `id` da mensagem. O status final chega pelo
        webhook

        `message.status_updated`.


        `Idempotency-Key` (opcional): a mesma chave, para o mesmo usuário,
        devolve a

        mensagem já criada, sem novo envio nem nova cobrança. A chave não expira
        e o

        corpo **não** é comparado: reusar a chave com outro destinatário devolve
        a

        mensagem antiga, sem erro. Use uma chave por intenção de envio, derivada
        de

        um evento do seu sistema (ex. `pedido-12345-confirmacao`).


        Permissão: `MESSAGES_SEND`.
      operationId: sendMessage
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyOptional'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendMessageRequest'
            examples:
              template:
                summary: Template aprovado
                value:
                  receiver: '+5511999999999'
                  templateName: pedido_confirmado
                  templateVariables:
                    - João
                    - '12345'
              texto:
                summary: Texto na janela de 24h
                value:
                  receiver: '+5511999999999'
                  body: Já vou te responder, só preciso checar uma coisa.
              midia:
                summary: Template com mídia no cabeçalho
                value:
                  receiver: '+5511999999999'
                  templateName: fatura_disponivel
                  templateVariables:
                    - https://meusite.com/fatura.pdf
                    - João
                    - Janeiro
              agendada:
                summary: Mensagem agendada
                value:
                  receiver: '+5511999999999'
                  templateName: lembrete_consulta
                  templateVariables:
                    - amanhã às 14h
                  scheduled_at: '2027-01-15T14:00:00-03:00'
              botoes:
                summary: Botões de resposta
                value:
                  receiver: '+5511999999999'
                  interactive:
                    type: button
                    body: Qual a sua dúvida?
                    buttons:
                      - id: rastreio
                        title: Rastrear pedido
                      - id: troca
                        title: Trocar produto
              lista:
                summary: Lista de opções
                value:
                  receiver: '+5511999999999'
                  interactive:
                    type: list
                    body: Escolha o setor
                    buttonText: Ver setores
                    sections:
                      - title: Atendimento
                        rows:
                          - id: vendas
                            title: Vendas
                            description: Planos e preços
                          - id: suporte
                            title: Suporte
              formulario:
                summary: Formulário
                value:
                  receiver: '+5511999999999'
                  interactive:
                    type: flow
                    flowId: 0b7e6a52-3c1d-4f7a-9a55-2f4f6f1f9c10
                    body: Pode responder rapidinho por aqui?
                    buttonText: Responder
              localizacao:
                summary: Localização
                value:
                  receiver: '+5511999999999'
                  location:
                    latitude: -23.5613
                    longitude: -46.6565
                    name: Loja Paulista
                    address: Av. Paulista, 1000
              reacao:
                summary: Reação
                value:
                  receiver: '+5511999999999'
                  reaction:
                    emoji: 👍
                    messageId: ara_msg_k9Bf2Xq
      responses:
        '202':
          description: Mensagem aceita
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          description: >
            - Autenticação (corpo padrão do Spring): header ausente ou chave sem
              `MESSAGES_SEND`.
            - `ACCOUNT_NOT_ACTIVATED`: conta ainda não ativada. Com
              `details.reason = FIRST_SUBSCRIPTION_REQUIRED`, falta a primeira
              assinatura.
            - `ORGANIZATION_SUSPENDED`, `FRAUD_SUSPECTED`,
            `PLAN_FEATURE_LOCKED`.

            - `NUMBER_BLOCKED`: destinatário que falhou repetidas vezes
            (inválido ou
              sem WhatsApp).
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/Error'
                  - $ref: '#/components/schemas/SpringError'
              example:
                error:
                  code: ACCOUNT_NOT_ACTIVATED
                  message: Ative a conta para enviar mensagens.
                  details:
                    reason: FIRST_SUBSCRIPTION_REQUIRED
        '409':
          description: >
            - `CONFLICT`: template não encontrado na organização.

            - `PROVIDER_NOT_RESOLVED`: o número de envio não está pronto para
            enviar.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: CONFLICT
                  message: Template 'pedido_confirmado' não encontrado.
                  details: {}
        '422':
          description: >
            - `INVALID_RECIPIENT`: telefone inválido (precisa ter de 7 a 15
            dígitos).

            - `RECIPIENT_OPTED_OUT`: o destinatário pediu para não receber.

            - `CONVERSATION_WINDOW_CLOSED`: conteúdo fora de template com a
            janela de
              24h fechada.
            - `TEMPLATE_PAUSED`, `TEMPLATE_NOT_SENDABLE`,
            `TEMPLATE_VARIABLES_MISMATCH`:
              template pausado, não aprovado ou com número de variáveis diferente.
            - `SENDER_NOT_ALLOWED`: `sender` não pertence à organização ou não
            pode ser
              usado.
            - `UNPROCESSABLE_ENTITY`: outras regras de envio sem código próprio.

            - `ararahq-*`: falha reconhecida do provedor, com
            `details.diagnostic`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                error:
                  code: CONVERSATION_WINDOW_CLOSED
                  message: A janela de 24h está fechada. Envie um template.
                  details: {}
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  parameters:
    IdempotencyKeyOptional:
      in: header
      name: Idempotency-Key
      required: false
      schema:
        type: string
        maxLength: 255
      description: >-
        Chave de idempotência, de 1 a 255 caracteres, sem caracteres de controle
        (fora disso: `400 INVALID_IDEMPOTENCY_KEY`). Derive de um evento do seu
        sistema (id do pedido, id da tentativa) e reuse a mesma chave nas
        retentativas.
      example: pedido-12345-confirmacao
  schemas:
    SendMessageRequest:
      type: object
      required:
        - receiver
      description: >-
        Envie exatamente um entre `templateName`, `body`, `interactive`,
        `location` e `reaction`.
      properties:
        receiver:
          type: string
          description: >
            Telefone do destinatário. Formato recomendado: E.164 com `+`

            (`+5511999999999`). Também passam `5511999999999` e

            `whatsapp:+5511999999999`. A Arara fica só com os dígitos e exige de
            7

            a 15; fora disso, `422 INVALID_RECIPIENT`.
          example: '+5511999999999'
        templateName:
          type: string
          nullable: true
          description: Nome do template aprovado. Pode ser enviado a qualquer hora.
          example: pedido_confirmado
        templateVariables:
          type: array
          nullable: true
          items:
            type: string
          description: >
            Variáveis posicionais do template, na ordem de `{{1}}`, `{{2}}`...

            (alias aceito: `variables`). Em template com mídia no cabeçalho, a
            URL

            da mídia vai na primeira posição. Não aceita quebra de linha nem
            tab.
          example:
            - João
            - '12345'
        body:
          type: string
          nullable: true
          description: >-
            Texto da mensagem. Só com a janela de 24h aberta; fora dela, `422
            CONVERSATION_WINDOW_CLOSED`.
        interactive:
          allOf:
            - $ref: '#/components/schemas/InteractivePayload'
          nullable: true
          description: >-
            Botões, lista, formulário ou cobrança. Só com a janela de 24h
            aberta.
        location:
          allOf:
            - $ref: '#/components/schemas/LocationPayload'
          nullable: true
          description: Localização. Só com a janela de 24h aberta.
        reaction:
          allOf:
            - $ref: '#/components/schemas/ReactionPayload'
          nullable: true
          description: Reação com emoji a uma mensagem.
        charge:
          allOf:
            - $ref: '#/components/schemas/ChargePayload'
          nullable: true
          description: >-
            Cobrança que acompanha um template com botão `CHARGE`. Use junto com
            `templateName`.
        sender:
          type: string
          nullable: true
          description: >-
            Número da organização que envia. Omitido, usa o número padrão.
            Número que não é seu responde `422 SENDER_NOT_ALLOWED`.
        replyTo:
          type: string
          nullable: true
          description: ID (`ara_msg_...`) da mensagem citada na resposta.
        scheduled_at:
          type: string
          format: date-time
          nullable: true
          description: |
            Agenda o envio (ISO 8601). Data no futuro deixa a mensagem em
            `SCHEDULED` até o horário. Data no passado envia na hora. Aceita
            também o nome `scheduledAt`.
          example: '2027-01-15T14:00:00-03:00'
        smartLinkParam:
          type: string
          nullable: true
          description: Parâmetro do botão de smart link do template.
        smartLinkUrl:
          type: string
          nullable: true
          description: URL de destino do botão de smart link do template.
        type:
          type: string
          nullable: true
          enum:
            - template
            - text
            - interactive
            - location
            - reaction
          description: Opcional. Quando omitido, é inferido do conteúdo enviado.
        mode:
          type: string
          nullable: true
          enum:
            - LIVE
          description: >-
            Opcional. Todo envio por chave de API é real (`LIVE`). Não existe
            modo de teste.
        media_url:
          type: string
          nullable: true
          deprecated: true
          description: >-
            Legado (aceita também `mediaUrl`). Use a primeira posição de
            `templateVariables`.
    MessageResponse:
      type: object
      required:
        - status
        - mode
        - sender
        - receiver
      properties:
        id:
          type: string
          nullable: true
          description: >-
            ID público da mensagem. Use para consultar e para casar com o
            webhook.
          example: ara_msg_k9Bf2Xq
        status:
          type: string
          enum:
            - PENDING
            - SCHEDULED
            - PROCESSING
            - SENT
            - DELIVERED
            - READ
            - FAILED
            - CANCELED
          description: |
            Estado atual, sempre em maiúsculas:

            - `PENDING`: criada, na fila.
            - `SCHEDULED`: agendada.
            - `PROCESSING`: em processamento.
            - `SENT`: aceita pelo provedor.
            - `DELIVERED`: entregue no aparelho.
            - `READ`: lida.
            - `FAILED`: falhou. Veja `reason`.
            - `CANCELED`: cancelada antes do envio.

            O webhook `message.status_updated` usa os mesmos estados em
            minúsculas, com `PENDING` virando `queued`.
        mode:
          type: string
          enum:
            - LIVE
          description: Todo envio por chave de API é `LIVE`.
        sender:
          type: string
          description: Número que enviou.
        receiver:
          type: string
          description: Número do destinatário.
        body:
          type: string
          nullable: true
          description: Conteúdo final enviado, com as variáveis aplicadas. Pode vir `null`.
        cost:
          type: number
          nullable: true
          description: >-
            Custo em reais. Pode vir `null`. Os valores dos exemplos são
            ilustrativos.
        reason:
          type: string
          nullable: true
          description: Motivo da falha quando `status` é `FAILED`.
      example:
        id: ara_msg_k9Bf2Xq
        status: PENDING
        mode: LIVE
        sender: whatsapp:+5511888888888
        receiver: whatsapp:+5511999999999
        body: Oi João, seu pedido 12345 foi confirmado!
        cost: 0.068
        reason: null
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              example: INSUFFICIENT_FUNDS
              description: >-
                Código estável do erro. Use este campo no seu tratamento, não a
                mensagem.
            message:
              type: string
              nullable: true
              description: Descrição legível, em português. Pode mudar.
            details:
              type: object
              additionalProperties: true
              description: Dados extras do erro. Vazio na maioria dos casos.
      example:
        error:
          code: INSUFFICIENT_FUNDS
          message: Saldo insuficiente.
          details: {}
    SpringError:
      type: object
      description: >-
        Corpo padrão do Spring nas falhas de autenticação (`401`, `403` e `503`
        do filtro de chave). Não tem `error.code`.
      properties:
        timestamp:
          type: string
          format: date-time
        status:
          type: integer
          example: 401
        error:
          type: string
          example: Unauthorized
        path:
          type: string
          example: /v1/messages
    InteractivePayload:
      type: object
      required:
        - type
        - body
      properties:
        type:
          type: string
          enum:
            - button
            - list
            - flow
            - charge
            - charge_status
          description: >
            - `button`: botões de resposta (`buttons`).

            - `list`: lista de opções (`buttonText` e `sections`).

            - `flow`: abre um formulário (`flowId`).

            - `charge`: cartão de cobrança (`charge`).

            - `charge_status`: baixa de uma cobrança já enviada
            (`chargeStatus`).
        body:
          type: string
          description: Texto da mensagem.
        buttonText:
          type: string
          nullable: true
          description: >-
            Em `list`, texto do botão que abre a lista (obrigatório). Em `flow`,
            texto do botão que abre o formulário (padrão `Responder`, até 20
            caracteres).
        sections:
          type: array
          nullable: true
          description: Só em `list`.
          items:
            $ref: '#/components/schemas/InteractiveSection'
        buttons:
          type: array
          nullable: true
          description: >-
            Só em `button`. A resposta do cliente chega pelo webhook
            `message.received`.
          items:
            $ref: '#/components/schemas/InteractiveButton'
        flowId:
          type: string
          format: uuid
          nullable: true
          description: Só em `flow`. O `id` do formulário, de `GET /v1/flows`.
        charge:
          allOf:
            - $ref: '#/components/schemas/ChargePayload'
          nullable: true
          description: Só em `charge`.
        chargeStatus:
          allOf:
            - $ref: '#/components/schemas/ChargeStatusPayload'
          nullable: true
          description: Só em `charge_status`.
    LocationPayload:
      type: object
      required:
        - latitude
        - longitude
      properties:
        latitude:
          type: number
          format: double
        longitude:
          type: number
          format: double
        name:
          type: string
          nullable: true
          description: Nome do local.
        address:
          type: string
          nullable: true
          description: Endereço exibido.
    ReactionPayload:
      type: object
      required:
        - emoji
        - messageId
      properties:
        emoji:
          type: string
          description: Emoji da reação.
        messageId:
          type: string
          description: ID (`ara_msg_...`) da mensagem que recebe a reação.
    ChargePayload:
      type: object
      required:
        - referenceId
        - items
      description: |
        Cobrança enviada como cartão de pagamento. Valores sempre em centavos.
        Informe um meio de pagamento por cobrança: `pix`, `paymentLink` ou
        `boleto`. O pagamento é gerado no seu banco ou PSP; a Arara só entrega o
        cartão. Guia em [Cobranças](/cobrancas).
      properties:
        referenceId:
          type: string
          description: Seu identificador da cobrança. É com ele que você dá baixa depois.
        items:
          type: array
          items:
            $ref: '#/components/schemas/ChargeItem'
        pix:
          type: object
          nullable: true
          required:
            - code
            - key
            - keyType
            - merchantName
          properties:
            code:
              type: string
              description: Pix copia e cola (BR Code) gerado no seu banco ou PSP.
            key:
              type: string
            keyType:
              type: string
              enum:
                - CPF
                - CNPJ
                - EMAIL
                - PHONE
                - EVP
            merchantName:
              type: string
        paymentLink:
          type: object
          nullable: true
          required:
            - url
          properties:
            url:
              type: string
              description: Link de pagamento, em `https`.
        boleto:
          type: object
          nullable: true
          required:
            - digitableLine
          properties:
            digitableLine:
              type: string
              description: Linha digitável do boleto, só dígitos.
        shippingCents:
          type: integer
          format: int64
          default: 0
        discountCents:
          type: integer
          format: int64
          default: 0
        expiresAt:
          type: integer
          format: int64
          nullable: true
          description: Vencimento, em epoch segundos. Pelo menos 5 minutos à frente.
        expiresDescription:
          type: string
          nullable: true
          description: Texto exibido junto do vencimento. Só vale com `expiresAt`.
        imageUrl:
          type: string
          nullable: true
          description: Foto no topo do cartão. Sem ela, vale a do primeiro item com foto.
    InteractiveSection:
      type: object
      required:
        - title
        - rows
      properties:
        title:
          type: string
        rows:
          type: array
          items:
            $ref: '#/components/schemas/InteractiveRow'
    InteractiveButton:
      type: object
      required:
        - id
        - title
      properties:
        id:
          type: string
          description: Identificador do botão. Volta no webhook quando o cliente toca.
        title:
          type: string
          description: Texto do botão.
    ChargeStatusPayload:
      type: object
      required:
        - referenceId
        - status
      properties:
        referenceId:
          type: string
          description: O `referenceId` da cobrança enviada.
        status:
          type: string
          enum:
            - PAID
            - FAILED
            - CANCELED
            - EXPIRED
        description:
          type: string
          nullable: true
    ChargeItem:
      type: object
      required:
        - name
        - amountCents
      properties:
        name:
          type: string
        amountCents:
          type: integer
          format: int64
          description: Valor unitário, em centavos.
        quantity:
          type: integer
          default: 1
        id:
          type: string
          nullable: true
        imageUrl:
          type: string
          nullable: true
          description: Foto do item. Com foto, o `id` do item é ignorado.
    InteractiveRow:
      type: object
      required:
        - id
        - title
      properties:
        id:
          type: string
          description: Identificador da opção. Volta no webhook quando o cliente escolhe.
        title:
          type: string
        description:
          type: string
          nullable: true
  responses:
    BadRequest:
      description: >-
        Requisição inválida: `VALIDATION_ERROR` (campo obrigatório ausente ou
        regra do corpo), `INVALID_JSON` (JSON malformado ou tipo errado),
        `INVALID_PATH_PARAM` ou `INVALID_IDEMPOTENCY_KEY`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: VALIDATION_ERROR
              message: O número do recebedor é obrigatório
              details: {}
    Unauthorized:
      description: >
        Chave inexistente, revogada, expirada, com prefixo errado (`ara_` sem

        `ara_live_`) ou vinda de IP fora da allowlist. O corpo é o JSON padrão
        do

        Spring, sem `error.code`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SpringError'
          example:
            timestamp: '2026-09-15T14:30:00.000+00:00'
            status: 401
            error: Unauthorized
            path: /v1/messages
    PaymentRequired:
      description: >-
        `INSUFFICIENT_FUNDS` (saldo insuficiente na carteira) ou
        `PLAN_REQUIRED`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INSUFFICIENT_FUNDS
              message: Saldo insuficiente.
              details: {}
    TooManyRequests:
      description: >
        Limite atingido:


        - `RATE_LIMIT_EXCEEDED`: limite geral da API. Traz `Retry-After` e
          `X-RateLimit-Remaining`.
        - `SEND_RATE_LIMITED`: teto de envio, inclusive o limite diário da
        chave.
          Traz `Retry-After`.
        - `BATCH_BUSY`: já existe um lote em processamento. Traz `Retry-After`.

        - `MARKETING_FREQUENCY_EXCEEDED`: o contato já recebeu marketing demais
          na janela. **Não** traz `Retry-After`.
      headers:
        Retry-After:
          schema:
            type: integer
          description: >-
            Segundos a aguardar antes de repetir a chamada, com a mesma
            `Idempotency-Key`. Ausente em `MARKETING_FREQUENCY_EXCEEDED`.
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: >-
            Requisições restantes na janela atual. Presente em
            `RATE_LIMIT_EXCEEDED`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: SEND_RATE_LIMITED
              message: Limite de envio atingido. Tente de novo em instantes.
              details: {}
    ServiceUnavailable:
      description: >
        Indisponibilidade temporária. Espere o `Retry-After` e repita com a
        mesma

        `Idempotency-Key`.


        - `SEND_TEMPORARILY_UNAVAILABLE`: o envio não pôde ser aceito agora
          (formato `{ "error": { ... } }`).
        - Autenticação indisponível: a chave não pôde ser conferida. Corpo no
          formato padrão do Spring, com `Retry-After: 5`.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Segundos a aguardar antes de repetir a chamada.
      content:
        application/json:
          schema:
            anyOf:
              - $ref: '#/components/schemas/Error'
              - $ref: '#/components/schemas/SpringError'
          example:
            error:
              code: SEND_TEMPORARILY_UNAVAILABLE
              message: Envio temporariamente indisponível. Tente de novo em instantes.
              details: {}
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ara_live_...
      description: >-
        Chave de API da Arara (`ara_live_...`), enviada no header
        `Authorization: Bearer ara_live_...`. Crie e gerencie as chaves no
        painel: https://app.ararahq.com/dashboard/apikeys. O valor completo
        aparece uma única vez, na criação.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.