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

# List messages

> List messages of this project, newest first.



## OpenAPI

````yaml /api-reference/v2/openapi.json get /v2/messages
openapi: 3.0.3
info:
  title: Zavu API v2
  version: 2.0.0
  description: >-
    The second version of the Zavu API. It runs on the same engine as /v1: the
    same API keys, the same per-project rate limit, the same sending limits,
    prices and reviews. /v1 keeps working unchanged, and you can mix calls to
    both.


    **Addresses, not senders.** /v2 has no sender object. Where a message leaves
    from is named the way the recipient sees it: an E.164 number, an email
    address, `@botname` for Telegram, `@username` for Instagram, the Facebook
    Page id for Messenger, and the literal `sms_oneway` for one-way SMS (sent
    from shared numbers, never a number of your own). `GET /v2/channels` lists
    one entry per (address, type); a number that carries WhatsApp and SMS is two
    entries with the same address.


    **What else changes.** No `Zavu-Sender` header (`from` or `conversationId`
    instead); every send reports how it was routed; one project webhook carries
    every event; every error is `{code, message, details}` at the root.


    **Wrong method.** A path under `/v2/messages`, `/v2/conversations`,
    `/v2/contacts`, `/v2/channels`, `/v2/templates`, `/v2/campaigns`,
    `/v2/agents`, `/v2/webhook` or `/v2/email-domains` called with a method it
    does not take answers `405 method_not_allowed`, with `details.allowed` and
    an `Allow` header.


    **Money.** Every amount is USD, rounded to 6 decimals.
servers:
  - url: https://api.zavu.dev
security:
  - bearerAuth: []
tags:
  - name: Messages
    description: Send, list and read messages.
  - name: Conversations
    description: Inbox threads, one per contact across channels.
  - name: Contacts
    description: Contacts and the identifiers they are reached at.
  - name: Channels
    description: 'Where the project can send from: one entry per (address, type).'
  - name: Templates
    description: WhatsApp message templates.
  - name: Campaigns
    description: One message to many recipients.
  - name: Webhook
    description: The one project webhook for every event.
  - name: Agents
    description: AI agents and the addresses they answer on.
  - name: Numbers
    description: Search and buy phone numbers.
  - name: Email Domains
    description: Domains to send email from.
  - name: Functions
    description: Configure Zavu Functions.
paths:
  /v2/messages:
    get:
      tags:
        - Messages
      summary: List messages
      description: List messages of this project, newest first.
      operationId: listMessagesV2
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum:
              - queued
              - sending
              - sent
              - delivered
              - failed
              - received
        - name: to
          in: query
          schema:
            type: string
        - name: channel
          in: query
          schema:
            $ref: '#/components/schemas/ChannelType'
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            maximum: 100
        - name: cursor
          in: query
          description: Opaque `nextCursor` from the previous page.
          schema:
            type: string
      responses:
        '200':
          description: Messages, newest first.
          content:
            application/json:
              schema:
                type: object
                required:
                  - items
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/Message'
                  nextCursor:
                    type: string
                    nullable: true
        '400':
          description: >-
            Invalid query parameter (`details.errors`), e.g. an unknown `status`
            or `channel`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - bearerAuth: []
components:
  schemas:
    ChannelType:
      type: string
      description: A transport a message travels on.
      enum:
        - sms
        - sms_oneway
        - whatsapp
        - telegram
        - email
        - instagram
        - messenger
        - voice
    Message:
      type: object
      required:
        - id
        - to
        - direction
        - status
        - channel
        - messageType
        - createdAt
      properties:
        id:
          type: string
          example: jd7x2k3m4n5p6q7r8s9t0
        to:
          type: string
          example: '+56912345678'
        from:
          type: string
          example: '+13125551212'
          description: >-
            Where the message came from: for an outbound message, your address
            (as `GET /v2/channels` lists it for `channel`, except WhatsApp,
            where it is the sender's own number field); for an inbound one, the
            contact.
        channel:
          $ref: '#/components/schemas/ChannelType'
        direction:
          type: string
          description: >-
            Who sent the message. Needed to render a thread: `status` cannot
            tell the two apart, because an inbound message is also stored as
            `delivered`.
          enum:
            - inbound
            - outbound
        messageType:
          $ref: '#/components/schemas/MessageType'
        status:
          $ref: '#/components/schemas/MessageStatus'
        text:
          type: string
          description: Text content or caption.
        content:
          $ref: '#/components/schemas/MessageContent'
        conversationId:
          type: string
          description: >-
            ID of the conversation (inbox thread) this message belongs to. Use
            it to build a direct dashboard link:
            `https://dashboard.zavu.dev/{locale}/inbox?conv={conversationId}`.
            Omitted only on legacy messages created before conversation
            threading.
          example: js723987cyghwqxxaxcf590qd18axd95
        providerMessageId:
          type: string
          description: Message ID from the delivery provider.
        errorCode:
          type: string
          nullable: true
          description: >-
            Why a failed message failed, as a Zavu code: `RISK_REJECTED`,
            `DAILY_LIMIT_EXCEEDED`, `TEN_DLC_REQUIRED`, `SMS_NOT_DELIVERED`,
            `WHATSAPP_WINDOW_CLOSED`... A delivery provider's own code is
            translated where its meaning is known and kept in
            `errorDetails.providerCode`; one with no known meaning is
            `SMS_DELIVERY_FAILED`, `WHATSAPP_REJECTED`, `VOICE_DELIVERY_FAILED`,
            `EMAIL_DELIVERY_FAILED` or `DELIVERY_FAILED`. (/v1 returns the
            provider's raw code here.)
        errorMessage:
          type: string
          nullable: true
        cost:
          type: number
          nullable: true
          description: >-
            Zavu's own per-message platform charge, in USD (6 decimals).
            Messaging is billed against the plan's allowance, so it is often 0.
        costProvider:
          type: number
          nullable: true
          description: >-
            What delivering the message cost Zavu (carrier cost), in USD. Filled
            in when the delivery report brings it, so null until then.
        costTotal:
          type: number
          nullable: true
          description: >-
            `cost` + `costProvider`. For SMS this is BEFORE Zavu's markup, so it
            is not what your balance was charged: read `charged` for that. For
            voice it includes the markup.
        metadata:
          type: object
          additionalProperties:
            type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        attemptedChannels:
          type: array
          items:
            $ref: '#/components/schemas/ChannelType'
          description: >-
            Transports tried before the current `channel`, oldest first, when an
            automatic fallback moved the message (WhatsApp to SMS). Empty when
            no fallback happened. Fallback runs after the send returned, so this
            can change after `POST /v2/messages` answered.
        riskHold:
          type: string
          nullable: true
          enum:
            - held
            - released
            - null
          description: >-
            `held` while the message waits for risk review (it stays `queued`),
            `released` once a reviewer let it go, null when it was never held.
        errorDetails:
          type: object
          nullable: true
          description: Present when `errorCode` translates a delivery provider's code.
          properties:
            providerCode:
              type: string
              description: The provider's own code, unchanged.
              example: '3'
        charged:
          type: number
          nullable: true
          description: >-
            What this message has taken from your prepaid balance, in USD. SMS
            and one-way SMS only: charged per segment when the message is
            accepted (`routing.estimatedCost`), raised if the delivery report
            brings a higher real cost, and refunded (0) when the message fails
            AND the carrier charged nothing. A failed SMS the carrier did charge
            for (it attempted delivery) stays charged. Null on other channels,
            which are not billed per message here.
    Error:
      type: object
      required:
        - code
        - message
      description: >-
        Every v2 error has this shape at the root. There is no `{error: {...}}`
        wrapper anywhere in v2. A missing resource is `404 not_found` with the
        message `<Resource> not found`; an address the project does not have is
        `404 not_found`, `None of this project's channels has the address
        <address>.`, with the address in `details` under the name the request
        used (`from` or `address`). A `message` is written for the caller and
        never carries a stack trace or a source path. A body that is not JSON is
        `400 invalid_json`, with `details.errors` (`path: []`, the parse error
        in `message`).
      properties:
        code:
          type: string
          example: invalid_request
        message:
          type: string
        details:
          type: object
          additionalProperties: true
          description: >-
            Machine-readable context. Keys that mean the same thing everywhere:

            - `errors`: present on every `400 invalid_request` for an invalid
            body or query parameter, `[{path, message, ...}]`, `path` naming the
            field (`["to"]`, `["contacts", 2, "recipient"]`, `["limit"]`).

            - `recommendation`: one sentence saying what to do next.

            - `dashboardUrl`: the dashboard screen that fixes it.

            - `connect`: on `409 no_channel`, the channels this project can
            connect to reach the recipient, each `{type, request, note,
            available, dashboardUrl?}`; `request` is the exact call (`POST
            /v2/channels` with its body, or `POST /v2/numbers`), or null for a
            channel connected from the dashboard (WhatsApp, Instagram,
            Messenger). `available: false` says why in `note` (one-way SMS
            closed until business verification is approved).

            - `address` / `from`: the address the error is about, under the name
            the request used for it.
          properties:
            errors:
              type: array
              items:
                type: object
                required:
                  - path
                  - message
                properties:
                  path:
                    type: array
                    items:
                      oneOf:
                        - type: string
                        - type: integer
                  message:
                    type: string
                  code:
                    type: string
                additionalProperties: true
    MessageType:
      type: string
      description: >-
        Type of message. Every type except `text` and `template` is sent on
        WhatsApp only: the send routes them there whatever `channel` says, and
        in v2 an explicit `channel` other than `whatsapp` is refused with `400`
        naming `messageType` and `channel`.


        `location_request` asks the recipient to share their location and is
        WhatsApp-only. It takes no `content` object — the prompt goes in `text`
        (max 1024 characters) and the button label is fixed by WhatsApp. The
        recipient's answer arrives as an inbound `location` message whose
        `content.replyToMessageId` is the ID of the request.


        `request_contact_info` asks the recipient to share their phone number
        and is WhatsApp-only. Like `location_request` it takes no `content`
        object — the prompt goes in `text` (max 1024 characters) and WhatsApp
        renders a fixed **Share Contact Info** button. The answer arrives as an
        inbound `contact` message. Use it to recover the phone number of a
        contact who adopted a WhatsApp username and is only known by their
        business-scoped user ID (BSUID); when they share it, Zavu automatically
        links the phone number to that contact.
      enum:
        - text
        - image
        - video
        - audio
        - document
        - sticker
        - location
        - contact
        - buttons
        - list
        - cta_url
        - request_contact_info
        - location_request
        - reaction
        - template
    MessageStatus:
      type: string
      enum:
        - queued
        - sending
        - sent
        - delivered
        - read
        - failed
        - received
        - pending_url_verification
    MessageContent:
      type: object
      description: Content for non-text message types (WhatsApp and Telegram).
      properties:
        mediaUrl:
          type: string
          description: URL of the media file (for image, video, audio, document, sticker).
          example: https://example.com/image.jpg
        mediaId:
          type: string
          description: WhatsApp media ID if already uploaded.
        mimeType:
          type: string
          description: MIME type of the media.
          example: image/jpeg
        filename:
          type: string
          description: Filename for documents.
          example: invoice.pdf
        latitude:
          type: number
          description: Latitude for location messages.
        longitude:
          type: number
          description: Longitude for location messages.
        locationName:
          type: string
          description: Name of the location.
        locationAddress:
          type: string
          description: Address of the location.
        contacts:
          type: array
          description: Contact cards for contact messages.
          items:
            type: object
            properties:
              name:
                type: string
              phones:
                type: array
                items:
                  type: string
        buttons:
          type: array
          description: Interactive buttons (max 3).
          maxItems: 3
          items:
            type: object
            required:
              - id
              - title
            properties:
              id:
                type: string
                maxLength: 256
              title:
                type: string
                maxLength: 20
        listButton:
          type: string
          description: Button text for list messages.
          maxLength: 20
        sections:
          type: array
          description: Sections for list messages.
          items:
            type: object
            required:
              - title
              - rows
            properties:
              title:
                type: string
              rows:
                type: array
                maxItems: 10
                items:
                  type: object
                  required:
                    - id
                    - title
                  properties:
                    id:
                      type: string
                    title:
                      type: string
                      maxLength: 24
                    description:
                      type: string
                      maxLength: 72
        ctaDisplayText:
          type: string
          description: Button label for cta_url messages.
          maxLength: 20
          example: See Dates
        ctaUrl:
          type: string
          format: uri
          description: >-
            Destination URL opened in the device's default browser when the
            button is tapped. Used with messageType=cta_url. WhatsApp requires
            HTTPS in production.
          example: https://example.com/schedule
        ctaHeaderType:
          type: string
          description: Optional header type for cta_url messages.
          enum:
            - text
            - image
            - video
            - document
        ctaHeaderText:
          type: string
          description: Header text when ctaHeaderType is 'text'.
          maxLength: 60
        ctaHeaderMediaUrl:
          type: string
          format: uri
          description: >-
            Public HTTPS URL of the header media when ctaHeaderType is 'image',
            'video', or 'document'. WhatsApp fetches this URL — it must be
            publicly reachable and return the declared content type.
        footerText:
          type: string
          description: Optional footer text for cta_url messages.
          maxLength: 60
          example: Dates subject to change.
        emoji:
          type: string
          description: Emoji for reaction messages.
        reactToMessageId:
          type: string
          description: Message ID to react to.
        replyToMessageId:
          type: string
          description: >-
            Zavu message ID of the quoted message this message replies to.
            Present on inbound messages that quote an earlier message. Omitted
            when the quoted message is not found in Zavu (e.g. an old or unknown
            message) — use replyToProviderMessageId in that case.
        replyToProviderMessageId:
          type: string
          description: >-
            Provider message ID (WhatsApp WAMID) of the quoted message. Present
            whenever an inbound message is a reply, even if the quoted message
            is not stored in Zavu.
        replyToFrom:
          type: string
          description: Sender of the quoted message (phone number in E.164 format).
        replyToText:
          type: string
          description: >-
            Truncated snippet of the quoted message's text, for display. Empty
            when the quoted message has no text (e.g. media).
        replyToMessageType:
          type: string
          description: Type of the quoted message (text, image, video, etc.).
        templateId:
          type: string
          description: Template ID for template messages.
        templateVariables:
          type: object
          description: >-
            Variables for body placeholders. Key them to match the template
            body: by position (`1`, `2`, ...) for positional templates, or by
            name (e.g. `customer_name`) for named templates. Zavu detects the
            template's format and sends the correct payload to Meta. Named keys
            also resolve a named text-header variable. Do not mix positional and
            named keys in the same request.
          additionalProperties:
            type: string
          example:
            '1': John
            '2': ORD-12345
        templateButtonVariables:
          type: object
          description: >-
            Variables for dynamic button placeholders (URL buttons and OTP
            buttons). Keys are the button index (0, 1, 2) in the template's
            `buttons` array — not the placeholder name. Values substitute the
            `{{1}}` placeholder inside that button's URL.


            **WhatsApp constraints:**

            - URL buttons only accept `{{1}}` — positional, numeric, no
            whitespace, no name. Named placeholders like `{{token}}` are stored
            as literal URL text by Meta and cannot be substituted.

            - At most one placeholder per URL button.

            - A template may have at most three buttons.

            - Static URL buttons (no placeholder) and `quick_reply` buttons are
            not included here.
          additionalProperties:
            type: string
          example:
            '0': abc-report-token
        templateHeaderVariables:
          type: object
          description: >-
            Value for a text-header variable, keyed by `1` (WhatsApp text
            headers allow at most one variable). Optional override. If omitted,
            Zavu resolves the header from `templateVariables` using the header
            placeholder's name (e.g. `novios`). Static text headers need no
            value.
          additionalProperties:
            type: string
          example:
            '1': Jorge y Laura
        referral:
          $ref: '#/components/schemas/AdReferral'
    AdReferral:
      type: object
      description: >-
        Click-to-WhatsApp (CTWA) ad attribution: where an inbound conversation
        came from.


        WhatsApp only. Present on the **first inbound message** of a
        conversation opened from a Meta ad or post, and on no message after it —
        so store it when it arrives rather than expecting it again. Organic
        conversations never carry it.


        Field names are camelCased to match the rest of this API; Meta sends
        them as snake_case (`ctwa_clid`, `source_id`, ...). Fields that do not
        apply are omitted: a `post` source has no click id, and an image ad has
        no `videoUrl`.
      properties:
        sourceType:
          type: string
          description: Where the click came from.
          enum:
            - ad
            - post
        sourceId:
          type: string
          description: Identifier of the ad or post that produced the click.
          example: '120210000000000000'
        sourceUrl:
          type: string
          format: uri
          description: Meta permalink to the ad or post.
        headline:
          type: string
          description: Headline of the ad or post.
        body:
          type: string
          description: Body copy of the ad or post.
        mediaType:
          type: string
          description: Type of media on the ad, when it had any.
          enum:
            - image
            - video
        imageUrl:
          type: string
          format: uri
          description: Image of the ad. Present when `mediaType` is `image`.
        videoUrl:
          type: string
          format: uri
          description: Video of the ad. Present when `mediaType` is `video`.
        thumbnailUrl:
          type: string
          format: uri
          description: Thumbnail of the ad media.
        ctwaClid:
          type: string
          description: >-
            Click-to-WhatsApp click identifier. This is the value Meta's
            Conversions API needs to credit a conversion back to the ad that
            produced the conversation. Present on `ad` sources; a `post` source
            has none.
          example: ARIzZm9vYmFyY3R3YWNsaWQ
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````

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