# How to make an AI agent text like a person on WhatsApp

> Prompt rules, WhatsApp's own formatting syntax and TypeScript code that turns a model's Markdown into short, ordered messages that read like a person texting.

Source: https://whasp.ai/blog/write-for-whatsapp-ai-agent-prompts · 2026-10-20

## Key takeaways

- WhatsApp formats text with its own syntax (*bold*, _italic_, ~strikethrough~, three backticks for monospace), so the Markdown a model writes by default has to be converted before it is sent.
- A chat reply works best as two or three short messages with one thought each, not one long block, and the prompt should say so in numbers.
- The prompt sets the style but code should enforce it: convert Markdown, split on blank lines, cap the number of messages and send each part with its own idempotency key.
- whasp has no typing indicator and sends a number's messages one at a time, about a second apart, so a slow reply looks like silence and a fast model matters more than a delay.
- Every part of a split reply counts toward the 1,500 messages a day a whasp number can send, so the number of parts is a budget as well as a style choice.

To make an AI agent text like a person on WhatsApp, give its system prompt four rules: short messages with one thought each, WhatsApp's own formatting instead of Markdown, at most one question per turn, and plain words with no assistant boilerplate. Then enforce them in code: convert Markdown to WhatsApp syntax and split the reply into two or three messages before you send it.

## Why do AI replies feel wrong on WhatsApp?

Chat models tend to answer like documents: a greeting, a bolded list, a closing offer of more help. In a chat bubble that reads like a form letter, and symbols such as `#` arrive exactly as typed. People text in short bursts, and a reply that does not look like a burst gives the agent away in its first message.

Anthropic's own [model overview](https://platform.claude.com/docs/en/models/overview) tells developers who want shorter answers to ask for them in the prompt, and the same holds for any model. Take one message, "It went great, I got the job", and two replies to it:

```text
Congratulations on your new role. That is wonderful news. Here are some steps to consider:

**1. Review the offer in detail**
**2. Prepare for your first week**

Let me know if I can help with any of these.
```

```text
Maya, that is great news. Congratulations.

Tell me everything. How did they tell you?
```

The second is two messages, one question and no formatting. The first is one block, with Markdown that is not part of WhatsApp's documented syntax.

## What formatting does WhatsApp support?

WhatsApp's help page [How to format your messages](https://faq.whatsapp.com/539178204879377) documents eight pieces of syntax, and its Android, iPhone and Web versions list the same ones.

| Format | What you type |
| --- | --- |
| Bold | `*text*` |
| Italic | `_text_` |
| Strikethrough | `~text~` |
| Monospace | three backticks on each side of the text |
| Inline code | one backtick on each side of the text |
| Bulleted list | `* text` or `- text`, one per line |
| Numbered list | `1. text`, one per line |
| Quote | `> text` |

Bold is a single asterisk, which is why the `**double asterisks**` and `# headings` a model writes by default need converting. The page lists no syntax for headings, tables or link text, so write URLs plainly and turn table rows into short lines.

One caveat from the page itself: its Web version notes that the newer formatting is only available on Web and Mac desktop, while the Android and iPhone versions show the same syntax without that note. Treat lists and quotes as a bonus. `- item` and `1. item` still read fine as plain text where they do not render.

A few rules keep formatting from looking like a document. Use bold on one word or phrase per message at most. Use a list only for three or more parallel items, such as steps. Never send a table. Put one link in a message: Meta's [text message reference](https://developers.facebook.com/docs/whatsapp/cloud-api/messages/text-messages) for its business API says the WhatsApp client renders a preview of only the first URL in a text body.

## What should the system prompt say?

A system prompt for a WhatsApp agent should state style rules as numbers and examples, because adjectives like "concise" and "friendly" mean little to a model. This template covers the behavior above, and you fill the persona line yourself:

```text
You are Ava, texting with {{name}} on WhatsApp. {{one line: who you are, how you talk}}

How you write:
- You are texting, not writing an email. One thought per message, one to three sentences.
- Send at most 3 messages per turn. Put a blank line between messages: each paragraph is sent as its own message.
- Match their length and energy. If they write four words, do not answer with forty.
- Ask at most one question per turn, and only when you need the answer. Do not end every turn with one.
- Use plain words. No "Certainly", no "I hope this helps", no "As an AI language model".
- Emoji: at most one per turn, and only if they use them. Often none.

Formatting (WhatsApp, not Markdown):
- Bold is *one asterisk on each side*. Italic is _underscores_.
- Never use #, tables, **double asterisks** or [text](url) links. Write the URL plainly.
- Use a list only for three or more parallel items, one per line, starting with "- " or "1. ".
- One link per message, on its own line.

If they send a voice note, a photo or a file, you get a transcript or a description in square brackets. Answer as if you heard or saw it.
If they ask if you are an AI, say yes in one sentence and carry on.
If they ask you to stop writing to them, confirm once in one short message.
```

Four choices in it matter. The blank-line rule gives your code a reliable place to split. The numbers ("one to three sentences", "at most 3 messages") are testable, which a vague "keep it short" is not. The AI line comes from a safety point, not a style one: sounding like a person is a way of writing, not permission to deny being an AI, and [AI companion safety rules](https://whasp.ai/blog/ai-companion-safety-rules-2026) covers where disclosure is a legal duty. Last, the name: the `message.received` event carries `sender_name`, the name the person gave themselves on WhatsApp. It is text they control, so treat it as data and never as instructions.

## How do you enforce the rules in code?

Enforce the prompt's rules in code, because models drift. This TypeScript file converts Markdown to WhatsApp syntax, splits on blank lines and sends each part through `POST /v1/messages`:

```ts
// Turn the Markdown a model likes to write into WhatsApp's own syntax.
export function toWhatsApp(md: string): string {
  return md
    .replace(/\r\n/g, "\n")
    .replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, "$1 $2") // [label](url) -> label url
    .replace(/^#{1,6}\s+(.+)$/gm, (_, t: string) => `*${t.replace(/\*/g, "")}*`) // # Title -> *Title*
    .replace(/\*\*(.+?)\*\*/g, "*$1*") // **bold** -> *bold*
    .replace(/__(.+?)__/g, "*$1*") // __bold__ -> *bold*
    .replace(/^\s*(?:-{3,}|\*{3,}|_{3,})\s*$/gm, "") // horizontal rules
    .replace(/\n{3,}/g, "\n\n")
    .trim();
}

// One paragraph = one message. Long paragraphs break at sentence ends.
export function splitReply(text: string, maxParts = 4, maxChars = 700): string[] {
  const parts: string[] = [];
  for (const para of text.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean)) {
    if (para.length <= maxChars) {
      parts.push(para);
      continue;
    }
    let current = "";
    for (const sentence of para.split(/(?<=[.!?])\s+/)) {
      if (current && current.length + sentence.length + 1 > maxChars) {
        parts.push(current);
        current = sentence;
      } else {
        current = current ? `${current} ${sentence}` : sentence;
      }
    }
    if (current) parts.push(current);
  }
  // Over the cap: fold the tail into the last allowed message, up to 4,000 characters.
  while (parts.length > maxParts && parts.at(-2)!.length + parts.at(-1)!.length < 4000) {
    parts.splice(-2, 2, `${parts.at(-2)}\n\n${parts.at(-1)}`);
  }
  return parts.flatMap((p) => p.match(/[\s\S]{1,4000}/g) ?? []); // whasp text limit is 4,096
}

export async function sendReply(to: string, reply: string, eventId: string): Promise<void> {
  const parts = splitReply(toWhatsApp(reply));
  for (const [i, text] of parts.entries()) {
    const res = await fetch("https://api.whasp.ai/v1/messages", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.WHASP_API_KEY}`,
        "Content-Type": "application/json",
        // Same event and same part always get the same key, so a retry never double-sends.
        "Idempotency-Key": `${eventId}:${i}`,
      },
      body: JSON.stringify({ to, text }),
    });
    if (res.status !== 202) {
      const { error } = (await res.json()) as { error: { code: string; message: string } };
      throw new Error(`${error.code}: ${error.message}`); // chat_opted_out, number_unavailable...
    }
  }
}
```

The loop awaits each send, so the parts reach whasp in order, and whasp then sends a number's messages one at a time, in order. The `Idempotency-Key` is built from the webhook event id and the part index. whasp keeps a key for 7 days and returns the first message for a repeated key, so a webhook retry cannot send the same part twice. A refused send (a 4xx) does not use up its key.

Tune `maxChars` and `maxParts` to the persona: a terse assistant might use 300 and 3, a storyteller 900 and 5. A regex cannot repair everything. If a model writes a Markdown table, the converter above leaves it alone, and the fix belongs in the prompt. The `*italic*` of Markdown becomes bold in WhatsApp, which is one more reason the template tells the model to use underscores. Wire `sendReply` into the webhook flow from [building an agent with the OpenAI API](https://whasp.ai/blog/whatsapp-ai-agent-openai), and keep the signature check from the whasp [docs](https://whasp.ai/docs).

## How fast should the agent reply?

An agent on whasp should reply fast and without artificial delay, because a number cannot show a typing indicator. Typing indicators are not supported, so a reply that takes 40 seconds looks like silence, and a delay you add on purpose looks the same. Use a fast model for ordinary chat turns. For work that is slow by nature, such as a tool call or a search, send a short holding message ("give me a minute, checking") and the answer after it.

A split reply is also the pacing. whasp sends one number's messages one at a time, about a second apart, so three parts arrive as a quick burst, which is how people text.

Budget the parts. A whasp number can send 1,500 messages in 24 hours, and a number is built for about 100 active users. That works out to roughly 15 sent messages per user per day on average, and a four-part reply uses a quarter of that in one turn. Past the limit, sends fail with 429 `number_daily_limit`. [Scaling to more numbers](https://whasp.ai/blog/scale-whatsapp-ai-agent-multiple-numbers) covers the capacity math.

## What tone reads as a person?

A WhatsApp agent reads as a person when it follows the other side's lead and does not perform. A few habits do most of the work:

- **Mirror the length.** A one-word message gets one line back. A long message about a bad day gets a longer reply, still in short paragraphs.
- **Reply in their language.** If they switch from English to Spanish, so does the agent.
- **Skip the sign-off.** No "Let me know if you need anything else" at the end of every turn.
- **Do not echo.** Do not restate their message before answering it.
- **Ask one thing.** Two questions in one message get one answer, usually the easier one.
- **Greet once.** Say hello in the first message of a conversation, or after a gap of days, not on every turn.
- **Pick, do not list.** Asked what to cook, a friend says "pasta, you have everything for it", not five options.
- **Admit gaps briefly.** "No idea, honestly" beats three sentences of hedging.
- **Name sparingly.** Using the name in every message sounds like a call-center script.

Companions need more than style: a stable persona, memory of what the person said last week, and rules for sensitive moments. [Launching an AI companion on WhatsApp](https://whasp.ai/blog/ai-companion-on-whatsapp) covers the whole path, and [conversation memory](https://whasp.ai/blog/whatsapp-ai-agent-memory) covers the part that makes "last week" possible.

To check the result, read real transcripts, not just the prompt. Keep five fixed test messages and rerun them whenever you change the model or the prompt: a one-word message, a long message about a bad day, a request that needs steps, "are you an AI?", and a message in another language. A quick automated pass helps too: count how often a reply contains `**`, a `#` heading or more than four parts, and track the median message length. Those numbers drift whenever the model does.

## What to do next

Connect this to a live number. [Build the webhook and model call with the OpenAI API](https://whasp.ai/blog/whatsapp-ai-agent-openai), add [voice notes and photos](https://whasp.ai/blog/whatsapp-voice-notes-ai-agent) so the agent can answer what people actually send, and store the history yourself, because whasp's servers keep no messages. The quickstart in the [whasp docs](https://whasp.ai/docs#quickstart) gets a number, a webhook and a first reply running, and [pricing](https://whasp.ai/pricing) lists what a number costs.

## Questions

### Does WhatsApp support Markdown?

Not Markdown as such. WhatsApp's help page documents its own syntax: _italic_, *bold*, ~strikethrough~, three backticks for monospace, lists that start with *, - or 1., quotes that start with > and inline code in single backticks. It lists nothing for headings, tables or link text.

### How do I stop a WhatsApp bot from sending walls of text?

Put the limit in the system prompt as numbers, for example one to three sentences per message and at most three messages per turn. Then enforce it in code by splitting the reply on blank lines and capping the number of parts.

### How long can one WhatsApp message from the whasp API be?

The text field takes up to 4,096 characters. For chat, stay far below that: a message of a few hundred characters reads like a text, a few thousand reads like an email.

### Can my agent show a typing indicator while the model thinks?

No. whasp numbers are phones driven like a person, and typing indicators are not supported. Use a fast model for chat turns, and for slow work send a short holding message first.

### Should an agent that texts like a person say it is an AI?

Yes, when asked. Sounding natural is a style choice, not permission to deny being an AI. Put a line in the system prompt that has the agent say so in one sentence and carry on.
