Skip to main content

Send Free-Form Message

Send a WhatsApp message without using a template — you can type any text or attach any media. This only works within the 24-hour customer service window, meaning the contact must have sent you a message in the last 24 hours.

How the 24-hour window works: When a customer sends you a WhatsApp message, a 24-hour window opens. During this time, you can reply with any content. After 24 hours, the window closes and you must use a template message instead.

POST /api/v1/messages/send-message

Auto-created contacts

If the contact phone number doesn't exist in your Waplify account, the contact will be created automatically when you send the message.

Request body

FieldTypeRequiredDescription
contact_phonestringYesPhone number with country code, without +
contact_namestringNoContact's name. Defaults to User #<random> if omitted
message_typestringYesOne of: text, image, video, audio, document
messagestringRequired for textText message content (max 4,096 characters)
media_urlstringRequired for media typesPublicly accessible URL for the media file
captionstringNoCaption for image, video, or document (max 1,024 characters)
filenamestringNoFilename for document attachments
waba_phone_idstringNoWhich WhatsApp number to send from
buttonsobjectNoInteractive buttons — only with message_type: text. See Interactive buttons below

Examples

Text message

curl -X POST https://server.waplify.io/api/v1/messages/send-message \
-H "Authorization: Bearer wapl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"contact_phone": "911234567890",
"contact_name": "John Doe",
"message_type": "text",
"message": "Hello! How can I help you today?"
}'

Image message

curl -X POST https://server.waplify.io/api/v1/messages/send-message \
-H "Authorization: Bearer wapl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"contact_phone": "911234567890",
"message_type": "image",
"media_url": "https://example.com/product-photo.png",
"caption": "Here is the product you asked about"
}'

Video message

curl -X POST https://server.waplify.io/api/v1/messages/send-message \
-H "Authorization: Bearer wapl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"contact_phone": "911234567890",
"message_type": "video",
"media_url": "https://example.com/tutorial.mp4",
"caption": "Watch this tutorial"
}'

Audio message

curl -X POST https://server.waplify.io/api/v1/messages/send-message \
-H "Authorization: Bearer wapl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"contact_phone": "911234567890",
"message_type": "audio",
"media_url": "https://example.com/voice-note.mp3"
}'

Document message

curl -X POST https://server.waplify.io/api/v1/messages/send-message \
-H "Authorization: Bearer wapl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"contact_phone": "911234567890",
"message_type": "document",
"media_url": "https://example.com/report.pdf",
"caption": "Monthly report attached",
"filename": "Monthly Report.pdf"
}'

Interactive buttons

Add a buttons object to a text message to attach tappable buttons — either up to 3 reply buttons or one website button (never both).

When buttons is present:

  • message_type must be text
  • message is required and limited to 1,024 characters
  • Interactive messages are session messages, so the 24-hour window must be open (same as any free-form message)

buttons object

FieldTypeRequiredDescription
typestringYesreply for reply buttons, or cta_url for a website button
reply_buttonsarrayFor reply1–3 reply buttons (see below)
url_buttonobjectFor cta_urlA single website button (see below)

Reply button (each item in reply_buttons):

FieldTypeDescription
idstringYour own identifier, echoed back in the webhook when the customer taps it (max 256 characters)
titlestringButton label shown to the customer (max 20 characters, must be unique within the message)

Website button (url_button):

FieldTypeDescription
display_textstringButton label (max 20 characters)
urlstringDestination link — must start with http:// or https://

Example — reply buttons

curl -X POST https://server.waplify.io/api/v1/messages/send-message \
-H "Authorization: Bearer wapl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"contact_phone": "911234567890",
"message_type": "text",
"message": "Did this answer your question?",
"buttons": {
"type": "reply",
"reply_buttons": [
{ "id": "yes", "title": "Yes, thanks" },
{ "id": "no", "title": "No, I need help" }
]
}
}'

Example — website button

curl -X POST https://server.waplify.io/api/v1/messages/send-message \
-H "Authorization: Bearer wapl_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"contact_phone": "911234567890",
"message_type": "text",
"message": "Browse our latest catalog.",
"buttons": {
"type": "cta_url",
"url_button": { "display_text": "View catalog", "url": "https://example.com" }
}
}'
caution

A message carries either reply buttons or a website button — not both — and buttons are not supported on media messages (image, video, audio, document). Requests that break these rules are rejected with a 422 error.

Success response

{
"status": "success",
"message": "Message sent successfully",
"message_id": "wamid.HBgNOTE4MDMxMjM0NTY3OA==",
"contact_id": "507f1f77bcf86cd799439012",
"message_type": "text",
"timestamp": "2026-06-15T10:00:00Z"
}
"Success" means accepted, not delivered

A "status": "success" response means WhatsApp has accepted your message — it does not mean the message has been delivered yet. Actual delivery, read, and failure statuses arrive later via webhooks. See Send Template Message for a detailed explanation of the message lifecycle.

Error responses

Error format note

This endpoint returns errors in a detail field (not the error/message format used by other endpoints). Check for both formats in your error handling code.

24-hour window not open

// 403 Forbidden
{
"detail": "Cannot send free-form message: No inbound message found from this contact. The 24-hour customer service window is not open. Use the template-based /send endpoint instead."
}

What this means: The contact has never messaged you, or their last message was more than 24 hours ago. Use the Send Template Message endpoint instead.

24-hour window expired

// 403 Forbidden
{
"detail": "Cannot send free-form message: The 24-hour customer service window has expired. Last inbound message was at 2026-06-14T08:00:00+00:00. Use the template-based /send endpoint instead."
}

What this means: The contact did message you, but more than 24 hours have passed since their last message. Use a template message to re-engage them.

Invalid phone number

// 400 Bad Request
{
"error": "bad_request",
"message": "Invalid phone number format"
}

Missing required field

// 400 Bad Request
{
"error": "bad_request",
"message": "message is required when message_type is 'text'"
}

Media file limits

TypeMax SizeAccepted Formats
Image5 MBJPEG, PNG
Video16 MBMP4
Audio16 MBAAC, MP3, M4A, AMR, OGG
Document100 MBPDF, DOC, DOCX, PPT, PPTX, TXT
caution

Free-form messages can only be sent within 24 hours of the contact's last inbound message. If you need to message a contact who hasn't messaged you recently, use the Send Template Message endpoint.