> ## 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** 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 (drafts). See [initiatives](/initiatives).
</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. Click
    its title to rename it (picking the event names it for you until you do).
    Everything below is on the same screen.
  </Step>

  <Step title="Pick the event">
    Under **Choose when it sends**, pick the event that sends the message. 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="Pick the messages">
    Under **Choose which message to send**, click **Add a message**, pick a
    channel and its template. Add a second message to send on both channels.
  </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.

    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 what identifies each send">
    Under **What identifies each send?**, pick the event field that changes
    with every event, usually an id such as `paymentLinkId` or `orderId`. Each
    value is sent once per customer. See
    [how each send is identified](/transactional-sends).
  </Step>

  <Step title="Create and launch">
    Click **Create**, then **Launch** on the page that opens. From now on, every matching event sends a message. The
    panel on the right 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. What keeps a retry from messaging the customer twice is
  the field that identifies each send: 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 that
  identifies each send.

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

<Tip>
  The API tab in the Transactional's right-hand panel shows this request with
  your own event name and fields, and marks what each field is used for. Copy it
  from there.
</Tip>

## 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.** Every step of the setup list must be done, including
filling every template variable.
