---
title: Sending messages | API Docs
description: 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](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages/) and [marketing, utility, and authentication templates](https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/) for the channel rules. The Linq part schemas remain the authority for shapes accepted by this API.

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

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

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

A fresh `message.received` [webhook](/channel/whatsapp/guides/webhooks/index.md) 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](/channel/whatsapp/api/resources/attachments/index.md) for the response contract and [error handling](/channel/whatsapp/guides/platform/errors/index.md).

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