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

# Send a message (API key)

> Send a WhatsApp or Instagram message on a channel with your `hmok_` API key.
The body is the Meta message content object including the recipient
(`to` for WhatsApp, `recipient.id` for Instagram). HookMyApp resolves the
channel token server-side. Accepts an API key scoped to `messages.send`
or `channel.manage`. Delivery is at-least-once: do not retry a request
whose response you did not receive without checking first.


Use `Authorization: Bearer hmok_...` (an API key, or a dashboard session) instead of a channel token. Accepted scopes are `messages.send` or `channel.manage`.

Delivery is at-least-once: don't automatically retry after an ambiguous failure (timeout, dropped connection). Check the recipient's thread first to see whether the message already went out.

If you need the raw Graph request, see the [`hmat_` gateway twin](/instagram/api/messages/send-message).


## OpenAPI

````yaml api-reference/openapi.yaml POST /channels/{ch}/messages
openapi: 3.1.0
info:
  title: HookMyApp API
  version: 1.0.0
  description: >
    The HookMyApp REST API. Everything the dashboard and the CLI can do, your

    code and your AI agents can do too.


    ## Authentication


    Two kinds of Bearer credentials exist. Do not mix them up:


    - **API key (`hmok_...`)** authenticates *you* (or your agent) to
      `https://api.hookmyapp.com`.
      Create one in the dashboard under **Org → API keys → Create API Key**
      (full org access, optional expiration date, reveal or
      revoke it any time from that page), or without a
      browser via the [agent auth flow](#tag/agent-auth) described in
      [`GET /auth.md`](https://api.hookmyapp.com/auth.md). That flow can mint a
      scope-limited key. Send it as `Authorization: Bearer hmok_...`, or on
      `/mcp` as `X-API-Key: hmok_...`. Use it for org, workspace, customer,
      channel and webhook management.
    - **Channel token (`hmat_...`)** sends messages, replies to comments, and
      publishes from one connected channel.
      Mint it with `GET /meta/channels/{id}/token`. It is not valid on
      `https://api.hookmyapp.com`.
      The same sends, replies and publishing are also available with the API key under
      `/channels/{ch}` (see the Channel actions tag).

    Browser sessions from the dashboard use the same endpoints with a session

    cookie or WorkOS JWT instead of `hmok_`.


    ## IDs


    Every ID on the wire is a typed public ID, never an internal UUID:

    `ws_` workspace/customer, `ch_` channel, `org_` organization,

    `cred_` connection credential, `ac_` agent credential.


    ## Workspace context


    Workspace-scoped routes (channels, webhook config, and deliveries) resolve
    the

    workspace from the `X-Workspace-Id: ws_XXXXXXXX` header.


    ## Errors


    Errors return a stable machine-readable `code` plus a human `message`.

    Agent tokens that exceed their granted scopes get `403
    AGENT_SCOPE_INSUFFICIENT`.


    ## Webhook signatures


    Deliveries to your webhook are signed with `X-HookMyApp-Signature-256`,

    an HMAC-SHA256 of the raw body keyed on the channel `WEBHOOK_HMAC_SECRET`.

    The Verify Token is a separate value used only for the GET ownership probe.
servers:
  - url: https://api.hookmyapp.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Agent auth
    description: Register an agent credential (`hmok_`) with email OTP, no browser needed
  - name: Organizations
    description: Your organization and its summary
  - name: Customers
    description: Customer workspaces you run messaging for (SaaS Mode)
  - name: Onboarding links
    description: Links your customers open to connect their own channels
  - name: Workspaces
    description: Team workspaces
  - name: Channels
    description: Connected WhatsApp and Instagram channels, and their channel tokens
  - name: Webhook config
    description: Where inbound events are delivered for a channel
  - name: Deliveries
    description: Inspect inbound delivery logs and app responses
  - name: Channel actions
    description: >-
      Send, reply, moderate and publish on a channel with the API key; the
      channel token stays server-side
paths:
  /channels/{ch}/messages:
    post:
      tags:
        - Channel actions
      summary: Send a message (API key)
      description: >
        Send a WhatsApp or Instagram message on a channel with your `hmok_` API
        key.

        The body is the Meta message content object including the recipient

        (`to` for WhatsApp, `recipient.id` for Instagram). HookMyApp resolves
        the

        channel token server-side. Accepts an API key scoped to `messages.send`

        or `channel.manage`. Delivery is at-least-once: do not retry a request

        whose response you did not receive without checking first.
      operationId: sendChannelMessage
      parameters:
        - name: ch
          in: path
          required: true
          schema:
            type: string
          description: ch_ channel id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - message
              properties:
                message:
                  type: object
                  additionalProperties: true
                  description: Meta message content object, recipient included
            example:
              message:
                to: '15551234567'
                type: text
                text:
                  body: Hello
      responses:
        '200':
          description: Sent
          content:
            application/json:
              schema:
                type: object
                required:
                  - channelId
                  - messageId
                  - status
                properties:
                  channelId:
                    type: string
                  messageId:
                    type:
                      - string
                      - 'null'
                  status:
                    type: string
                    enum:
                      - sent
        '400':
          $ref: '#/components/responses/Error'
        '401':
          $ref: '#/components/responses/Error'
        '403':
          $ref: '#/components/responses/Error'
        '404':
          $ref: '#/components/responses/Error'
components:
  responses:
    Error:
      description: Error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    Error:
      type: object
      properties:
        statusCode:
          type: integer
          example: 403
          description: HTTP status code
          mirrors the response status: null
        code:
          type: string
          description: Stable machine-readable code, e.g. `AGENT_SCOPE_INSUFFICIENT`
        message:
          type: string
        requestId:
          type: string
          description: Include this when contacting support about the request
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: '`Authorization: Bearer hmok_...` API key or a dashboard session token'

````