Send a transactional email
This tutorial sends a transactional email with the BoostMail API, from minting a key to handling the send receipt. You mint a key, set it as an environment variable, create a subscriber to send to, send the transactional email, and branch on the response.
Prerequisites
Section titled “Prerequisites”Before you start, you need:
- An API key minted with the
subscribers:writeandtransactional:sendscopes. Mint one in the BoostMail dashboard: see the Authentication guide for the key format and the full scope list. - A transactional campaign set up in your BoostMail dashboard, under Campaigns. No
/v1endpoint creates one, so you set it up in the dashboard first. Note its id: this tutorial uses the placeholder<your-transactional-campaign-id>for it.
Set your key as an environment variable:
export BOOSTMAIL_API_KEY=bm_live_...-
Mint a key with the
subscribers:writeandtransactional:sendscopes in the BoostMail dashboard. BoostMail shows the secret once, so copy it before you navigate away. -
Set the key in your shell:
Terminal window export BOOSTMAIL_API_KEY=bm_live_... -
Create a subscriber to send to. Transactional send does not require the recipient to already be a subscriber (see step 4), but this gives you a known recipient to test against.
Terminal window curl -X POST https://api.boostmail.app/v1/subscribers \-H "Authorization: Bearer $BOOSTMAIL_API_KEY" \-H "Content-Type: application/json" \const response = await fetch('https://api.boostmail.app/v1/subscribers', {method: 'POST',headers: {Authorization: `Bearer ${process.env.BOOSTMAIL_API_KEY}`,'Content-Type': 'application/json',},});const subscriber = await response.json();import osimport requestsresponse = requests.post("https://api.boostmail.app/v1/subscribers",headers={"Authorization": f"Bearer {os.environ['BOOSTMAIL_API_KEY']}"},)subscriber = response.json()If you run this again with the same email, BoostMail returns
422 validation_failedinstead of creating a duplicate:{"error": {"code": "validation_failed","message": "validation_failed","details": [{"field": "email", "code": "already_exists", "message": "A subscriber with this email already exists"}]}}This is expected, not a bug. Use a fresh email for a clean run, or treat the
422as confirmation the subscriber is already there. -
Send the transactional email. The call requires an
idempotency-keyheader: generate a fresh value (a UUID works well) for each new logical send, and reuse that same value on every retry of it. Use thecampaign_idof the transactional campaign from the Prerequisites.Terminal window curl -X POST https://api.boostmail.app/v1/transactional/send \-H "Authorization: Bearer $BOOSTMAIL_API_KEY" \-H "Content-Type: application/json" \-H "idempotency-key: <uuid>" \-d '{"campaign_id": "<your-transactional-campaign-id>","email": "[email protected]"}'const response = await fetch('https://api.boostmail.app/v1/transactional/send', {method: 'POST',headers: {Authorization: `Bearer ${process.env.BOOSTMAIL_API_KEY}`,'Content-Type': 'application/json','idempotency-key': '<uuid>',},body: JSON.stringify({campaign_id: '<your-transactional-campaign-id>',}),});const receipt = await response.json();import osimport requestsresponse = requests.post("https://api.boostmail.app/v1/transactional/send",headers={"Authorization": f"Bearer {os.environ['BOOSTMAIL_API_KEY']}","idempotency-key": "<uuid>",},json={"campaign_id": "<your-transactional-campaign-id>",},)receipt = response.json()You can also pass an optional
template_dataobject with any merge fields your transactional campaign template uses. It is part of the idempotency fingerprint: reusing the sameidempotency-keywith differenttemplate_datachanges the fingerprint and returns409 conflict(idempotency-key reuse), it does not send a second message. To send a genuinely different message, use a newidempotency-key. -
Handle the receipt. A
200response always carries astatusfield: branch on it before reading anything else.A non-suppressed send returns:
{"status": "scheduled","message_id": "<id>","scheduled_at": "<iso-8601>"}A suppressed send returns:
{"status": "suppressed","reason": "hard_bounce"}reasonishard_bounceorspam_complaint. A suppressed receipt means BoostMail accepted the request but enqueued nothing: nomessage_idorscheduled_aton that branch. See the Suppression guide for what causes a recipient to be suppressed.