Skip to content
Linq Copy agent prompt

Sending messages

Choose a send route, respect the customer window, and retry safely.

There are two ways to send. POST /v1/messages takes a destination and lets Linq select the eligible line and existing chat.

POST /v1/chats/{chat}/messages sends within a chat you already know. Both return the same accepted-message shape and support idempotency.

WhatsApp separates service messages, sent during the customer window, from template messages in marketing, utility, and authentication categories. See service messages and marketing, utility, and authentication templates for the channel rules. The Linq part schemas remain the authority for shapes accepted by this API.

Set Idempotency-Key to a stable value for the logical send. If a timeout leaves the response unknown, retry the same route with the same key and body. Do not generate a new key for a transport retry, and do not reuse a key for different content.

The chat read exposes customer_window. While it is open, you may send free-form parts such as text or media. Outside it, send one approved template part. An out-of-window free-form request returns 422 outside_customer_window; Linq does not replace your content.

{
"to": "+15551234567",
"parts": [{ "type": "text", "body": "How can we help?" }]
}
{
"to": "+15551234567",
"parts": [
{
"type": "template",
"name": "order_update",
"language": "en_US",
"parameters": { "order_number": "A-1042" }
}
]
}

The values above are illustrative. Read GET /v1/templates and validate each send against the selected template’s send_schema. The part schema served by GET /v1/parts/{type} defines the common template fields. Validation rejects invalid content and returns JSON Pointers to the most specific identifiable field.

HTTP 202 means accepted, not delivered. Consume event signals, then read

GET /v1/chats/{chat}/events for the message’s current state. Terminal outcomes are delivered, read, or failed. If no terminal evidence arrives, keep the outcome unresolved rather than blindly resending.

A fresh message.received webhook includes data.message, so you can read its parts without fetching the message again. Look for a media part with a url. For example, an incoming document:

{
"type": "media",
"kind": "document",
"filename": "invoice.pdf",
"media_id": "example-inbound-media-id",
"url": "https://whatsapp.messages.api.linqapp.com/v1/attachments/media_example/content"
}

Use the returned url with GET /v1/attachments/{attachment}/content. It requires your account’s Bearer API key on every download; it is not a public file link. Download from your server and keep the key out of browser code and logs. Only attach credentials to the trusted WhatsApp API origin shown below, and do not follow redirects.

Terminal window
# Use the document part's returned URL, not its media_id or upload_ref.
ATTACHMENT_URL='https://whatsapp.messages.api.linqapp.com/v1/attachments/media_example/content'
file=$(mktemp) || exit 1
trap 'rm -f "$file"' EXIT
status=$(curl --silent --show-error \
--proto '=https' --max-time 30 \
--header "Authorization: Bearer ${LINQ_WHATSAPP_API_KEY}" \
--output "$file" --write-out '%{http_code}' \
"$ATTACHMENT_URL") || exit 1
if [ "$status" = 200 ]; then
mv "$file" invoice.pdf
else
printf 'Attachment download returned HTTP %s\n' "$status" >&2
exit 1
fi

This example makes one request and saves the original bytes on HTTP 200. It intentionally omits --location, so credentials are not forwarded through a redirect. In your application, accept only 200 as a successful download and choose the local output filename yourself. See the attachment reference for the response contract and error handling.

Response Action
409 attachment_not_ready Capture is still pending. Wait at least Retry-After before retrying. Use a bounded policy, such as at most three attempts within 30 seconds; if the requested wait exceeds the remaining budget, stop and schedule a later attempt.
410 attachment_unavailable The attachment failed or expired. Stop retrying this download.
404 attachment_not_found No downloadable attachment is visible to this key, including missing or pruned files. Check the URL and account credentials; do not retry blindly.

Older events may contain only a chat and sequence range. Read

GET /v1/chats/{chat}/events when data.message is absent, and download only parts that actually include a url. The URL is response-only; do not include it when sending a message.