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.
Always make sends idempotent
Section titled “Always make sends idempotent”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.
Choose content from the customer window
Section titled “Choose content from the customer window”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.
Follow the outcome
Section titled “Follow the outcome”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.
Download incoming attachments
Section titled “Download incoming attachments”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.
# 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 1trap 'rm -f "$file"' EXITstatus=$(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.pdfelse printf 'Attachment download returned HTTP %s\n' "$status" >&2 exit 1fiThis 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.