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

# Send message (asynchronous)

> Queues the message for sending and responds right away with a `jobId`. The message is sent in
the background, because communicating with the provider can take a few seconds.

The `type` field defines the kind of message being sent.

**Recipient.** Provide one of these, in this order of precedence:

| field | when to use |
| --- | --- |
| `ticketId` | Send inside a ticket that already exists. Takes precedence over the other two. |
| `contactId` | Send to a contact that already exists, on any channel. Required on channels without a phone number (Instagram, Facebook/Messenger, Webchat, Telegram) — on them you can only message someone who has already talked to the channel. Takes precedence over `number`. |
| `number` | Phone channels (WhatsApp, RCS). If there is no contact with this number yet, one is created. Not valid on channels without a phone number. |

The recipient is checked before the message is queued:

| code | when it happens |
| --- | --- |
| `ERR_API_CONTACT_NOT_IDENTIFIED` (`400`) | None of `ticketId`, `contactId` or `number` — or only `number` on a channel without a phone number. |
| `ERR_API_CONTACT_NOT_FOUND` (`404`) | `contactId` does not exist or belongs to another company. |
| `ERR_API_CONTACT_UNREACHABLE` (`422`) | The contact cannot receive on this channel: no previous conversation on it (channels without a phone number; on Webchat, no visitor session on this webchat) or no phone number (phone channels). |

**Interactive on Webchat.** The webchat renders the same interactive messages as the official
WhatsApp API that do not depend on a Meta resource: `button`, `list`, `cta_url`,
`location_request_message` and `carousel`. On it, `interactive` is checked before the message is
queued, with the WhatsApp limits, and the problem comes back right away: `422` with the code
`ERR_API_INTERACTIVE_INVALID` and, in `message`, the field and the reason (for example
`interactive.action.buttons: de 1 a 3 botões`). `flow`, `product`, `product_list`,
`catalog_message`, `address_message`, `voice_call` and `call_permission_request` do not exist on
webchat. On the other channels the provider does the checking, during the send.

**Use `Idempotency-Key` here.** If the call times out and you retry without this key, the
contact may receive the same message twice.

**Media is checked before it enters the queue.** With `type: media`, the file is read during the
call itself, and not during the background send — so a problematic file gets a `400` right away,
instead of returning a `jobId` that would fail later with no warning:

| code | when it happens |
| --- | --- |
| `ERR_API_MEDIA_EMPTY` | The uploaded file has 0 bytes. |
| `ERR_API_MEDIA_TYPE_UNKNOWN` | There is no way to tell the type: the name came without an extension and the `Content-Type` of the multipart part was not provided. |
| `ERR_API_MEDIA_TYPE_MISMATCH` | The file contents contradict the declared type (for example, a PDF sent as an image). |

A difference only in format within the same category is not rejected: a PNG declared as JPEG is
sent normally. The type used for sending is always the one from the file contents.



## OpenAPI

````yaml /api-reference/openapi.en.yaml post /messages
openapi: 3.0.3
info:
  title: Connecta DESK Public API
  version: 1.0.0
  description: >-
    Public API of Connecta DESK, available at `/api/v1`. Access uses a token
    generated in the tokens module. Each token has permissions per **module ×
    method**, and every list read is paginated.


    ## Authentication


    ```

    Authorization: Bearer sk_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

    ```


    The token belongs to a **company**, not to a user. The company used is
    always the token's company, so sending `companyId` in the body has no
    effect. A missing, invalid, revoked or expired token gets the same `401`
    response.


    ## Order of checks


    1. **token** → `401`

    2. **active company** → `403`

    3. **API enabled on the plan** → `403 ERR_API_NOT_ENABLED` (does not apply
    to on-premise)

    4. **company rate limit** → `429`

    5. **token permission** → `403 ERR_API_FORBIDDEN_SCOPE`


    ## User ids


    Every user id in the API is the user's id **in this company**, the same one
    `GET /users` returns. This applies to what the API returns (`user.id`,
    `assignedTo.id`, `userCreated.id`) and to what it accepts (`userId`,
    `userIds`, `assignedToUserId` and the `{id}` in user routes).


    The same person can work in several companies, with a different name and
    profile in each one, and has a different id in each company.


    An id that does not belong to this company returns `404`, with the same
    message as an id that does not exist.


    ## Pagination


    Every list `GET` is paginated. The `meta` contains only `page`, `perPage`
    and `hasMore`, without the total number of records. To go through
    everything, keep advancing `page` while `hasMore` is `true`.


    ## Idempotency


    Optional on `POST`/`PUT`, through the `Idempotency-Key` header. If you
    repeat the call with the same key and the same body, you get the stored
    response (with `Idempotent-Replay: true`). With the same key and a different
    body, the response is `409`.
servers:
  - url: https://api.connectadesk.com/api/v1
    description: Production
security:
  - apiToken: []
tags:
  - name: contacts
    description: Contact records, extra fields and marketing source.
  - name: tickets
    x-displayName: Tickets
    description: Opening, transferring, closing and wrap-up of tickets.
  - name: messages
    description: Asynchronous sending and reading of messages.
  - name: tags
    description: Tags and their link to tickets.
  - name: queues
    description: Service queues and their agents.
  - name: wrapups
    description: Wrap-up types.
  - name: users
    description: Agents and administrators.
  - name: schedules
    description: Scheduled messages.
  - name: quick-messages
    description: Quick replies.
  - name: campaigns
    description: Bulk sending.
  - name: crm
    description: Boards, stages, cards and metrics.
  - name: channels
    description: Channels (read only).
  - name: reports
    description: Reports and dashboards.
  - name: products
    description: Product catalog, categories and sales performance.
  - name: custom-fields
    description: Custom fields for contacts and cards, plus company-wide global variables.
  - name: profiles
    description: Company permission profiles (read only).
  - name: webchat
    description: >-
      Native webchat from your own system: open a visitor session, send and read
      messages. The token needs the `webchat` scope (GET and POST).
paths:
  /messages:
    post:
      tags:
        - messages
      summary: Send message (asynchronous)
      description: >-
        Queues the message for sending and responds right away with a `jobId`.
        The message is sent in

        the background, because communicating with the provider can take a few
        seconds.


        The `type` field defines the kind of message being sent.


        **Recipient.** Provide one of these, in this order of precedence:


        | field | when to use |

        | --- | --- |

        | `ticketId` | Send inside a ticket that already exists. Takes
        precedence over the other two. |

        | `contactId` | Send to a contact that already exists, on any channel.
        Required on channels without a phone number (Instagram,
        Facebook/Messenger, Webchat, Telegram) — on them you can only message
        someone who has already talked to the channel. Takes precedence over
        `number`. |

        | `number` | Phone channels (WhatsApp, RCS). If there is no contact with
        this number yet, one is created. Not valid on channels without a phone
        number. |


        The recipient is checked before the message is queued:


        | code | when it happens |

        | --- | --- |

        | `ERR_API_CONTACT_NOT_IDENTIFIED` (`400`) | None of `ticketId`,
        `contactId` or `number` — or only `number` on a channel without a phone
        number. |

        | `ERR_API_CONTACT_NOT_FOUND` (`404`) | `contactId` does not exist or
        belongs to another company. |

        | `ERR_API_CONTACT_UNREACHABLE` (`422`) | The contact cannot receive on
        this channel: no previous conversation on it (channels without a phone
        number; on Webchat, no visitor session on this webchat) or no phone
        number (phone channels). |


        **Interactive on Webchat.** The webchat renders the same interactive
        messages as the official

        WhatsApp API that do not depend on a Meta resource: `button`, `list`,
        `cta_url`,

        `location_request_message` and `carousel`. On it, `interactive` is
        checked before the message is

        queued, with the WhatsApp limits, and the problem comes back right away:
        `422` with the code

        `ERR_API_INTERACTIVE_INVALID` and, in `message`, the field and the
        reason (for example

        `interactive.action.buttons: de 1 a 3 botões`). `flow`, `product`,
        `product_list`,

        `catalog_message`, `address_message`, `voice_call` and
        `call_permission_request` do not exist on

        webchat. On the other channels the provider does the checking, during
        the send.


        **Use `Idempotency-Key` here.** If the call times out and you retry
        without this key, the

        contact may receive the same message twice.


        **Media is checked before it enters the queue.** With `type: media`, the
        file is read during the

        call itself, and not during the background send — so a problematic file
        gets a `400` right away,

        instead of returning a `jobId` that would fail later with no warning:


        | code | when it happens |

        | --- | --- |

        | `ERR_API_MEDIA_EMPTY` | The uploaded file has 0 bytes. |

        | `ERR_API_MEDIA_TYPE_UNKNOWN` | There is no way to tell the type: the
        name came without an extension and the `Content-Type` of the multipart
        part was not provided. |

        | `ERR_API_MEDIA_TYPE_MISMATCH` | The file contents contradict the
        declared type (for example, a PDF sent as an image). |


        A difference only in format within the same category is not rejected: a
        PNG declared as JPEG is

        sent normally. The type used for sending is always the one from the file
        contents.
      parameters:
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            examples:
              texto:
                summary: Text
                value:
                  channelId: 4
                  type: text
                  number: '5511999999999'
                  contactName: Marina Alves
                  body: 'Olá, Marina! Seu pedido #88213 foi confirmado.'
              template:
                summary: Template
                value:
                  channelId: 4
                  type: template
                  number: '5511999999999'
                  template:
                    name: confirmacao_pedido
                    parameters:
                      - Marina
                      - '88213'
              porContato:
                summary: By contact (channel without a phone number)
                value:
                  channelId: 11
                  type: text
                  contactId: 842
                  body: Oi, Maria! Seu pedido saiu para entrega.
              dentroDeUmAtendimento:
                summary: Within a ticket
                value:
                  channelId: 4
                  type: text
                  ticketId: 9034
                  body: Seu pedido saiu para entrega.
                  closeTicket: true
                  wrapupTypeId: 12
              interativa:
                summary: Interactive
                value:
                  channelId: 4
                  type: interactive
                  number: '5511999999999'
                  interactive:
                    type: button
                    body:
                      text: Podemos confirmar a entrega para amanhã?
                    action:
                      buttons:
                        - type: reply
                          reply:
                            id: sim
                            title: Sim
                        - type: reply
                          reply:
                            id: nao
                            title: Reagendar
              interativaLista:
                summary: 'Interactive: list (webchat)'
                value:
                  channelId: 11
                  type: interactive
                  contactId: 842
                  interactive:
                    type: list
                    header:
                      type: text
                      text: Cardápio
                    body:
                      text: O que você vai pedir?
                    footer:
                      text: Entrega em até 40 min
                    action:
                      button: Ver cardápio
                      sections:
                        - title: Lanches
                          rows:
                            - id: x-burger
                              title: X-Burger
                              description: Bun, patty and cheese
                            - id: x-salada
                              title: X-Salada
                        - title: Bebidas
                          rows:
                            - id: suco
                              title: Suco natural
              interativaLink:
                summary: 'Interactive: link button'
                value:
                  channelId: 11
                  type: interactive
                  ticketId: 9034
                  interactive:
                    type: cta_url
                    header:
                      type: image
                      image:
                        link: https://cdn.loja.com.br/pedido.png
                    body:
                      text: Seu pedido saiu para entrega.
                    action:
                      name: cta_url
                      parameters:
                        display_text: Rastrear pedido
                        url: https://loja.com.br/pedidos/88213
              interativaLocalizacao:
                summary: 'Interactive: location request'
                value:
                  channelId: 11
                  type: interactive
                  ticketId: 9034
                  interactive:
                    type: location_request_message
                    body:
                      text: Envie sua localização para calcularmos o frete.
                    action:
                      name: send_location
              interativaCarrossel:
                summary: 'Interactive: carousel'
                value:
                  channelId: 11
                  type: interactive
                  ticketId: 9034
                  interactive:
                    type: carousel
                    body:
                      text: Ofertas da semana
                    action:
                      cards:
                        - card_index: 0
                          type: cta_url
                          header:
                            type: image
                            image:
                              link: https://cdn.loja.com.br/tenis.png
                          body:
                            text: Tênis de corrida
                          action:
                            name: cta_url
                            parameters:
                              display_text: Comprar
                              url: https://loja.com.br/tenis
                        - card_index: 1
                          type: button
                          header:
                            type: image
                            image:
                              link: https://cdn.loja.com.br/camiseta.png
                          body:
                            text: Camiseta dry-fit
                          action:
                            buttons:
                              - type: quick_reply
                                quick_reply:
                                  id: quero-camiseta
                                  title: Quero
            schema:
              type: object
              required:
                - channelId
              properties:
                channelId:
                  type: integer
                  description: >-
                    Channel where the conversation takes place. See the
                    available channels at `GET /channels`.
                  example: 4
                type:
                  type: string
                  enum:
                    - text
                    - media
                    - template
                    - interactive
                  description: >-
                    Type of message. It defines which other fields are required:


                    | value | what it requires |

                    | --- | --- |

                    | `text` | `body` with the text. |

                    | `media` | Sent as `multipart/form-data`, with the file in
                    `medias`. |

                    | `template` | `template` with name and parameters. Required
                    outside the WhatsApp 24h window. |

                    | `interactive` | `interactive` in the official WhatsApp API
                    format (buttons, list, link, location request, carousel). |
                  default: text
                number:
                  type: string
                  description: >-
                    Recipient on phone channels (WhatsApp, RCS): digits only,
                    with country and area code. If there is no contact with this
                    number yet, one is created. Not valid on channels without a
                    phone number.
                  example: '5511999999999'
                contactId:
                  type: integer
                  description: >-
                    Recipient by the id of a contact that already exists, on any
                    channel. Required on channels without a phone number
                    (Instagram, Facebook/Messenger, Webchat, Telegram) — on them
                    you can only message someone who has already talked to the
                    channel. The contact must belong to the token's company.
                    Takes precedence over `number`.
                  example: 842
                ticketId:
                  type: integer
                  description: >-
                    Sends inside a ticket that already exists. Takes precedence
                    over `contactId` and `number`.
                  example: 9034
                contactName:
                  type: string
                  description: >-
                    Name given to the contact if it is created by this send
                    (only with `number`).
                  example: Marina Alves
                body:
                  type: string
                  description: >-
                    Message text, or caption when `type: media`. Accepts system
                    variables (e.g. `{{name}}`).
                  example: 'Olá, Marina! Seu pedido #88213 foi confirmado.'
                template:
                  allOf:
                    - $ref: '#/components/schemas/TemplateInput'
                  description: >-
                    Required when `type: template`. Accepts the simple form (`{
                    name, parameters }`), which is the

                    recommended one: the API validates the template and builds
                    the message. It also accepts the fully built object with

                    `components`, for those who already work with that format.
                interactive:
                  type: object
                  description: >-
                    Required when `type: interactive`. The `interactive` object
                    of the official WhatsApp API (Cloud API),

                    the same on every channel that has interactive messages: the
                    payload you already send to official

                    WhatsApp works on Webchat with no changes.


                    On Webchat, the types and the limits checked during the
                    call:


                    | `type` | what it takes | limits |

                    | --- | --- | --- |

                    | `button` | `body`, `action.buttons[]` (`type: reply`,
                    `reply.id`, `reply.title`); optional `header` and `footer` |
                    1 to 3 buttons; title up to 20; id up to 256, no repeats |

                    | `list` | `body`, `action.button` (text of the button that
                    opens the list), `action.sections[].rows[]` (`id`, `title`,
                    `description`); optional text `header` and `footer` | 1 to
                    10 sections, 1 to 10 rows in total; button up to 20; row
                    title up to 24; description up to 72; section title up to 24
                    (required with more than one section); id up to 200 |

                    | `cta_url` | `body`, `action.name: cta_url`,
                    `action.parameters.display_text` and `url` (http or https);
                    optional `header` and `footer` | button text up to 20 |

                    | `location_request_message` | `body`, `action.name:
                    send_location` | no header |

                    | `carousel` | `body`, `action.cards[]`: `header` (image or
                    video), `body` and one link button (`action.name: cta_url`)
                    or reply buttons (`action.buttons[]`, `type: quick_reply`) |
                    1 to 10 cards |


                    On all of them: `body.text` is required, up to 1024
                    characters (list: 4096); text `header` up to 60;

                    `footer` up to 60. A media `header` (`image`, `video`,
                    `document`) only by public http(s) `link` —

                    the webchat does not use media by Meta id.


                    The contact's choice arrives as it does on WhatsApp: a text
                    message with the option title,

                    replying to (quoting) the interactive message.
                  example:
                    type: button
                    body:
                      text: Podemos confirmar a entrega para amanhã?
                    action:
                      buttons:
                        - type: reply
                          reply:
                            id: sim
                            title: Sim
                        - type: reply
                          reply:
                            id: nao
                            title: Reagendar
                queueId:
                  type: integer
                  description: Moves the ticket to this queue after sending.
                  example: 7
                openTicket:
                  type: boolean
                  description: Reopens the ticket if it is closed.
                closeTicket:
                  type: boolean
                  description: Closes the ticket after sending.
                wrapupTypeId:
                  type: integer
                  description: >-
                    Wrap-up recorded when the ticket is closed with
                    `closeTicket`.
                  example: 12
          multipart/form-data:
            schema:
              type: object
              required:
                - channelId
              properties:
                channelId:
                  type: integer
                  description: >-
                    Channel where the conversation takes place. See the
                    available channels at `GET /channels`.
                  example: 4
                type:
                  type: string
                  enum:
                    - text
                    - media
                    - template
                    - interactive
                  description: >-
                    Type of message. It defines which other fields are required:


                    | value | what it requires |

                    | --- | --- |

                    | `text` | `body` with the text. |

                    | `media` | Sent as `multipart/form-data`, with the file in
                    `medias`. |

                    | `template` | `template` with name and parameters. Required
                    outside the WhatsApp 24h window. |

                    | `interactive` | `interactive` in the official WhatsApp API
                    format (buttons, list, link, location request, carousel). |
                number:
                  type: string
                  description: Recipient on phone channels. See `number` in the JSON body.
                  example: '5511999999999'
                contactId:
                  type: integer
                  description: >-
                    Recipient by the id of an existing contact. See `contactId`
                    in the JSON body.
                  example: 842
                ticketId:
                  type: integer
                  description: Ticket the message belongs to.
                  example: 9034
                body:
                  type: string
                  example: 'Olá, Marina! Seu pedido #88213 foi confirmado.'
                medias:
                  type: string
                  format: binary
                  description: 'File to send. Required when `type: media`.'
      responses:
        '202':
          description: Accepted and queued for sending.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      jobId:
                        type: string
                        example: '1841'
                      status:
                        type: string
                        example: queued
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          description: >-
            The message is not queued. Two reasons:


            - `ERR_API_NO_WHATSAPP_CREDITS`: WhatsApp Business channel with
            WhatsApp billing enabled and
              the company out of credit. Top up the credits to send through this channel again. The minimum
              balance depends on the type: `template` requires a balance above the price of a marketing
              message; `text`, `media` and `interactive`, above the price of a service message.
            - `ERR_API_ACTIVE_CONTACTS_LIMIT_REACHED`: the company reached its
            monthly active contacts
              limit and the recipient has not exchanged messages with it this month yet. A contact counts
              once per month, at the first message exchanged (sent or received); contacts that already
              counted keep receiving normally. The count resets on the 1st. Does not apply to `web_whatsapp`
              channels.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                semCredito:
                  summary: Out of WhatsApp credit
                  value:
                    error:
                      code: ERR_API_NO_WHATSAPP_CREDITS
                      message: >-
                        a empresa está sem crédito de WhatsApp. Recarregue os
                        créditos para voltar a enviar por este canal.
                limiteContatosAtivos:
                  summary: Monthly active contacts limit
                  value:
                    error:
                      code: ERR_API_ACTIVE_CONTACTS_LIMIT_REACHED
                      message: >-
                        a empresa atingiu o limite de contatos ativos do mês e
                        este contato ainda não conversou com ela neste mês.
                        Contrate mais contatos ativos ou aguarde a virada do
                        mês.
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
components:
  parameters:
    idempotencyKey:
      name: Idempotency-Key
      in: header
      description: >-
        Unique key for the operation. If you repeat the call with the same key
        and the same body, you get the stored response.
      schema:
        type: string
        maxLength: 200
      example: pedido-88213-confirmacao
  schemas:
    TemplateInput:
      type: object
      description: >-
        The **simple** way to send a template. You provide the name and the
        parameters, and the system finds the template on the channel, checks
        that the parameters match what it requires and builds the message in
        Meta's format.
      required:
        - name
      properties:
        name:
          type: string
          description: >-
            Exact name of the approved template. You can look it up at `GET
            /channels/{id}/templates`.
          example: confirmacao_pedido
        language:
          type: string
          description: >-
            Template language. Only needed when there is more than one template
            with the same name in different languages.
          example: pt_BR
        parameters:
          type: object
          properties:
            body:
              type: array
              description: One item per `{{n}}` variable in the body, in order.
              items:
                type: string
              example:
                - Maria
                - '12345'
            header:
              type: array
              description: >-
                One item per variable in a text header. A media header does not
                use this field.
              items:
                type: string
          example:
            - Marina
            - '88213'
      example:
        name: confirmacao_pedido
        parameters:
          body:
            - Maria
            - '12345'
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Fixed error code, so your system can handle each case.
              example: ERR_API_FORBIDDEN_SCOPE
            message:
              type: string
            details: {}
  responses:
    BadRequest:
      description: Invalid request body.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: ERR_API_VALIDATION
              message: O campo `number` é obrigatório.
    NotFound:
      description: >-
        The resource does not exist or belongs to another company. For security,
        the response is the same in both cases.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: ERR_API_NOT_FOUND
              message: Registro não encontrado.
    Conflict:
      description: '`Idempotency-Key` conflict.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: ERR_API_IDEMPOTENCY_KEY_REUSED
              message: Idempotency-Key já usada com um corpo diferente.
    UnprocessableEntity:
      description: >-
        The body is well formed, but a required field is missing or a value is
        not accepted. For example, `application` other than `contact`/`card`, an
        empty `orderedIds` or a blank `name`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: ERR_API_VALIDATION
              message: >-
                O parâmetro `application` é obrigatório e aceita "contact" ou
                "card".
  securitySchemes:
    apiToken:
      type: http
      scheme: bearer
      description: API token in the `sk_live_…` format.

````

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