# Send a Message

Send free-form text — with several attachments if you need them, on the customer's own channel or a named one — inside the messaging window that channel currently has open.

## Getting here

**Automate → Workflows**, open a workflow, click **+** on the canvas, then pick **Send a Message** under **Add Steps**.

## Before you start

- An **owner**, **admin** or **agent** can add this step and save the workflow. Publishing that workflow — the thing that makes it run on live conversations — is an **owner** or **admin** action.
- Building and running workflows depends on your plan; see [pricing](/pricing).
- Nothing outside Inchat has to be connected. The step sends on whichever channel the conversation is already using, or a channel you name.

## Set up

1. Open the workflow and click **+** where the message belongs.
2. Pick **Send a Message**.
3. Choose a channel under the channel picker — the last one the customer used, or a specific connected channel.
4. Write the text under **Message Content · Default**. Insert `{{contact.name}}`, `{{answer.text}}` or another field token where you want it personalised.
5. Optional: attach up to 5 files under **Attachment** (**Attachments (n/max)** once more than one is added), add per-channel text under **Channel Response** for a channel that needs different wording, tick **Also send on other channels** or **Track links**, and open **Advanced Settings** to add a failure branch.
6. Save the step, then publish the workflow.

## Fields

| Field | What it means | Default | Limits |
| --- | --- | --- | --- |
| Channel | Where the message is sent: the conversation's last-interacted channel, or one you name. | Last interacted channel | — |
| Message Content · Default | The text sent on every channel that has no per-channel override, up to 2,000 characters. Field tokens like `{{contact.name}}` are filled in at send time. | — | Goes with the first attachment when media is attached: inline under a WhatsApp image, video or document; as a separate text message right after the file for a WhatsApp voice note or any attachment on Messenger or Instagram. |
| Attachment / Attachments (n/max) | Up to 5 files (image, video, audio or document), sent in the order listed. | — | Turns off **Also send on other channels** — the fan-out is text-only. |
| Also send on other channels | Sends the same text on up to three other channels belonging to the same person, matched by a shared phone number or email on their contact records. | off | Not available with an attachment on this step. One send per channel even if more than one of that person's contact records share it. |
| Track links | Rewrites links in the text to short links, so taps count toward link-click stats and can fire a **Link clicked** trigger. | off | — |
| Channel Response | Different wording for a specific channel type, used instead of the default text when the step actually sends on that channel type. | — | — |
| Add Message Failure Branch | Adds an **onFailure** arm that runs instead of the steps that would otherwise follow, when this send fails. | off | Without it, a failed send is recorded and the run moves on to the next step as if nothing happened. |

## How it works

- On a windowed channel (WhatsApp, Messenger, Instagram) the step checks whether the customer's last message — or, on WhatsApp, their last call — was within the last 24 hours, before it calls the channel's send API at all. Outside that window the send is refused and nothing reaches Meta.
- Field tokens are filled in, and links are shortened if **Track links** is on, before the message is sent. On WhatsApp the text is then converted from this product's own light markdown into WhatsApp's formatting dialect (`**bold**` becomes `*bold*`, and so on) right before the Graph call — the row saved to the conversation holds the pre-conversion text, not the exact characters WhatsApp rendered on the customer's screen.
- A voice note attached here goes out mono OGG/Opus if you recorded it through the step's own uploader (it is converted); a pasted MP3 or M4A URL marked as a voice note is refused before it reaches Meta, because WhatsApp only accepts mono OGG/Opus voice notes.
- A pasted `/api/media/<message>/<index>` link — Inchat's own login-gated inbox proxy — is first tried for redemption: if it points at an attachment this workspace actually stored, it is swapped for a short-lived signed URL before the send, and goes through. Only a link that cannot be redeemed (an unknown or foreign `/api/media` shape) then hits the shape check below and fails.
- Past that, the attachment URL is checked for its SHAPE only, before the send — it has to be https and a well-formed URL. It is never actually fetched to confirm the file is really there: a well-formed https URL that is dead, private, or simply wrong still passes this check, reaches Meta, and can fail at Meta's own download — a failure this step does not see, so it is logged as sent even though the customer received nothing.
- With more than one attachment, each file is sent as its own message in the order listed, and the message text goes with the first file only — inline for a WhatsApp image, video or document, otherwise as a separate text message right after it.
- **Also send on other channels** looks up other contact records in this workspace that share this contact's phone number or email, takes up to three of their most recently active conversations — one per CHANNEL, never two on the same channel even from two matching contact rows — and sends the same text there after the primary send succeeds. It can only deliver on Messenger, Instagram, WhatsApp or the website widget — a sibling record whose most recent channel is anything else (email, Telegram, …) fails that one send with **unsupported channel type** and moves on to the next target; the step itself is not affected. This fan-out send has no messaging-window check of its own, unlike the primary send.
- Every delivered send is written to the conversation as a reply from the workflow, which is what keeps the thread showing **Awaiting reply** as answered rather than stuck.

## When it fails

- Messaging window closed: on WhatsApp, Messenger or Instagram, past the window, the send is refused before Graph is called. The execution log's line for this step reads **Messaging window closed — free-form send blocked. Use a send_template step (approved WhatsApp / utility template) instead.**
- No **Add Message Failure Branch**: the step is recorded failed and the run continues to the next step in the flow — a tag, an assignment or a close placed after it still happens, on a conversation that never received this message.
- With the failure branch on, the branch's own steps run instead of the ones that would have followed, and nothing after the branch runs on this path.
- A voice note pasted as MP3/M4A and marked as a voice note fails outright: the execution log names the fix (a mono OGG/Opus file, or upload through the step so it is converted).
- A non-https or malformed attachment URL fails the step before any send is attempted — but this check never fetches the URL, so a well-formed https link that is dead or private still passes it, reaches Meta, and can fail there instead, invisibly to this step's own logged outcome. A pasted `/api/media/…` link fails only when it cannot be redeemed to a file this workspace actually stored; a redeemable one is swapped for a signed URL and sends normally.
- When the primary send's delivery cannot be confirmed one way or the other, the run stops after this step even with no failure branch set — later steps are not attempted on a conversation whose last send is unconfirmed.

## Limits

- It does not open a closed conversation on its own and does not reopen a closed WhatsApp/Messenger/Instagram window — that is what **Send WhatsApp Template** is for.
- **Also send on other channels** is a fan-out of the SAME text to up to three sibling channels; it cannot carry an attachment or per-channel wording, it does not run at all when this step also sends media, and — unlike the primary send — it carries no messaging-window check of its own.
- The attachment URL check never fetches the file — it only checks the URL's shape (https, well-formed). A dead or private https link is not caught here; it reaches Meta and can fail at Meta's own download instead, with this step still logged as sent. Only an unredeemable `/api/media/…` link is refused outright; one that resolves to a real stored attachment is swapped for a signed URL and sent.
- It does not wait for a reply. The run continues to the next step immediately after the send is confirmed (or fails), the same turn — pair it with **Ask a Question** for a step that pauses for an answer.
- It sends on the CHANNEL, not to a stored phone number you type in — to reach a number you have not messaged before, use **Start a WhatsApp Chat**.

## Best practices

- Turn on **Add Message Failure Branch** on anything that leads to money or a deadline — a payment link, a booking confirmation — so a closed window or a malformed attachment URL does not leave the flow behaving as if the customer was told. It cannot catch a dead-but-well-formed https link — that one reaches Meta and is logged as sent regardless.
- Keep **Track links** on for links you want a **Link clicked** trigger to react to later in the same workflow or a different one.

## Use cases

- Confirm an order was received, with the receipt image attached as media.
- Send a short answer straight after **AI Reply** on the path where the AI could not answer, with a failure branch that falls back to a plain text version if the channel rejects the first attempt.

## FAQ and troubleshooting

### Why did my message never arrive, with no error shown to the customer?

Most often the messaging window had closed. On WhatsApp, Messenger and Instagram, a free-form Send a Message outside the window is refused before it reaches Meta — nothing is sent, and the step is marked failed in the workflow's **Activity** tab. Use **Send WhatsApp Template** to reach a WhatsApp customer outside the window.

### The step failed. Does the rest of the workflow still run?

Yes, unless you turned on **Add Message Failure Branch**. Without it, the run continues to the next step regardless — a tag or an assignment placed after it still happens. With it, the branch's steps run instead and nothing after the branch runs on this path.

### Can I send to a number the contact has not messaged from?

Not with this step — it sends on the conversation's own channel or one you name for THIS conversation. To reach a WhatsApp number this conversation has not messaged from, use **Start a WhatsApp Chat**.

### Does adding an attachment change what Also send on other channels does?

It turns it off. The fan-out to sibling channels only ever sends the text, so it is disabled while an attachment is set on this step.

## Related

- [Send WhatsApp Template](https://inchat.inseller.my/help/workflows/send-whatsapp-template)
- [Send a File (voice / image / video)](https://inchat.inseller.my/help/workflows/send-a-file)
- [AI Reply](https://inchat.inseller.my/help/workflows/ai-reply)
- [Start a WhatsApp Chat (new number)](https://inchat.inseller.my/help/workflows/start-a-whatsapp-chat)
- [Workflow triggers](https://inchat.inseller.my/help/workflows/workflow-triggers)
- [Workflows: how a flow runs](https://inchat.inseller.my/help/workflows/workflows-overview)

---

Source files: `src/lib/workflows/ai-builder/spec.ts`, `src/lib/workflows/canvas-model.ts`, `src/components/workflows/WorkflowStepConfigDrawer.tsx`, `src/lib/workflows/engine-message-actions.ts`, `src/lib/workflows/engine.ts`, `src/lib/inbox/wa-window.ts`, `src/lib/workflows/send-message-media.ts`, `src/lib/workflows/media-type.ts`, `src/lib/workflows/media-url.ts`, `src/lib/send/rich-text.ts`, `src/lib/workflows/dispatch-send.ts`, `src/lib/access/permissions.ts`
