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

# Send a message

> One call for a template, media, or plain text. The action is chosen by the payload:
a template wins, then `media`, then `text`.

Address the message either with `conversationId` (an existing thread) or with
`channelId` + `to` (finds the thread, or starts one).

**Outside the 24-hour customer service window, WhatsApp only accepts templates.**
A plain-text send to a cold contact will be rejected by the platform.

`to` must be in E.164 with no `+` — `201001234567`, not `01001234567`. A local
number without a country code will not be delivered.




## OpenAPI

````yaml /openapi/public-api.yaml post /v1/conversations/send
openapi: 3.1.0
info:
  title: Linkiasoft API
  version: 1.0.0
  description: >
    Send and read messages across WhatsApp, Instagram, Messenger and Telegram,
    and read

    and update the CRM behind them — leads, pipeline stages, company contacts
    and activity

    history.


    Every endpoint is authenticated with a tenant-scoped API key and returns
    only data

    belonging to that key's workspace.


    **Two hosts, one key.** Messaging lives on `social.linkiasoft.com` and the
    CRM on

    `api.linkiasoft.com`. Each endpoint below shows its own base URL; the same
    API key

    authenticates against both.
servers:
  - url: https://social.linkiasoft.com/api
    description: Production
security:
  - apiKey: []
    tenantId: []
tags:
  - name: Conversations
    description: Read threads and send messages.
  - name: Templates
    description: Approved WhatsApp message templates.
  - name: Channels
    description: The connected accounts you can send from.
  - name: Leads
    description: Deals moving through your pipeline.
  - name: Pipeline
    description: The stages a lead can sit in.
  - name: Contacts
    description: People attached to a company.
  - name: Activities
    description: Logged calls, emails, meetings and follow-ups.
  - name: Customers
    description: The companies and accounts your contacts belong to.
  - name: Tags
    description: Labels applied to customers, contacts and leads.
  - name: Quick replies
    description: Team-shared canned responses.
  - name: WhatsApp groups
    description: Create and manage WhatsApp group chats.
paths:
  /v1/conversations/send:
    post:
      tags:
        - Conversations
      summary: Send a message
      description: >
        One call for a template, media, or plain text. The action is chosen by
        the payload:

        a template wins, then `media`, then `text`.


        Address the message either with `conversationId` (an existing thread) or
        with

        `channelId` + `to` (finds the thread, or starts one).


        **Outside the 24-hour customer service window, WhatsApp only accepts
        templates.**

        A plain-text send to a cold contact will be rejected by the platform.


        `to` must be in E.164 with no `+` — `201001234567`, not `01001234567`. A
        local

        number without a country code will not be delivered.
      operationId: sendMessage
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendRequest'
            examples:
              template:
                summary: Template (works outside the 24h window)
                value:
                  channelId: 3f2b1a9c-0d4e-4a7b-9c1e-8f6a2b3c4d5e
                  to: '201001234567'
                  contactName: Ahmed Hassan
                  template:
                    templateName: ticket_resolved
                    templateLanguage: ar
                    templateParams:
                      - Ahmed
                      - '4821'
              templateFlat:
                summary: Template, flat form (for callers that cannot nest JSON)
                description: >
                  Some integrations — Zoho Desk workflow webhooks, form posts —
                  can only

                  emit flat key/value pairs. These fields are equivalent to the
                  nested

                  `template` object. `templateParams` also accepts a JSON array
                  string

                  (`["a","b"]`) or a comma-separated list; use the JSON form
                  when a value

                  itself contains a comma.
                value:
                  channelId: 3f2b1a9c-0d4e-4a7b-9c1e-8f6a2b3c4d5e
                  to: '201001234567'
                  templateName: ticket_resolved
                  templateLanguage: ar
                  templateParams: Ahmed,4821
              text:
                summary: Plain text into an existing thread
                value:
                  conversationId: 7c8d9e0f-1a2b-3c4d-5e6f-7a8b9c0d1e2f
                  text: Thanks — that's all sorted.
      responses:
        '202':
          description: Accepted and queued for delivery.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Message'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  schemas:
    SendRequest:
      type: object
      description: >
        Provide `conversationId`, or `channelId` + `to`. Then exactly one of a
        template,

        `media`, or `text`.
      properties:
        conversationId:
          type: string
          format: uuid
          description: An existing thread to send into.
        channelId:
          type: string
          format: uuid
          description: Which connected account to send from. Required with `to`.
        to:
          type: string
          description: Recipient in E.164 without `+`, e.g. `201001234567`.
          examples:
            - '201001234567'
        contactName:
          type: string
          description: Display name to store if this starts a new thread.
        text:
          type: string
          description: Plain text body. Ignored when a template or `media` is present.
        media:
          $ref: '#/components/schemas/SendMedia'
        template:
          $ref: '#/components/schemas/SendTemplate'
        templateName:
          type: string
          description: Flat alias for `template.templateName`.
        templateLanguage:
          type: string
          description: Flat alias for `template.templateLanguage`.
        templateParams:
          description: >
            Flat alias for `template.templateParams`. Accepts an array, a JSON
            array

            string, or a comma-separated list.
          oneOf:
            - type: array
              items:
                type: string
            - type: string
    Message:
      type: object
      properties:
        id:
          type: string
          format: uuid
        conversation_id:
          type: string
          format: uuid
        direction:
          type: string
          enum:
            - inbound
            - outbound
        type:
          type: string
          description: Drives how `text` and `media` are interpreted.
          enum:
            - text
            - image
            - video
            - audio
            - document
            - sticker
            - location
            - contact
            - poll
            - reaction
            - template
        text:
          type:
            - string
            - 'null'
          description: >
            The body for text; the caption for image/video/document; the emoji
            for a

            reaction; the place name for a location; null for audio and
            stickers.
        media:
          type:
            - object
            - 'null'
          description: Type-specific metadata. Null for plain text.
          additionalProperties: true
        status:
          type: string
          description: >
            Delivery state. `failed` is terminal and `error_reason` explains it.
            Read

            status only appears when the contact has read receipts enabled.
          enum:
            - pending
            - sent
            - delivered
            - read
            - failed
        error_reason:
          type:
            - string
            - 'null'
        sender_name:
          type:
            - string
            - 'null'
        from_me:
          type: boolean
          description: True when we sent it.
        platform_message_id:
          type: string
          description: The platform's own id. Empty until the send is acknowledged.
        created_at:
          type: string
          format: date-time
    SendMedia:
      type: object
      required:
        - mediaUrl
        - type
      properties:
        mediaUrl:
          type: string
          format: uri
          description: >-
            Publicly reachable URL. We fetch it; it is not uploaded through this
            API.
        type:
          type: string
          enum:
            - image
            - video
            - audio
            - document
        caption:
          type: string
        fileName:
          type: string
          description: Filename shown for documents.
    SendTemplate:
      type: object
      required:
        - templateName
        - templateLanguage
      properties:
        templateName:
          type: string
          description: An `APPROVED` template name from `GET /v1/templates`.
        templateLanguage:
          type: string
          description: >-
            Language code the template was approved under, e.g. `en`, `en_US`,
            `ar`.
        templateParams:
          type: array
          items:
            type: string
          description: >
            Values for the body placeholders `{{1}}`, `{{2}}`, … in order. The
            count must

            match the template exactly or the send is rejected.
    Error:
      type: object
      properties:
        statusCode:
          type: integer
        message:
          description: A sentence explaining the failure, or a list of field errors.
          oneOf:
            - type: string
            - type: array
              items:
                type: string
        error:
          type: string
      examples:
        - statusCode: 403
          message: 'API key missing required scope(s): conversations:read'
          error: Forbidden
  responses:
    BadRequest:
      description: The request was malformed. The `message` says what to fix.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: The API key is missing, invalid, revoked, or expired.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >
        The key is valid but lacks the scope for this endpoint, or the workspace
        has hit a

        plan limit (for example its monthly active-contact quota).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: >
        No such record in your workspace. Records belonging to another workspace
        are

        reported as not found rather than forbidden.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: >
        Your workspace API key. Create one in **Settings → API Keys**; the
        plaintext value

        is shown once and never again.


        Keys are scoped. A key needs `conversations:send` to send,
        `conversations:read` to

        read threads and messages, `templates:read` to list templates, and
        `channels:read`

        to list channels. A key missing the scope for an endpoint gets `403`.


        The CRM endpoints use the same `resource:action` form: `leads:read`,
        `leads:create`,

        `leads:update`, `leads:delete`, `customers:read`, `customers:create`,

        `customers:update`, `customers:delete`, `customers:manage`.


        Several resources have no scope of their own and borrow one:


        | Resource | Governed by |

        |---|---|

        | Contacts | `customers:*` |

        | Activities | `customers:*` for customer activities, `leads:*` for lead
        activities |

        | Tags | `customers:read` to list, `customers:manage` to change |

        | Pipeline stages | `leads:*` |

        | Quick replies | `quick-replies:read` / `quick-replies:write` |

        | WhatsApp groups | `wa-groups:manage` |


        Grant only what the integration needs — a key that only sends
        notifications should

        hold `conversations:send` and nothing else.
    tenantId:
      type: apiKey
      in: header
      name: X-Tenant-Id
      description: >
        Your workspace UUID. Required on every request **in addition to**
        `x-api-key`.


        It does not select the workspace — data access is always scoped to the
        workspace

        that owns the API key, and a mismatched value cannot read anyone else's
        data.

````