> ## 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.

# Send a message

> Send a message to a recipient via SMS or WhatsApp.

**Channel selection:**
- If `channel` is omitted and `messageType` is `text`, defaults to SMS
- If `messageType` is anything other than `text`, WhatsApp is used automatically

**WhatsApp 24-hour window:**
- Free-form messages (non-template) require an open 24h window
- Window opens when the user messages you first
- Use template messages to initiate conversations outside the window

**Daily limits:**
- Unverified accounts: 200 messages per channel per day
- Complete KYC verification to increase limits to 10,000/day



## OpenAPI

````yaml /openapi.json post /v1/messages
openapi: 3.0.3
info:
  title: Zavu Unified Messaging Layer API
  version: 0.2.0
  description: >
    Unified multi-channel messaging API for Zavu.


    Supported channels:

    - **SMS**: Simple text messages

    - **WhatsApp**: Rich messaging with media, buttons, lists, CTA URL buttons,
    and templates

    - **Telegram**: Bot messaging with text, media, and interactive elements

    - **Email**: Transactional emails via Amazon SES


    Design goals:

    - Simple `send()` entrypoint for developers

    - Project-level authentication via Bearer token

    - Support for all WhatsApp message types (text, image, video, audio,
    document, sticker, location, contact, buttons, list, cta_url, reaction,
    template)

    - If a non-text message type is sent, WhatsApp channel is used automatically

    - 24-hour WhatsApp conversation window enforcement

    - Universal `to` field accepts phone numbers (E.164), email addresses, or
    numeric chat IDs (Telegram/Instagram/Messenger)
servers:
  - url: https://api.zavu.dev
security:
  - bearerAuth: []
paths:
  /v1/messages:
    post:
      summary: Send a message
      description: >-
        Send a message to a recipient via SMS or WhatsApp.


        **Channel selection:**

        - If `channel` is omitted and `messageType` is `text`, defaults to SMS

        - If `messageType` is anything other than `text`, WhatsApp is used
        automatically


        **WhatsApp 24-hour window:**

        - Free-form messages (non-template) require an open 24h window

        - Window opens when the user messages you first

        - Use template messages to initiate conversations outside the window


        **Daily limits:**

        - Unverified accounts: 200 messages per channel per day

        - Complete KYC verification to increase limits to 10,000/day
      operationId: sendMessage
      parameters:
        - $ref: '#/components/parameters/SenderHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MessageRequest'
            examples:
              sms:
                summary: Simple SMS
                value:
                  to: '+56912345678'
                  text: Your verification code is 123456
              whatsapp_text:
                summary: WhatsApp text message
                value:
                  to: '+56912345678'
                  channel: whatsapp
                  text: Hello from Zavu!
              whatsapp_image:
                summary: WhatsApp image
                value:
                  to: '+56912345678'
                  messageType: image
                  text: Check out this product!
                  content:
                    mediaUrl: https://example.com/product.jpg
              whatsapp_buttons:
                summary: WhatsApp interactive buttons
                value:
                  to: '+56912345678'
                  messageType: buttons
                  text: How would you rate your experience?
                  content:
                    buttons:
                      - id: great
                        title: Great!
                      - id: okay
                        title: It was okay
                      - id: poor
                        title: Not good
              whatsapp_template:
                summary: WhatsApp template message
                value:
                  to: '+56912345678'
                  messageType: template
                  content:
                    templateId: tmpl_abc123
                    templateVariables:
                      '1': John
                      '2': ORD-12345
              whatsapp_template_with_url_button:
                summary: WhatsApp template with dynamic URL button
                value:
                  to: '+56912345678'
                  messageType: template
                  content:
                    templateId: tmpl_abc123
                    templateVariables:
                      '1': ORDAZ BRAVO MARCO
                      '2': 'REPORTE: Conducta inadecuada'
                    templateButtonVariables:
                      '0': abc-report-token
              whatsapp_cta_url:
                summary: WhatsApp CTA URL button
                value:
                  to: '+56912345678'
                  channel: whatsapp
                  messageType: cta_url
                  text: Tap the button below to see available dates.
                  content:
                    ctaDisplayText: See Dates
                    ctaUrl: https://example.com/schedule?ref=zavu
                    ctaHeaderType: image
                    ctaHeaderMediaUrl: https://example.com/banner.png
                    footerText: Dates subject to change.
              telegram_text:
                summary: Telegram text message
                value:
                  to: '123456789'
                  channel: telegram
                  text: Hello from Zavu via Telegram!
              telegram_template:
                summary: Telegram template message
                value:
                  to: '123456789'
                  channel: telegram
                  messageType: template
                  content:
                    templateId: tmpl_abc123
                    templateVariables:
                      '1': John
                      '2': ORD-12345
              sms_template:
                summary: SMS template message
                value:
                  to: '+56912345678'
                  channel: sms
                  messageType: template
                  content:
                    templateId: tmpl_abc123
                    templateVariables:
                      '1': John
                      '2': ORD-12345
              instagram_text:
                summary: Instagram text message
                value:
                  to: '17841400000000000'
                  channel: instagram
                  text: Hello from Zavu via Instagram!
              messenger_text:
                summary: Messenger text message (Facebook Page / Marketplace chat)
                value:
                  to: '24025631120151183'
                  channel: messenger
                  text: Hello from Zavu via Messenger!
              voice_tts:
                summary: Voice text-to-speech message
                value:
                  to: '+56912345678'
                  channel: voice
                  text: Your verification code is 1 2 3 4 5 6
              voice_tts_spanish:
                summary: Voice TTS in Spanish
                value:
                  to: '+56912345678'
                  channel: voice
                  text: Su codigo de verificacion es 1 2 3 4 5 6
                  voiceLanguage: es-ES
              email_simple:
                summary: Simple email
                value:
                  to: user@example.com
                  channel: email
                  subject: Your order has shipped
                  text: >-
                    Hi John, your order #12345 has shipped and will arrive in
                    2-3 business days.
              email_html:
                summary: Email with HTML body
                value:
                  to: user@example.com
                  channel: email
                  subject: Welcome to Zavu
                  text: Welcome to Zavu! We're excited to have you.
                  htmlBody: >-
                    <h1>Welcome to Zavu!</h1><p>We're excited to have you on
                    board.</p>
                  replyTo: support@example.com
              email_attachment:
                summary: Email with attachments
                value:
                  to: user@example.com
                  channel: email
                  subject: Your invoice
                  text: Please find your invoice attached.
                  attachments:
                    - filename: invoice.pdf
                      content: JVBERi0xLjQK...
                      content_type: application/pdf
              email_inline_image:
                summary: Email with inline image
                value:
                  to: user@example.com
                  channel: email
                  subject: Your report
                  text: See your report below.
                  htmlBody: <h1>Report</h1><img src="cid:chart"><p>Details above.</p>
                  attachments:
                    - filename: chart.png
                      path: https://example.com/chart.png
                      content_type: image/png
                      content_id: chart
      responses:
        '202':
          description: Message accepted for delivery.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResponse'
        '400':
          description: Invalid request or WhatsApp 24h window not open.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                window_closed:
                  summary: WhatsApp window closed
                  value:
                    code: whatsapp_window_closed
                    message: >-
                      WhatsApp 24-hour window is not open. Use a template
                      message or wait for user to message first.
        '401':
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Message blocked due to URL restrictions.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                url_not_verified:
                  summary: URL not verified
                  value:
                    code: url_not_verified
                    message: >-
                      Message blocked: Contains unverified URLs. Submit URLs for
                      verification via the /v1/urls endpoint.
                    details:
                      urls:
                        - https://example.com
                      submissionEndpoint: /v1/urls
                url_shortener_blocked:
                  summary: URL shortener blocked
                  value:
                    code: url_shortener_blocked
                    message: >-
                      Message blocked: URL shorteners are not allowed. Please
                      use the full destination URL.
                    details:
                      blockedUrls:
                        - https://bit.ly/abc123
                      reason: url_shorteners_hidden_destination
        '404':
          description: Template or sender not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Idempotency conflict (message already sent).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - bearerAuth: []
components:
  parameters:
    SenderHeader:
      name: Zavu-Sender
      in: header
      required: false
      description: >-
        Optional sender profile ID. If omitted, the project's default sender
        will be used.
      schema:
        type: string
        example: sender_12345
  schemas:
    MessageRequest:
      type: object
      description: Request body to send a message.
      required:
        - to
      properties:
        to:
          type: string
          description: >-
            Recipient phone number in E.164 format, email address, or numeric
            chat ID (for Telegram/Instagram/Messenger).
          example: '+56912345678'
        channel:
          $ref: '#/components/schemas/Channel'
          description: >-
            Delivery channel. Use 'auto' for intelligent routing. If omitted,
            channel is auto-selected based on sender capabilities and recipient
            type. For email recipients, defaults to 'email'.
          default: auto
        messageType:
          $ref: '#/components/schemas/MessageType'
          description: Type of message. Defaults to 'text'.
          default: text
        text:
          type: string
          description: Text body for text messages or caption for media messages.
          example: Your verification code is 123456.
        content:
          $ref: '#/components/schemas/MessageContent'
          description: Additional content for non-text message types.
        subject:
          type: string
          description: >-
            Email subject line. Required when channel is 'email' or recipient is
            an email address.
          maxLength: 998
          example: Your order confirmation
        htmlBody:
          type: string
          description: >-
            HTML body for email messages. If provided, email will be sent as
            multipart with both text and HTML.
        replyTo:
          type: string
          format: email
          description: Reply-To email address for email messages.
          example: support@example.com
        idempotencyKey:
          type: string
          description: Optional idempotency key to avoid duplicate sends.
          example: msg_01HZY4ZP7VQY2J3BRW7Z6G0QGE
        metadata:
          type: object
          description: Arbitrary metadata to associate with the message.
          additionalProperties:
            type: string
        fallbackEnabled:
          type: boolean
          description: >-
            Whether to enable automatic fallback to SMS if WhatsApp fails.
            Defaults to true.
          default: true
        voiceLanguage:
          type: string
          description: >-
            Language code for voice text-to-speech (e.g., 'en-US', 'es-ES',
            'pt-BR'). If omitted, language is auto-detected from recipient's
            country code.
          example: es-ES
        attachments:
          type: array
          description: >-
            Email attachments. Only supported when channel is 'email'. Maximum
            40MB total size.
          maxItems: 50
          items:
            $ref: '#/components/schemas/EmailAttachmentInput'
    MessageResponse:
      type: object
      required:
        - message
      properties:
        message:
          $ref: '#/components/schemas/Message'
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          example: invalid_request
        message:
          type: string
          example: Phone number is invalid
        details:
          type: object
          additionalProperties: true
    Channel:
      type: string
      description: Delivery channel. Use 'auto' for intelligent routing.
      enum:
        - auto
        - sms
        - sms_oneway
        - whatsapp
        - telegram
        - email
        - instagram
        - messenger
        - voice
    MessageType:
      type: string
      description: >-
        Type of message. Non-text types are supported by WhatsApp and Telegram
        (varies by type).
      enum:
        - text
        - image
        - video
        - audio
        - document
        - sticker
        - location
        - contact
        - buttons
        - list
        - cta_url
        - reaction
        - template
    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
    EmailAttachmentInput:
      type: object
      description: >-
        Email attachment. Provide either `content` (base64) or `path` (URL), not
        both.
      required:
        - filename
      properties:
        filename:
          type: string
          description: Name of the attached file.
          maxLength: 255
          example: invoice.pdf
        content:
          type: string
          description: Content of the attached file as a Base64-encoded string.
        path:
          type: string
          format: uri
          description: >-
            URL where the attachment file is hosted. The server will fetch the
            file.
        content_type:
          type: string
          description: >-
            MIME type of the attachment. If not set, will be derived from the
            filename.
          example: application/pdf
        content_id:
          type: string
          description: >-
            Content ID for inline images. Reference in HTML as `<img
            src="cid:your_content_id">`.
          maxLength: 100
          example: logo
    Message:
      type: object
      required:
        - id
        - to
        - status
        - channel
        - messageType
        - createdAt
      properties:
        id:
          type: string
          example: jd7x2k3m4n5p6q7r8s9t0
        to:
          type: string
          example: '+56912345678'
        from:
          type: string
          example: '+13125551212'
        senderId:
          type: string
          example: sender_12345
        channel:
          $ref: '#/components/schemas/Channel'
        messageType:
          $ref: '#/components/schemas/MessageType'
        status:
          $ref: '#/components/schemas/MessageStatus'
        text:
          type: string
          description: Text content or caption.
        content:
          $ref: '#/components/schemas/MessageContent'
        providerMessageId:
          type: string
          description: Message ID from the delivery provider.
        errorCode:
          type: string
          nullable: true
        errorMessage:
          type: string
          nullable: true
        cost:
          type: number
          nullable: true
          description: MAU cost in USD (charged for first contact of the month).
        costProvider:
          type: number
          nullable: true
          description: Provider cost in USD (Telnyx, SES, etc.).
        costTotal:
          type: number
          nullable: true
          description: Total cost in USD (MAU + provider cost).
        metadata:
          type: object
          additionalProperties:
            type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    MessageStatus:
      type: string
      enum:
        - queued
        - sending
        - sent
        - delivered
        - read
        - failed
        - received
        - pending_url_verification
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````