> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sagepilot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Draft and send messages

> Prefill the chat composer or send a message on the visitor's behalf from your page.

Your page can put text into the chat for the visitor. Use `setDraft()` to prefill the message box so the visitor can review and send it, or `sendMessage()` to send it directly. For example, a returns page can open the chat with the order number already filled in.

Neither method opens the chat. Call `ChatWidget.open()` when the visitor should see it.

## Prefill a message

```js theme={null}
await window.sagepilotWidgetReady;
window.ChatWidget.open();

const result = await window.ChatWidget.setDraft(
  "I would like to return order #12345."
);
```

`setDraft(text)` replaces the text in the message box and keeps any attachments the visitor already added. Pass an empty string to clear the text. It does not send anything and does not save a draft between visits.

## Send a message

```js theme={null}
await window.sagepilotWidgetReady;
window.ChatWidget.open();

const result = await window.ChatWidget.sendMessage(
  "I would like to return order #12345."
);

if (result.status === "sent") {
  // The message was sent. result.conversationId identifies the conversation.
} else if (result.status === "requires_input") {
  // The chat is showing its contact form or verification step.
} else if (result.status === "unknown") {
  // The message may have been sent. Check the conversation before trying again.
} else {
  // Not sent. result.code explains why.
}
```

`sendMessage(text)` sends the text as the current visitor, in their current conversation. If there is no conversation yet, it starts one. Text the visitor already typed and their attachments are kept.

## Results

Both methods resolve to an object with a `status`. They do not throw.

| `status`         | Meaning                                                                                                                                                                   |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `drafted`        | `setDraft()` put the text in the message box. Nothing was sent.                                                                                                           |
| `sent`           | The message was sent. `conversationId` identifies the conversation. It does not mean a reply has been sent yet.                                                           |
| `requires_input` | The visitor must complete a step in the chat first. `code` is `contact_required` or `authentication_required`. Nothing was sent, and the call does not resume on its own. |
| `failed`         | Nothing was sent, or the draft could not be confirmed. `code` explains why.                                                                                               |
| `unknown`        | The message was submitted, but the result could not be confirmed. Do not retry automatically.                                                                             |

When `sendMessage()` returns `requires_input` or `failed`, it places your text in the message box if the box is empty, so the visitor can send it after completing the form.

### Failure codes

| `code`                     | Meaning                                                                            |
| -------------------------- | ---------------------------------------------------------------------------------- |
| `not_initialized`          | The widget is not ready. Wait for `window.sagepilotWidgetReady` first.             |
| `invalid_text`             | The text is not a string, or `sendMessage()` received empty text.                  |
| `identity_pending`         | An `identify()` or `logout()` call is still in progress. Wait for it to finish.    |
| `identity_changed`         | The visitor's identity changed before the call finished.                           |
| `not_ready`                | The chat did not become ready within 15 seconds.                                   |
| `busy`                     | Another message is still being sent.                                               |
| `conversation_closed`      | The current conversation is closed. The visitor can start a new one from the chat. |
| `conversation_unavailable` | The conversation could not be loaded.                                              |
| `draft_failed`             | The text could not be placed in the message box.                                   |
| `frame_reloaded`           | The chat reloaded before the call finished.                                        |
| `timeout`                  | No result arrived within 60 seconds.                                               |
| `send_unconfirmed`         | The message was submitted, but its result could not be confirmed.                  |
| `conversation_changed`     | The visitor switched conversations before the result arrived.                      |

## Rules to follow

* **Do not retry automatically.** A result of `unknown`, or a `timeout` after sending, can mean the message was delivered. Retrying can send it twice.
* **One call at a time.** Calls from the same page run in order. While one message is still sending, the next can return `busy`.
* **Send once per action.** If your page sends a message on load, for example after a redirect, make sure it does not send again when the page re-renders or the visitor comes back.
* **Contact and verification steps still apply.** If the channel requires a contact form or OTP, the visitor must complete it before a message is sent.
* **Identify first.** If the visitor is a signed-in customer, wait for `identify()` to finish before sending. See [Identity](/sdks/web/identity).
* **Plain text only.** Both methods accept a text string. To send a file, the visitor attaches it in the chat.

## Example: open chat from a link with context

A page that opens chat about a specific order when the visitor arrives from an email link such as `https://example.com/help?order=12345`:

```js theme={null}
const order = new URLSearchParams(window.location.search).get("order");

if (order && /^[0-9]+$/.test(order)) {
  await window.sagepilotWidgetReady;
  window.ChatWidget.open();
  await window.ChatWidget.setDraft(`I need help with order #${order}.`);
}
```

Validate anything you read from the URL before you put it in a message. The widget does not read your page's URL parameters itself.
