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

# Get WhatsApp Messages

> Conversation history for one contact, in the exact form the AI agent is shown it. Scoped to the account behind the API key.

<Note>
  Returns the conversation history for one contact, in the exact form your AI agent is shown it. Only `user_phone_number` is required.
</Note>

## Quick Example

```bash theme={null}
curl -X GET "https://api.callkaro.ai/whatsapp/messages?user_phone_number=919876543210&limit=50" \
  -H "X-API-KEY: YOUR_API_KEY"
```

Restrict it to one of your numbers:

```bash theme={null}
curl -X GET "https://api.callkaro.ai/whatsapp/messages?user_phone_number=919876543210&agent_phone_number=919000000000" \
  -H "X-API-KEY: YOUR_API_KEY"
```

## Response

Messages come back **oldest first**, the same order the conversation is fed to the model.

```json theme={null}
{
  "status": "success",
  "message": "Messages fetched",
  "data": {
    "user_phone_number": "919876543210",
    "agent_phone_number": "919000000000",
    "limit": 50,
    "count": 3,
    "messages": [
      {
        "name": "inbound",
        "time": "2026-03-01 10:01:00",
        "status": "read",
        "type": "text",
        "body": "Where is my order?",
        "msg_id": "wamid.AAA"
      },
      {
        "name": "outbound",
        "time": "2026-03-01 10:02:00",
        "status": "delivered",
        "type": "template",
        "body": "[Template] Hi Rahul, your order #1234 has shipped. — Buttons: Track | Support",
        "msg_id": "wamid.BBB"
      },
      {
        "name": "outbound",
        "time": "2026-03-01 10:03:00",
        "status": "sent",
        "type": "text",
        "body": "It should arrive tomorrow. [Tool calls: get_order_status({\"id\":\"1234\"})->shipped]",
        "msg_id": "wamid.CCC",
        "tool_calls": [
          { "name": "get_order_status", "arguments": "{\"id\":\"1234\"}", "content": "shipped" }
        ]
      }
    ]
  }
}
```

## Fields

<ResponseField name="name" type="string">
  `inbound` (from the contact) or `outbound` (from your agent).
</ResponseField>

<ResponseField name="time" type="string">
  `YYYY-MM-DD HH:MM:SS`, IST.
</ResponseField>

<ResponseField name="status" type="string">
  Whether the message reached the user. See the table below — `sent` does **not** mean it arrived.
</ResponseField>

<ResponseField name="type" type="string">
  `text`, `template`, `interactive`, `image`, `video`, `audio`, `document`, `contact`, and so on.
</ResponseField>

<ResponseField name="body" type="string">
  The message **rendered the way the agent sees it**. A template arrives as readable text rather than a raw components blob, so you do not have to reassemble it — see below.
</ResponseField>

<ResponseField name="msg_id" type="string">
  WhatsApp's `wamid`. Empty for messages WhatsApp never accepted.
</ResponseField>

<ResponseField name="content_link" type="string">
  Present only on media messages: the stored file URL.
</ResponseField>

<ResponseField name="tool_calls" type="array">
  Present only on outbound messages where the agent called functions. Also summarised inside `body`, because that is how the model sees it — this field is the structured version so you do not have to parse it back out.
</ResponseField>

## What `status` means

Only the first three appear in this response.

| Status                | Reached the user? | Meaning                                                                                       |
| --------------------- | ----------------- | --------------------------------------------------------------------------------------------- |
| `read`                | **Yes**           | Delivered and opened by the user                                                              |
| `delivered`           | **Yes**           | On the user's device, not yet opened                                                          |
| `sent`                | **Not yet**       | Accepted by WhatsApp but not on the user's device — phone off, no network, or still in flight |
| `failed` / `not sent` | **No**            | WhatsApp rejected it or could not deliver. Excluded from this response                        |

<Warning>
  `sent` is the one to watch: it is returned, but it means the message has **not** reached the user. Treat only `delivered` and `read` as arrived.
</Warning>

## How `body` is built

Non-text messages are flattened into one readable string, using the same renderer that builds the model's context. That is deliberate: **what this endpoint returns and what the agent was given cannot drift apart.**

| Stored type                              | `body` you receive                                        |
| ---------------------------------------- | --------------------------------------------------------- |
| `text`                                   | the text itself                                           |
| `template`                               | `[Template] <body text> — Buttons: A \| B — <footer>`     |
| `interactive`                            | `[Interactive] <body text> — Buttons: A \| B`             |
| `image` / `video` / `audio` / `document` | `[Image]` and the caption, with the URL in `content_link` |
| `location`                               | `[Location shared] …` plus a Google Maps link             |
| any outbound with functions              | the reply text, then `[Tool calls: name(args)->result]`   |

## What is excluded

To match the agent's view exactly, the following never appear:

* **Internal records** — `developer` rows (system notes, scheduling markers) are not messages anyone sent
* **Messages that never reached the user** — `failed` and `not sent` are excluded. `sent` *is* returned, but see the status table above: it means WhatsApp accepted the message, not that it arrived
* **Anything before a chat reset** — if the conversation was cleared, messages from before that point are excluded, exactly as they are for the agent

<Note>
  `limit` defaults to **50** and is capped at **200**. Values above the cap are silently reduced rather than rejected.
</Note>

## Use Cases

* **Show history in your own UI** without building a renderer for every WhatsApp message type
* **Hand context to another system** — a CRM note, a support ticket, a human handover
* **Debug an agent reply** by reading the exact context it was given, including the tool calls it made

<Warning>
  Scoped to the account behind the API key — it can only ever read your own conversations. Passing another account's number returns an empty list, not an error.
</Warning>


## OpenAPI

````yaml GET /whatsapp/messages
openapi: 3.1.0
info:
  title: CallKaro AI API
  description: Campaign and outbound call management API for CallKaro AI voice agents
  version: 1.0.0
  contact:
    name: CallKaro AI Support
    email: support@callkaro.ai
    url: https://callkaro.ai
servers:
  - url: https://api.callkaro.ai
    description: Production server
security:
  - ApiKeyAuth: []
paths:
  /whatsapp/messages:
    get:
      tags:
        - Endpoints
      summary: Get WhatsApp Messages
      description: >-
        Conversation history for one contact, in the exact form the AI agent is
        shown it. Scoped to the account behind the API key.
      operationId: getWhatsAppMessages
      parameters:
        - name: user_phone_number
          in: query
          required: true
          schema:
            type: string
          description: The contact's number, digits only including country code.
          example: '919876543210'
        - name: agent_phone_number
          in: query
          required: false
          schema:
            type: string
          description: >-
            Restrict to one of your WhatsApp numbers. Omit to return this
            contact's conversation across every number of yours.
          example: '919000000000'
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 50
            maximum: 200
            minimum: 1
          description: >-
            How many messages to return, newest kept. Values above 200 are
            capped at 200.
      responses:
        '200':
          description: Messages, oldest first
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WhatsAppMessagesResponse'
              example:
                status: success
                message: Messages fetched
                data:
                  user_phone_number: '919876543210'
                  agent_phone_number: '919000000000'
                  limit: 50
                  count: 3
                  messages:
                    - name: inbound
                      time: '2026-03-01 10:01:00'
                      status: read
                      type: text
                      body: Where is my order?
                      msg_id: wamid.AAA
                    - name: outbound
                      time: '2026-03-01 10:02:00'
                      status: delivered
                      type: template
                      body: >-
                        [Template] Hi Rahul, your order #1234 has shipped. -
                        Buttons: Track | Support
                      msg_id: wamid.BBB
                    - name: outbound
                      time: '2026-03-01 10:03:00'
                      status: sent
                      type: text
                      body: >-
                        It should arrive tomorrow. [Tool calls:
                        get_order_status({"id":"1234"})->shipped]
                      msg_id: wamid.CCC
                      tool_calls:
                        - name: get_order_status
                          arguments: '{"id":"1234"}'
                          content: shipped
        '400':
          description: Invalid or missing parameters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                status: error
                message: user_phone_number is required
        '401':
          description: Missing or invalid X-API-KEY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                status: error
                message: Invalid API key
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - ApiKeyAuth: []
components:
  schemas:
    WhatsAppMessagesResponse:
      type: object
      properties:
        status:
          type: string
          example: success
        message:
          type: string
          example: Messages fetched
        data:
          type: object
          properties:
            user_phone_number:
              type: string
            agent_phone_number:
              type: string
              description: Echoes the filter used. Empty when you did not pass one.
            limit:
              type: integer
            count:
              type: integer
            messages:
              type: array
              items:
                $ref: '#/components/schemas/WhatsAppMessage'
    ErrorResponse:
      type: object
      properties:
        status:
          type: string
          example: error
        message:
          type: string
          description: Error description
          example: 'Missing required field: to_number'
    WhatsAppMessage:
      type: object
      properties:
        name:
          type: string
          enum:
            - inbound
            - outbound
          description: Direction. inbound = from the contact, outbound = from your agent.
        time:
          type: string
          description: When the message was created, IST, 'YYYY-MM-DD HH:MM:SS'.
        status:
          type: string
          enum:
            - sent
            - delivered
            - read
          description: >-
            Whether the message reached the user. read = delivered and opened;
            delivered = on the user's device; sent = accepted by WhatsApp but
            NOT yet on the user's device. failed / not sent never reached the
            user and are excluded from this endpoint.
        type:
          type: string
          description: >-
            text, template, interactive, image, video, audio, document, contact,
            ...
        body:
          type: string
          description: >-
            The message rendered exactly as the AI agent is shown it. A template
            arrives as readable text (e.g. "[Template] Your order shipped. -
            Buttons: Track | Support") rather than a raw components blob.
        msg_id:
          type: string
          description: >-
            WhatsApp message ID (wamid). Empty for messages WhatsApp never
            accepted.
        content_link:
          type: string
          description: 'Present only for media messages: the stored URL.'
        tool_calls:
          type: array
          items:
            type: object
          description: >-
            Present only on outbound messages where the agent called functions.
            Also summarised inside body.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY
      description: API key from https://callkaro.ai/dashboard/api-key

````