Email is sent through the same messages.send endpoint as WhatsApp and SMS — just set channel: "email". The contact must already exist and have an email address. Out of the box, mail sends from a shared Blueticked address; connect your own sending domain to send as your brand.
Send a transactional email
Transactional email (statements, receipts, password resets) takes a subject and a plain-text body, rendered inside the branded shell with the required compliance footer:
await bt.messages.send({
channel: "email",
to: { email: "sam@example.com" },
subject: "Your statement is ready",
body: "Hi Sam, your May statement is attached in your account.",
message_type: "transactional",
reply_to: "accounts@yourbusiness.co.za",
});message_type defaults to transactional. Transactional mail bypasses marketing opt-outs (it is never blocked by an all marketing suppression), so reserve it for genuine service messages.
Send a marketing email (HTML)
For marketing email, set message_type: "marketing" and supply your own html. Marketing sends honour marketing opt-outs and double opt-in. cc and bcc are supported:
await bt.messages.send({
channel: "email",
to: { email: "sam@example.com" },
subject: "20% off this month",
message_type: "marketing",
html: "<h1>Winter sale</h1><p>20% off all services this month.</p>",
cc: ["partner@example.com"],
bcc: ["archive@yourbusiness.co.za"],
reply_to: "hello@yourbusiness.co.za",
});- Provide a
body(plain text) orhtml; HTML is rendered inside the branded shell with the compliance footer. - Pass
business_idwhen the workspace has more than one connected email channel (otherwise the API returnsbusiness_id_required). - Validate first with
test_mode: trueto check the contact, channel readiness, and opt-outs without sending anything — see Test mode.
Custom sending domain (DKIM/SPF)
Until you verify your own domain, email sends from the shared Blueticked address. Register a sending identity to send as your brand with proper DKIM/SPF authentication and better deliverability. The flow is: register, add the returned DNS records, then verify.
const domain = await bt.email.sendingDomain.register({
domain: "mail.yourbusiness.co.za",
from_local_part: "accounts", // sends as accounts@mail.yourbusiness.co.za
});
// domain.records lists the DNS entries to add at your registrar:
for (const rec of domain.records) {
console.log(rec.type, rec.name, rec.value); // e.g. TXT resend._domainkey p=MIGf...
}Add those records at your DNS provider, then trigger a re-check:
const verified = await bt.email.sendingDomain.verify();
console.log(verified.status); // "verified" once DNS has propagatedDNS can take minutes to hours to propagate, so poll verify() (or get()) until the status is verified. Once verified, every email send uses your domain automatically. Manage the identity:
// Read the current identity (null if none configured):
const current = await bt.email.sendingDomain.get();
// Revert to the shared Blueticked sender:
await bt.email.sendingDomain.remove();There is one sending identity per business — remove the current one before registering another.
Email webhooks
Beyond the standard message.* lifecycle events, email adds engagement and reputation events. Subscribe to them under Developers → Webhooks and verify every delivery as described in Webhooks and signing:
| Event | Fires when |
|---|---|
email.opened | A recipient opened the email (first open per message). |
email.clicked | A recipient clicked a link (the payload includes the url). |
email.bounced | The email hard-bounced; the address is auto-suppressed. |
email.complained | The recipient marked it as spam; the address is auto-suppressed. |
Bounces and complaints add the address to your suppression list automatically, so future sends to it are skipped without any action on your part.