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

# Messages

> Send text, media, location, contacts, and reactions

export const TypeTooltip = ({name, type, children}) => {
  const [visible, setVisible] = React.useState(false);
  const [pos, setPos] = React.useState({
    top: 0,
    left: 0
  });
  const triggerRef = React.useRef(null);
  const show = () => {
    if (triggerRef.current) {
      const rect = triggerRef.current.getBoundingClientRect();
      setPos({
        top: rect.bottom + 6,
        left: rect.left
      });
    }
    setVisible(true);
  };
  const hide = () => setVisible(false);
  return <>
      <span ref={triggerRef} onMouseEnter={show} onMouseLeave={hide} style={{
    cursor: "pointer",
    position: "relative",
    display: "inline"
  }}>
        {children || <code>{name}</code>}
      </span>
      {visible && <span style={{
    position: "fixed",
    top: pos.top,
    left: pos.left,
    zIndex: 9999,
    padding: "8px 12px",
    borderRadius: "8px",
    fontSize: "13px",
    lineHeight: "1.5",
    fontFamily: "'Azeret Mono', monospace",
    whiteSpace: "pre",
    backgroundColor: "var(--tw-prose-pre-bg, #1e1e1e)",
    color: "var(--tw-prose-pre-code, #e5e5e5)",
    border: "1px solid var(--border, rgba(128,128,128,0.2))",
    boxShadow: "0 4px 16px rgba(0,0,0,0.3)",
    pointerEvents: "none"
  }}>
          {type}
        </span>}
    </>;
};

Every outbound message goes through `client.messages.send(params)`. The `params` shape is a discriminated <TypeTooltip name="SendMessageParams" type={`type SendMessageParams = {
readonly to: string;
readonly replyTo?: string;
readonly bizOpaqueCallbackData?: string;
} & MessageContent;`} /> — common fields (`to`, optional `replyTo`, optional `bizOpaqueCallbackData`) plus exactly one content key.

## Recipient format

`to` is a WhatsApp ID — the recipient's phone number in international format. Either `+15551234567` or `15551234567` is accepted.

## Text

```ts theme={null}
await client.messages.send({
  to: "+15551234567",
  text: "Hello!",
});
```

Pass a string for quick sends, or a <TypeTooltip name="TextInput" type={`interface TextInput {
readonly body: string;
readonly previewUrl?: boolean;
}`} /> for richer options:

```ts theme={null}
await client.messages.send({
  to: "+15551234567",
  text: { body: "Check this out: https://photon.codes", previewUrl: true },
});
```

<Accordion title="TextInput" description="">
  | Option       | Type      | Description |
  | ------------ | --------- | ----------- |
  | `body`       | `string`  |             |
  | `previewUrl` | `boolean` |             |
</Accordion>

## Media

Images, videos, audio, and documents all share the same <TypeTooltip name="MediaInput" type={`interface MediaInput {
readonly caption?: string;
readonly filename?: string;
readonly id?: string;
readonly link?: string;
readonly mimeType?: string;
}`} /> shape. Send by referencing an uploaded `id` or a public `link`:

```ts theme={null}
// Upload first, then reference by ID
const { mediaId } = await client.media.upload({
  file: await readFile("/path/to/photo.jpg"),
  mimeType: "image/jpeg",
});

await client.messages.send({
  to: "+15551234567",
  image: { id: mediaId, caption: "Here's the photo" },
});

// Or point at a hosted URL
await client.messages.send({
  to: "+15551234567",
  document: {
    link: "https://example.com/report.pdf",
    filename: "Q1-report.pdf",
  },
});
```

<Accordion title="MediaInput" description="">
  | Option     | Type     | Description |
  | ---------- | -------- | ----------- |
  | `caption`  | `string` |             |
  | `filename` | `string` |             |
  | `id`       | `string` |             |
  | `link`     | `string` |             |
  | `mimeType` | `string` |             |
</Accordion>

See [Media](/advanced-kits/whatsapp/media) for the upload flow and size limits.

### Content types

| Key        | Accepts                          | Use for                       |
| ---------- | -------------------------------- | ----------------------------- |
| `image`    | `MediaInput`                     | JPEG, PNG                     |
| `video`    | `MediaInput`                     | MP4, 3GP                      |
| `audio`    | `MediaInput`                     | AAC, MP4 audio, AMR, MP3, OGG |
| `document` | `MediaInput`                     | Any file with a filename      |
| `sticker`  | `StickerInput` (id or link only) | WebP stickers                 |

## Location

```ts theme={null}
await client.messages.send({
  to: "+15551234567",
  location: {
    latitude: 37.422,
    longitude: -122.084,
    name: "Googleplex",
    address: "1600 Amphitheatre Pkwy, Mountain View, CA",
  },
});
```

## Contact cards

Send one or more vCard-style contact cards:

```ts theme={null}
await client.messages.send({
  to: "+15551234567",
  contacts: [
    {
      name: { formattedName: "Alice Example", firstName: "Alice" },
      phones: [{ phone: "+15559876543", type: "MOBILE" }],
      emails: [{ email: "alice@example.com" }],
      addresses: [],
      urls: [],
    },
  ],
});
```

## Reactions

React to an incoming message by ID. Use an empty string for `emoji` to remove a reaction.

```ts theme={null}
await client.messages.send({
  to: "+15551234567",
  reaction: {
    messageId: "wamid.HBgMMTU1NT...",
    emoji: "❤️",
  },
});
```

## Replies

Thread a reply by setting `replyTo`:

```ts theme={null}
await client.messages.send({
  to: event.message.from,
  replyTo: event.message.id,
  text: "Replying to your last message.",
});
```

## Interactive and templates

Rich messages — buttons, lists, product carousels, flows, and pre-approved templates — have their own pages:

* [Interactive messages](/advanced-kits/whatsapp/interactive-messages) — buttons, lists, flows, product messages
* [Templates](/advanced-kits/whatsapp/templates) — template builder, parameters, carousels

## Mark as read

Clear unread state on the recipient's side:

```ts theme={null}
await client.messages.markRead(event.message.id);
```

## Send result

Every `send()` returns a <TypeTooltip name="SendMessageResult" type={`interface SendMessageResult {
readonly messageId: string;
readonly messageStatus: string;
}`} /> — record `messageId` if you plan to reply to, react to, or track status for that message.

```ts theme={null}
const { messageId, messageStatus } = await client.messages.send({
  to: "+15551234567",
  text: "Hello",
});
```

## Abort signal

Every method accepts an optional `RequestOptions` with an `AbortSignal`:

```ts theme={null}
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5000);

await client.messages.send(
  { to: "+15551234567", text: "Hello" },
  { signal: ctrl.signal },
);
```
