Skip to main content

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.

Every outbound message goes through client.messages.send(params). The params shape is a discriminated — 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

await client.messages.send({
  to: "+15551234567",
  text: "Hello!",
});
Pass a string for quick sends, or a for richer options:
await client.messages.send({
  to: "+15551234567",
  text: { body: "Check this out: https://photon.codes", previewUrl: true },
});
OptionTypeDescription
bodystring
previewUrlboolean

Media

Images, videos, audio, and documents all share the same shape. Send by referencing an uploaded id or a public link:
// 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",
  },
});
OptionTypeDescription
captionstring
filenamestring
idstring
linkstring
mimeTypestring
See Media for the upload flow and size limits.

Content types

KeyAcceptsUse for
imageMediaInputJPEG, PNG
videoMediaInputMP4, 3GP
audioMediaInputAAC, MP4 audio, AMR, MP3, OGG
documentMediaInputAny file with a filename
stickerStickerInput (id or link only)WebP stickers

Location

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:
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.
await client.messages.send({
  to: "+15551234567",
  reaction: {
    messageId: "wamid.HBgMMTU1NT...",
    emoji: "❤️",
  },
});

Replies

Thread a reply by setting replyTo:
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:

Mark as read

Clear unread state on the recipient’s side:
await client.messages.markRead(event.message.id);

Send result

Every send() returns a — record messageId if you plan to reply to, react to, or track status for that message.
const { messageId, messageStatus } = await client.messages.send({
  to: "+15551234567",
  text: "Hello",
});

Abort signal

Every method accepts an optional RequestOptions with an AbortSignal:
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5000);

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