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.
Interactive messages let the recipient tap a button, pick from a list, or launch a WhatsApp Flow instead of typing a reply. Pass the result to send under the interactive key — the SDK ships with builders so you rarely construct the raw by hand.
import { buttons, button } from "@photon-ai/whatsapp-business";
await client.messages.send({
to: "+15551234567",
interactive: buttons(
"How would you like to continue?",
button("confirm", "Confirm order"),
button("cancel", "Cancel"),
),
});
Up to three buttons, each with an ID you’ll receive back on tap:
import { buttons, button } from "@photon-ai/whatsapp-business";
const msg = buttons(
"Your package is ready for pickup. What now?",
button("pickup_now", "Pick up now"),
button("reschedule", "Reschedule"),
button("hold", "Hold 3 days"),
);
await client.messages.send({ to, interactive: msg });
When the user taps, you’ll receive an interactive event:
for await (const event of client.events.subscribe()) {
if (event.type !== "message") continue;
if (event.message.content.type !== "interactive") continue;
const { interactive } = event.message.content;
if (interactive.type === "button_reply") {
console.log(interactive.reply.id); // "pickup_now"
console.log(interactive.reply.title); // "Pick up now"
}
}
Lists
Lists support up to ten rows grouped into sections. The builder is immutable — chain .section() to add content:
import { list } from "@photon-ai/whatsapp-business";
const menu = list("Pick a drink", "Open menu")
.section("Hot", [
{ id: "coffee", title: "Coffee", description: "House blend" },
{ id: "tea", title: "Tea" },
])
.section("Cold", [
{ id: "iced", title: "Iced coffee" },
{ id: "smoothie", title: "Smoothie" },
])
.withHeader({ type: "text", text: "Menu" })
.withFooter("Ships in 5 minutes");
await client.messages.send({ to, interactive: menu });
List taps arrive as list_reply:
if (interactive.type === "list_reply") {
console.log(interactive.reply.id); // "coffee"
console.log(interactive.reply.description); // "House blend"
}
Product and product list
Single product — opens the catalog page:
import { product } from "@photon-ai/whatsapp-business";
await client.messages.send({
to,
interactive: product("catalog-123", "SKU-456"),
});
Multi-product, grouped by category:
import { productList } from "@photon-ai/whatsapp-business";
const catalog = productList("catalog-123", "Spring collection")
.section("New arrivals", ["SKU-100", "SKU-101", "SKU-102"])
.section("Back in stock", ["SKU-050", "SKU-051"])
.withFooter("Free shipping on orders over $50");
await client.messages.send({ to, interactive: catalog });
Orders placed through a product message arrive as an order inbound content type — see Events.
Flows
Launch a WhatsApp Flow (a structured form experience) with the flow helper:
import { flow } from "@photon-ai/whatsapp-business";
await client.messages.send({
to,
interactive: flow({
body: "Book an appointment",
parameters: {
flowId: "1234567890",
flowToken: "your-flow-token",
flowCta: "Book now",
flowMessageVersion: "3",
flowAction: "navigate",
flowActionPayloadJson: JSON.stringify({ screen: "START" }),
},
}),
});
Flow submissions arrive as nfm_reply:
if (interactive.type === "nfm_reply") {
const data = JSON.parse(interactive.reply.responseJson);
// handle the structured form response
}
Building raw interactive messages
If you need a shape the builders don’t cover, pass a plain :
await client.messages.send({
to,
interactive: {
type: "button",
body: "...",
action: {
buttons: [{ type: "reply", reply: { id: "a", title: "A" } }],
},
},
});
The builders are thin wrappers — they exist to catch typos and make common shapes ergonomic, not to restrict what you can send.