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

# Send transactional messages

> Send one WhatsApp or email message every time something happens in your system, like a payment link or an order confirmation.

A **Transactional** sends a message every time an event happens in your system:
a payment link is created, an order is received, an appointment is booked. Your
system sends Boom the event, and Boom sends the customer a WhatsApp template, an
email template, or both.

It is the simplest kind of initiative. There is no conversation to design, no
audience to pick and no schedule: one event, one message per channel.

<Note>
  Use a [Campaign](/campaigns) to message a list of people once, and an **Initiative**
  when you need waits, reminders, a conversation with the agent, or sends held
  for approval. See [initiatives](/initiatives) and [drafts](/drafts).
</Note>

## How it works

1. Your system records an event, for example `payment_link_created`, for a
   person.
2. Boom starts one run for that event and sends each message you set up: the
   email first, then the WhatsApp.
3. The run ends. Every event starts its own run, so two payment links created at
   the same time send two messages.

Each run is identified by one field of the event that you choose, like
`paymentLinkId`. **Each value sends once, ever, per customer**: if an event with the same
`paymentLinkId` arrives again, no second message goes out. That is what makes
retries from your side safe.

## Before you start

* **Transactional turned on for your organization.** Boom enables it per
  organization. If you don't see **Transactional** in the sidebar, or creating
  one over the API returns `transactional_not_ready`, ask your Boom contact.
* **A channel.** A connected WhatsApp number, a verified email domain, or both.
* **A template per channel.** A WhatsApp template approved by Meta, or a
  published email template. Its variables (like `{{1}}` or `{{customer_name}}`)
  are filled from the event or from the customer's profile.
* **An API key** for your organization, to send events. See
  [authentication](/authentication).

## Set it up

<Steps>
  <Step title="Create the Transactional">
    In Boom, open **Transactional** in the sidebar and create a new one. Give it a
    **Name** (picking the event names it for you until you change it). Everything
    below is on the same screen.
  </Step>

  <Step title="Pick the messages">
    Under **Message**, click **Add a message**, then pick a channel and its
    template. Add a second message to send on both channels. On an email, the
    **From** chip picks the sender; leave it to use the template's.
  </Step>

  <Step title="Pick the event">
    Under **When it sends**, click **Choose the event that sends it** and pick the
    event. If your system has not sent it yet, type its name. Event names use
    letters, numbers and underscores only: `payment_link_created`, not
    `payment_link.created`.
  </Step>

  <Step title="Fill the template">
    For each template variable, choose where its value comes from: a field of the
    event (like `customerName` or `paymentUrl`) or a field of the customer's
    profile (like their first name). A field the event has not sent yet can be
    typed as a new field. **Preview** on each message shows it as the customer
    gets it.

    Every variable must be filled. A message with an empty variable would reach
    the customer with the raw `{{1}}` in it, so Boom refuses to launch until
    each one is set.
  </Step>

  <Step title="Choose how many notifications per customer">
    **One per event** (the default) sends the notification every time the event
    arrives. **One per field value** sends once per value of a field you pick,
    usually an id such as `orderId`: use it when a customer can have several at
    once, or when a retry must never send twice. See
    [how many notifications a customer gets](/transactional-sends).
  </Step>

  <Step title="Launch">
    Click **Launch**. From now on, every matching event sends a message. Not ready
    yet? **Save draft** keeps it, and the line next to the buttons says what is
    still missing. **API request**, next to the event, shows the exact request
    your system has to send.
  </Step>
</Steps>

## Send the event

Make sure the customer exists, with the phone number and email Boom should use:

```bash theme={null}
export BOOM_KEY="boom_org_YOUR_KEY_HERE"
export BASE="https://www.useboom.ai/api/v1/cdp"

curl -X POST "$BASE/people" \
  -H "Authorization: Bearer $BOOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{"externalId":"cus_42","email":"ana@example.com","phoneNumber":"+5215512345678"}'
```

<Warning>
  Saving a person **replaces their whole profile**. A field you leave out is
  cleared, including `phoneNumber`, `email` and every key in `attributes`.
  Always send the complete profile, or only save the person when it changes.
  Otherwise a save that sends only the email removes the phone number, and the
  WhatsApp stops going out.
</Warning>

Then record the event each time it happens:

```bash theme={null}
curl -X POST "$BASE/events" \
  -H "Authorization: Bearer $BOOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "payment_link_created",
    "externalId": "evt_001",
    "personExternalId": "cus_42",
    "properties": {
      "paymentLinkId": "pl_9",
      "customerName": "Ana",
      "paymentUrl": "https://pay.example.com/pl_9"
    }
  }'
```

* `name` is the event you picked in step 2.
* `externalId` is your id for this event. Recording the same `externalId`
  twice stores it once, but with **one per event** a retry still sends again.
  With **one per field value**, a value that already sent never sends again.
* `personExternalId` is the customer's id in your system. Their phone and email
  come from their profile, not from the event.
* `properties` carries the fields your messages use, plus the field you picked
  for **one per field value**, if you did.

Use the single-event endpoint. Batch recording stores events but does not send
messages. See [events](/events) for the full reference.

<Tip>
  **API request**, next to the event on the Transactional's page, shows this
  request with your own event name and fields, and marks what each field is used
  for. Copy it from there.
</Tip>

## See how it's doing

The Transactional's **Analytics** page shows how its messages arrive. Its
**Detail** page shows only the setup.

* **Runs**, one per event, and how many people they reached.
* **Delivery rate**: delivered out of every message sent, failed ones included.
* **Open or read rate**: emails opened and WhatsApp messages read, out of those
  delivered. Email opens are counted only once open tracking is set up in
  **Settings › Email**; until then the page says so and shows no email open
  rate. Some inboxes block the tracking pixel, so email opens still run low.
* **Reply rate**: WhatsApp messages the customer answered within 24 hours,
  before any other message reached them in that chat. Email replies are not
  tracked.
* **Why messages didn't arrive**: failures by reason (an address that bounced,
  a number that isn't on WhatsApp) kept apart from messages that were never
  sent (no email address, no WhatsApp number, Smart Sending).

Pick a range (today, 7 or 30 days, or your own dates) and a channel. Each number
is compared with the same length of time just before. Until your system sends
the first event, the page shows the request to send instead.

## Do it from code or an agent

Everything above can also be done over the [API or MCP](/use-mcp), in three
calls:

1. **Create it with the whole setup**: `initiatives_create`
   (`POST /api/v1/initiatives`) with a `transactional` object.
2. **Check it is ready**: `initiatives_transactional_get`
   (`GET /api/v1/initiatives/{id}/transactional`) returns `readyToLaunch` and
   a `missing` list, each item with how to fix it.
3. **Launch**: `initiatives_launch` (`POST /api/v1/initiatives/{id}/launch`).

```bash theme={null}
curl -X POST "https://www.useboom.ai/api/v1/initiatives" \
  -H "Authorization: Bearer $BOOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Payment link",
    "transactional": {
      "eventName": "payment_link_created",
      "parallelRunsBy": "paymentLinkId",
      "whatsapp": {
        "templateId": "tpl_123",
        "channelId": "ch_456",
        "templateBindings": {
          "1": "person.firstName",
          "2": "engagement.workflowState.paymentUrl"
        }
      },
      "email": { "templateId": "etpl_789" }
    }
  }'
```

A binding names where a template variable's value comes from:
`engagement.workflowState.<property>` for a property of the event,
`person.<field>` for a field of the customer's profile, or `literal:<text>`
for fixed text. Every WhatsApp placeholder needs one. Email variables without
one use the template's own value.

To change it later, send only what changes to
`initiatives_transactional_configure`
(`PATCH /api/v1/initiatives/{id}/transactional`); `null` removes a field. A
launched Transactional starts using the change right away.

Find the ids with `journeys_message_channels` (WhatsApp numbers),
`journeys_message_templates` (approved WhatsApp templates for a number) and
`journeys_email_templates` (published email templates).

| Error | What it means |
| - | - |
| `422 transactional_not_ready` | The Transactional cannot be created as asked, for example because a template is not approved. |
| `422 journey_not_ready` | Launch was refused because the setup is incomplete, for example a variable nothing fills. The message says what to fix. |
| `422 transactional_event_only` | People cannot be added by hand. A Transactional starts only from its event. |
| `400 transactional_not_recurring` | A Transactional cannot also be recurring. |
| `400 not_transactional` | A Transactional tool was called on another kind of initiative. |

## Troubleshooting

**Nothing was sent.** Check that the Transactional is launched, that the event
name matches exactly, and that you used the single-event endpoint. Then open
**Executions** on the Transactional to see each event it received.

**The customer did not get the WhatsApp.** Their profile needs a phone number in
E.164 format (`+52...`). If it has one, Meta may have declined to deliver the
template. The Transactional then counts it as **failed**, not sent, and the
inbox shows why. The most common reason is error `63049`: Meta limits how many
template messages one person receives. The limit is **per recipient**, not only
per burst. A number that received several templates it didn't answer can have
every later template dropped for hours, even a single one. What helps:

* File order and payment messages under the **Utility** category, not
  Marketing. Meta limits Marketing templates much more.
* Don't send the same person several templates within seconds. Combine them
  into one message when you can.
* Expect some drops, and send the email too for anything the customer must
  receive.

**The email did not go out.** The email domain must be verified in
**Settings › Email**, and the customer needs an email on their profile.

**The email went to spam.** A new sending domain has no reputation yet. Make
sure SPF, DKIM and DMARC all pass for the domain you send from. Send from a
real address like `pagos@yourcompany.com` rather than `no-reply@`, and start
with a low volume that you raise over a few days.

**A tool or field from the docs is missing in your agent.** MCP clients keep
the tool list they loaded when they connected. After a Boom release, reconnect
the Boom MCP server so the client picks up new tools and fields.

**A second event sent nothing.** Its identifying field had a value that was
already sent to that customer. Each value sends once, ever. See
[how each send is identified](/transactional-sends).

**Launch is disabled.** The line next to the buttons, starting with "Before
launching", names what is still missing, including any template variable
nothing fills.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.