Operations

Welcome a digital product buyer on WhatsApp: one message per checkout

The welcome message is the safest and most useful of the whole digital product operation: the buyer just paid, wants to know where the access is and is receptive. The risk is technical, not about tone. Platforms send one event per purchase, and a checkout with an order bump is two or more purchases. Without handling, the student gets two welcomes in a row. This recipe fixes that.

Published on · 4 min read

This article is also available in: Português · Español

The right event on each platform

PlatformApproved purchase eventWhere the phone is
---------
Hotmart (2.0.0)PURCHASE_APPROVEDdata.buyer.checkout_phone and checkout_phone_code (area code separate for Brazilians)
Kiwifycompra_aprovada (webhook_event_type order_approved, order_status paid)Customer block of the payload; check in Test Webhook
Eduzzmyeduzz.invoice_paiddata.buyer.cellphone, phone and phone2

For subscriptions, each approved renewal is a new purchase. Kiwify sends compra_aprovada and subscription_renewed together; for the welcome, use only the first purchase (the subscription_id is the same on every charge, so record who was already welcomed). Details per platform are on Hotmart, Kiwify and Eduzz.

No-code path and what it covers

In Fluxos, Recuperar vendas, choose the platform and the goal 'Post-purchase welcome'. The assistant creates a flow with a single message, which uses {nome} and {produto}, and generates the webhook URL. Paste the URL into the platform, tick the approved purchase event and enter the platform's key in the assistant to validate the origin. In a few minutes the buyer starts receiving the message.

The starter text is short and generic; replace it with your voice. A good pattern: thanks, what the buyer received, where the access is and how to ask for help. Do not put a password or a single-use personal access link in plain text on WhatsApp if your platform already delivers that securely by email; use WhatsApp to say the email went out and to answer questions.

The order bump problem

An order bump is the extra offer ticked at checkout. To the platform, it is another purchase, with another event. If you send a welcome per event, the buyer gets one per product. On Hotmart, the payload says when the purchase is a bump (purchase.order_bump.is_order_bump) and carries the main purchase id (parent_purchase_transaction); when that id comes empty for a bump, the purchase itself is the main one. On Eduzz, the data.orderBump.mainSaleId field points to the main invoice.

The rule is: one welcome per checkout. Compute the checkout key (the main transaction), try to 'claim' that key in a table with UNIQUE and only send if you succeed. The no-code assistant does not group by checkout: it ignores the same phone on the same flow for 10 minutes, which covers many bump cases, but is not a guarantee if the events arrive more than 10 minutes apart. If that matters to you, use the code below.

Node.js (Express), Hotmart 2.0.0

// Hotmart 2.0.0: one welcome per checkout, even with order bumps
app.post("/hotmart", express.json(), async (req, res) => {
  if (req.get("X-HOTMART-HOTTOK") !== process.env.HOTMART_HOTTOK) return res.sendStatus(401);
  const { id, event, data } = req.body;
  res.sendStatus(200);
  if (event !== "PURCHASE_APPROVED") return;
  if (!(await saveEventOnce(id, req.body))) return; // duplicate delivery

  // An order bump is a separate purchase. The parent transaction id is the same for the whole checkout.
  const ob = data.purchase.order_bump;
  const parent = ob?.is_order_bump && ob.parent_purchase_transaction
    ? ob.parent_purchase_transaction
    : data.purchase.transaction;
  if (!(await claimWelcome(parent))) return;        // UNIQUE(parent): only the first purchase of the checkout welcomes

  const b = data.buyer;
  const phone = String((b.checkout_phone_code || "") + (b.checkout_phone || "")).replace(/\D/g, "");
  if (phone.length < 10) return;                     // no usable phone: log it, never guess
  await sendWhatsApp(phone.startsWith("55") ? phone : "55" + phone,
    "Oi, " + b.first_name + "! Sua compra de " + data.product.name + " foi confirmada. O acesso chega no e-mail " + b.email + ". Se não encontrar, olhe o spam ou responda aqui.");
});

// Shared helper: one send through the ZapZap API
async function sendWhatsApp(number, text) {
  const r = await fetch(`https://api.zapzapapi.com/api/v1/${process.env.ZAPZAP_INSTANCE}/send/text`, {
    method: "POST",
    headers: {
      "x-api-key": process.env.ZAPZAP_API_KEY,
      "x-api-secret": process.env.ZAPZAP_API_SECRET,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ number, text }),
  });
  if (!r.ok) throw new Error("ZapZap " + r.status + ": " + (await r.text()));
}

Missing phone: what to do and what not to do

Not every buyer leaves a phone, and on an affiliate product the producer must release the buyer's data. If the phone does not arrive, the right path is to log it and move on: do not try to guess a number, do not use a phone from another field (such as the producer's) and do not send to a number you 'found'. On Hotmart, a Brazilian buyer's phone may come without the area code, with the area code in checkout_phone_code; the code above joins the two and discards anything with fewer than 10 digits.

ZapZap only fires through the assistant when it detects a phone of 10 to 13 digits. When the payload brings the number split into two fields, the assistant does not join them; use the endpoint from the example.

A transactional message is not a campaign

The welcome is transactional: it happens because of a purchase, to the person who bought. That separates it from a campaign to your base, which needs specific consent and a different pace. If you use the message to push another offer, it stops being only transactional and the risk changes. If you want to make an offer later, wait and ask permission: 'Want to get tips and news here? Reply YES'.

Include an exit in every message and respect it. See the guide on LGPD on WhatsApp. ZapZap connects numbers through an unofficial, managed channel; one welcome message per purchase, with no repetition, is the lowest-risk use.

Quick summary

  • The events are PURCHASE_APPROVED (Hotmart), compra_aprovada (Kiwify) and myeduzz.invoice_paid (Eduzz).
  • An order bump is another purchase: group by the main transaction and send one welcome per checkout.
  • The assistant covers the simple case; your own endpoint solves bumps more than 10 minutes apart and phones without an area code.
  • A welcome is transactional: do not mix a new offer into the same message.

Also read

Frequently asked questions

The buyer received two welcomes. Why?

Probably because of an order bump. To the platform, the bump is another purchase with another event. Group by the main transaction (on Hotmart, parent_purchase_transaction; on Eduzz, orderBump.mainSaleId) and send one message per checkout.

Can I send the access link on WhatsApp?

Prefer to say the access went out by email and offer help. A personal link or a password in text on WhatsApp exposes the student if the device is shared. If the link is the course's public one, it is fine.

Does the welcome need consent?

It is a transactional message to someone who just bought, which has a different basis than a campaign. Even so, state the channel at checkout, offer an exit and respect a request to stop. For offers, ask for specific consent.

Does it work for recurring subscriptions?

For the first purchase, yes. For renewals, each approved charge creates a new event: record who already got a welcome so you do not repeat it. On Kiwify, the subscription_id is the same on every charge.

My buyer received nothing. What should I check?

Three points: whether the event arrived (the platform's history), whether the ZapZap response was triggered: 1 and whether the phone has 10 to 13 digits. On Hotmart, the area code may come separate from the number.

Can I use the same instance for everything?

You can, but the number is one and pace matters. Transactional messages per purchase are usually low volume, which helps. In a launch with peaks, consider the limit of 120 requests per minute per webhook and the send pace.

Welcome buyers the minute they purchase

Connect your platform in Fluxos, Recuperar vendas, or use the recipe code. R$15 per active number per month, with a welcome credit subject to the current campaign.