Skip to main content
POST
/
send-whatsapp
curl --request POST \
  --url https://api.flowiq.live/send-whatsapp \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "phone_number": "+27123456789",
  "message_type": "text",
  "text": "Hello! This is a test message from FlowIQ API."
}
'
{
"success": true,
"message": "Message sent successfully",
"data": {
"message_id": "wamid.HBgLMjc4MTIzNDU2NzgVAgASGBQzRUI...",
"recipient": "27123456789",
"message_type": "text",
"organization": "Your Organization Name",
"meta_response": {
"messaging_product": "whatsapp",
"contacts": [
{
"input": "27123456789",
"wa_id": "27123456789"
}
],
"messages": [
{
"id": "wamid.HBgLMjc4MTIzNDU2NzgVAgASGBQzRUI..."
}
]
}
}
}
This endpoint sends WhatsApp messages through Meta’s WhatsApp Business API. The organization must have Meta WhatsApp configured and the contact must already exist.

Overview

Send various types of WhatsApp messages to existing contacts in your FlowIQ organization. The API supports:
  • Text messages
  • Media messages (image, video, audio, document)
  • Location sharing
  • Contact cards
  • Interactive messages (buttons, lists)
  • Reactions to existing messages

Authentication

All requests require a Bearer token with API key format (fiq_...) in the Authorization header:
Authorization: Bearer fiq_YOUR_API_KEY
Only API keys with the fiq_ prefix are accepted. The API key must belong to the organization specified in tenantId.

Requirements

Prerequisites

  1. Meta WhatsApp Provider: Organization must have whatsapp_provider_choice set to meta 2. Configured Credentials: meta_phone_id and whatsapp_bearer_token must be set 3. Existing Contact: The recipient must already exist in your contacts database

Basic Usage

Send a Text Message

curl -X POST "https://api.flowiq.live/send-whatsapp?tenantId=YOUR_TENANT_ID" \
  -H "Authorization: Bearer fiq_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+27123456789",
    "message_type": "text",
    "text": "Hello! This is a test message from FlowIQ API."
  }'

Send an Image with Caption

curl -X POST "https://api.flowiq.live/send-whatsapp?tenantId=YOUR_TENANT_ID" \
  -H "Authorization: Bearer fiq_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+27123456789",
    "message_type": "image",
    "media_url": "https://example.com/image.jpg",
    "caption": "Check out this image!"
  }'

Reply to a Message

curl -X POST "https://api.flowiq.live/send-whatsapp?tenantId=YOUR_TENANT_ID" \
  -H "Authorization: Bearer fiq_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+27123456789",
    "message_type": "text",
    "text": "This is a reply to your message!",
    "context_message_id": "wamid.HBgLMjc4MTIzNDU2NzgVAgASGBQzRUI..."
  }'

Request Parameters

Query Parameters

ParameterTypeRequiredDescription
tenantIdstringYesYour organization’s tenant identifier (slug)

Request Body (Common Fields)

FieldTypeRequiredDescription
phone_numberstringYesRecipient phone number with country code
message_typestringYesType of message (see Message Types below)
context_message_idstringNoWhatsApp message ID to reply to

Message Types

Text Message

FieldTypeRequiredDescription
textstringYesThe text content of the message
{
  "phone_number": "+27123456789",
  "message_type": "text",
  "text": "Hello, World!"
}

Image Message

FieldTypeRequiredDescription
media_urlstringYesURL to the image file
captionstringNoOptional caption for the image
{
  "phone_number": "+27123456789",
  "message_type": "image",
  "media_url": "https://example.com/product.jpg",
  "caption": "Our latest product!"
}
Supported image formats: JPEG, PNG. The image will be automatically uploaded to our storage with a 1-year signed URL.

Video Message

FieldTypeRequiredDescription
media_urlstringYesURL to the video file
captionstringNoOptional caption for the video
{
  "phone_number": "+27123456789",
  "message_type": "video",
  "media_url": "https://example.com/demo.mp4",
  "caption": "Watch our demo video"
}

Audio Message

FieldTypeRequiredDescription
media_urlstringYesURL to the audio file
{
  "phone_number": "+27123456789",
  "message_type": "audio",
  "media_url": "https://example.com/message.mp3"
}
Audio messages do not support captions.

Document Message

FieldTypeRequiredDescription
media_urlstringYesURL to the document file
filenamestringNoDisplay filename for the document
captionstringNoOptional caption for the document
{
  "phone_number": "+27123456789",
  "message_type": "document",
  "media_url": "https://example.com/invoice.pdf",
  "filename": "Invoice_2025.pdf",
  "caption": "Please find your invoice attached"
}

Location Message

FieldTypeRequiredDescription
location.latitudenumberYesLatitude coordinate
location.longitudenumberYesLongitude coordinate
location.namestringNoLocation name
location.addressstringNoLocation address
{
  "phone_number": "+27123456789",
  "message_type": "location",
  "location": {
    "latitude": -33.9249,
    "longitude": 18.4241,
    "name": "Cape Town Office",
    "address": "123 Main Street, Cape Town"
  }
}

Contacts Message

Share one or more contact cards.
FieldTypeRequiredDescription
contactsarrayYesArray of contact objects
{
  "phone_number": "+27123456789",
  "message_type": "contacts",
  "contacts": [
    {
      "name": {
        "formatted_name": "John Doe",
        "first_name": "John",
        "last_name": "Doe"
      },
      "phones": [
        {
          "phone": "+27987654321",
          "type": "CELL"
        }
      ]
    }
  ]
}

Interactive Button Message

Send a message with up to 3 quick reply buttons.
FieldTypeRequiredDescription
interactive.typestringYesMust be "button"
interactive.bodystringYesMain message text (max 1024 chars)
interactive.headerstringNoHeader text (max 60 chars)
interactive.footerstringNoFooter text (max 60 chars)
interactive.buttonsarrayYesArray of button objects (max 3)
interactive.header_mediaobjectNoMedia header (image/video/document)

Basic Button Example

{
  "phone_number": "+27123456789",
  "message_type": "interactive",
  "interactive": {
    "type": "button",
    "header": "Order Confirmation",
    "body": "Would you like to confirm your order #12345?",
    "footer": "Reply within 24 hours",
    "buttons": [
      { "id": "confirm", "title": "Confirm" },
      { "id": "cancel", "title": "Cancel" },
      { "id": "modify", "title": "Modify Order" }
    ]
  }
}

Button with Image Header

{
  "phone_number": "+27123456789",
  "message_type": "interactive",
  "interactive": {
    "type": "button",
    "body": "Check out our new product!",
    "header_media": {
      "type": "image",
      "url": "https://example.com/product.jpg"
    },
    "buttons": [
      { "id": "buy", "title": "Buy Now" },
      { "id": "info", "title": "More Info" }
    ]
  }
}
Button titles are limited to 20 characters. You can have a maximum of 3 buttons.

Interactive List Message

Send a message with a list menu containing multiple sections and rows.
FieldTypeRequiredDescription
interactive.typestringYesMust be "list"
interactive.bodystringYesMain message text (max 1024 chars)
interactive.buttonstringYesMenu button text (max 20 chars)
interactive.headerstringNoHeader text (max 60 chars)
interactive.footerstringNoFooter text (max 60 chars)
interactive.sectionsarrayYesArray of section objects (max 10)
{
  "phone_number": "+27123456789",
  "message_type": "interactive",
  "interactive": {
    "type": "list",
    "header": "Our Menu",
    "body": "Please select from our available options:",
    "footer": "Tap the button below to view options",
    "button": "View Menu",
    "sections": [
      {
        "title": "Main Dishes",
        "rows": [
          {
            "id": "burger",
            "title": "Burger",
            "description": "Beef patty with cheese"
          },
          {
            "id": "pizza",
            "title": "Pizza",
            "description": "Margherita or Pepperoni"
          }
        ]
      },
      {
        "title": "Drinks",
        "rows": [
          { "id": "cola", "title": "Cola", "description": "500ml bottle" },
          {
            "id": "water",
            "title": "Water",
            "description": "Still or sparkling"
          }
        ]
      }
    ]
  }
}
List messages support up to 10 sections with up to 10 rows each. Row titles are limited to 24 characters and descriptions to 72 characters.

Reaction Message

React to an existing message with an emoji.
FieldTypeRequiredDescription
reaction.message_idstringYesWhatsApp message ID to react to
reaction.emojistringNoEmoji to react with (default: 👍)
{
  "phone_number": "+27123456789",
  "message_type": "reaction",
  "reaction": {
    "message_id": "wamid.HBgLMjc4MTIzNDU2NzgVAgASGBQzRUI...",
    "emoji": "❤️"
  }
}

Response Examples

Success Response (200)

{
  "success": true,
  "message": "Message sent successfully",
  "data": {
    "message_id": "wamid.HBgLMjc4MTIzNDU2NzgVAgASGBQzRUI...",
    "recipient": "27123456789",
    "message_type": "text",
    "organization": "Your Organization Name",
    "meta_response": {
      "messaging_product": "whatsapp",
      "contacts": [{ "input": "27123456789", "wa_id": "27123456789" }],
      "messages": [{ "id": "wamid.HBgLMjc4MTIzNDU2NzgVAgASGBQzRUI..." }]
    }
  }
}

Error: Contact Not Found (404)

{
  "error": "Contact not found",
  "message": "No contact exists with WhatsApp number 27123456789. Please create the contact first using the /api/contact endpoint.",
  "whatsapp_id": "27123456789"
}

Error: Invalid Provider (400)

{
  "error": "Invalid provider",
  "message": "Organization must use Meta WhatsApp provider. Current: wati"
}

Error: Missing Required Field (400)

{
  "error": "Missing required field",
  "message": "text is required for text messages"
}

Error: Invalid API Key (401)

{
  "error": "Invalid API key",
  "message": "API key has been revoked"
}

Error: Meta Upload Failed (500)

{
  "error": "Meta upload failed",
  "message": "Failed to upload media to Meta"
}

Media Handling

Media Upload Flow

When you send media messages, the API automatically: 1. Downloads the media from your provided URL 2. Uploads it to FlowIQ’s storage bucket 3. Generates a 1-year signed URL 4. Uploads to Meta’s servers for delivery 5. Stores the signed URL in the message database
This ensures:
  • Media is permanently stored for future reference
  • URLs won’t expire or break
  • Messages can be viewed in the FlowIQ inbox
Supported media types: - Images: JPEG, PNG - Videos: MP4 - Audio: MP3, AAC, OGG - Documents: PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX

Error Codes

Status CodeErrorDescription
200SuccessMessage sent successfully
400Bad RequestMissing required fields, invalid message type, or invalid provider
401UnauthorizedInvalid or expired API key, or tenant mismatch
404Not FoundOrganization or contact not found
500Internal Server ErrorMeta upload failed or server error

Best Practices

Contact Requirement

Always ensure the contact exists before sending messages. Use the /contact endpoint to create contacts first.

Phone Number Format

Include the country code in phone numbers (e.g., +27 for South Africa). The API removes special characters automatically.

Media URLs

Ensure media URLs are publicly accessible. The API needs to download the file before uploading to Meta.

Interactive Messages

Use interactive buttons and lists for better engagement. They provide a better user experience than plain text.

Message Context

Use context_message_id when replying to specific messages. This creates a visual thread in the WhatsApp conversation.

Integration Example

async function sendWhatsAppMessage(tenantId, apiKey, messageData) {
  try {
    const response = await fetch(
      `https://api.flowiq.live/send-whatsapp?tenantId=${tenantId}`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(messageData),
      }
    );

    const data = await response.json();

    if (!response.ok) {
      if (response.status === 404) {
        console.error("Contact not found:", data.message);
        // Handle contact creation first
      } else if (response.status === 400) {
        console.error("Invalid request:", data.message);
      }
      throw new Error(data.message);
    }

    return data;
  } catch (error) {
    console.error("Failed to send message:", error);
    throw error;
  }
}

// Send a text message
const textResult = await sendWhatsAppMessage(
  "your-tenant-id",
  "fiq_your_api_key",
  {
    phone_number: "+27123456789",
    message_type: "text",
    text: "Hello from the FlowIQ API!",
  }
);

// Send an interactive button message
const buttonResult = await sendWhatsAppMessage(
  "your-tenant-id",
  "fiq_your_api_key",
  {
    phone_number: "+27123456789",
    message_type: "interactive",
    interactive: {
      type: "button",
      body: "How would you like to proceed?",
      buttons: [
        { id: "yes", title: "Yes, continue" },
        { id: "no", title: "No, cancel" },
      ],
    },
  }
);

// Send a document with caption
const docResult = await sendWhatsAppMessage(
  "your-tenant-id",
  "fiq_your_api_key",
  {
    phone_number: "+27123456789",
    message_type: "document",
    media_url: "https://example.com/report.pdf",
    filename: "Monthly_Report.pdf",
    caption: "Please review the attached report",
  }
);

Rate Limiting

The WhatsApp Business API has rate limits imposed by Meta. If you exceed these limits, messages may fail to send. Implement exponential backoff and respect 429 responses.

Message Storage

All sent messages are automatically stored in:
  • helpdesk_messages: Full message details including media URLs
  • customer_sessions: Message history for the conversation thread
This enables message history viewing in the FlowIQ inbox and conversation tracking.

Authorizations

Authorization
string
header
required

Bearer token for authentication. Format: Bearer YOUR_BEARER_TOKEN

Query Parameters

tenantId
string
required

Organization tenant identifier (slug)

Body

application/json
phone_number
string
required

Recipient phone number with country code

Example:

"+27123456789"

message_type
enum<string>
required

Type of message to send

Available options:
text,
image,
video,
audio,
document,
location,
contacts,
interactive,
reaction
Example:

"text"

text
string

Text content (required for text messages)

Example:

"Hello, World!"

media_url
string

URL to media file (required for image/video/audio/document)

Example:

"https://example.com/image.jpg"

caption
string

Caption for media messages

Example:

"Check this out!"

filename
string

Filename for document messages

Example:

"document.pdf"

location
object
contacts
object[]

Array of contact cards to share

interactive
object

Interactive message configuration

reaction
object

Reaction configuration

context_message_id
string

WhatsApp message ID to reply to

Example:

"wamid.HBgLMjc4MTIzNDU2NzgVAgASGBQzRUI..."

Response

Message sent successfully

success
boolean
required

Whether the message was sent successfully

Example:

true

message
string
required

Success message

Example:

"Message sent successfully"

data
object
required