| Audience | Marketers and developers |
| Prerequisites | A message built in Build an email or Build an SMS, and an API key |
An API-triggered campaign sends when your own systems call it, with the recipients and their personalization supplied on each call. Use it for messages your application decides to send, such as an order confirmation or a password reset.
Set up the campaign
- Go to Native Delivery → Campaigns and add a campaign.
- Set the Schedule type to API-triggered.
- Build your message content.
- Select Activate.
An API-triggered campaign has no audience and no schedule. The API trigger card takes the place of the schedule settings. It shows a Sample payload for this campaign, generated with your message's own personalization variables, that you can copy and run.
A campaign carries one message. Dynamic content isn't supported in API-triggered campaigns.
Trigger a send
Send a POST to the trigger endpoint with your API key as a bearer token. Create keys under Settings → API keys.
curl -X POST 'https://api.hightouch.com/api/v1/campaigns/{campaignId}/trigger' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"recipients": [
{ "handle": "sam@example.com", "properties": { "First name": "Sam" } }
]
}'
Copy the sample payload from the API trigger card instead of writing this by hand. It carries your campaign's ID, your workspace's regional host, and your message's actual variable names.
Personalization keys are the variable names as they appear in the editor, not the underlying column names. If two variables in one message share a name, the campaign won't activate until you rename one.
Up to 100 recipients per call. See the API reference for the full parameter list.
Recipients
Each recipient is identified one of two ways.
| Key | Use it when |
|---|---|
handle | You have the email address or phone number. Supply the personalization on the call. |
id | You have the profile's primary key. Hightouch reads that profile's attributes and delivery address for you. |
Sending by id requires a Warehouse attribute cache under Settings, plus a delivery address column mapped for the channel.
Personalization merges in order: the profile's own attributes, then any top-level properties, then that recipient's properties.
Read the response
A 200 returns one result per recipient, in the order you sent them.
{
"results": [
{
"handle": "sam@example.com",
"status": "accepted",
"sendId": "..."
},
{
"handle": "not-an-address",
"status": "rejected",
"reason": "invalid_handle"
}
]
}
accepted means the message is queued, not that it was sent. Subscription preferences, send limits, and deliverability are all checked afterward, so an accepted recipient can still be skipped.
Rejections are per recipient and don't fail the call. A batch where every recipient is rejected still returns 200.
| Reason | Meaning |
|---|---|
invalid_handle | Not a usable email address or phone number |
missing_variables | The message needs variables this call didn't supply. The names come back in missingVariables |
invalid_id | The id was empty |
missing_handle_or_id | Neither key was supplied |
Recipients sent by handle are validated as you call. Recipients sent by id are checked when the message is built, so their failures show up in the send status.
Check a send
Look up any accepted sendId to see what happened to it.
curl 'https://api.hightouch.com/api/v1/campaigns/{campaignId}/sends/{sendId}' \
-H 'Authorization: Bearer YOUR_API_KEY'
| Status | Meaning |
|---|---|
sent | Handed off for delivery, no result yet |
delivered | Delivery was confirmed |
bounced | The address rejected it |
failed | The send failed |
dropped | Hightouch didn't send it. Some drops carry a reason |
A send appears here once it's been attempted, so a lookup immediately after triggering returns 404. Retry instead of treating the first 404 as an error.
What you don't get
An API-triggered campaign has no audience and no schedule, which also means it has no runs. You get all-time campaign metrics, but no per-send breakdown, no runs list, and no Skipped or Expired counts.
Subscription topics, unsubscribes, bounced addresses, quiet hours, and frequency caps are all still enforced, so a recipient who unsubscribed doesn't receive an API-triggered marketing message.
Suppression lists are the exception. They're applied when a campaign builds its audience from your warehouse, and an API-triggered send has no audience to build, so they don't apply here. Filter those recipients out before you call.
Whether a message ignores subscription topics depends on marking it transactional, not on how it's triggered. See Subscriptions and consent.
Errors
| Code | When |
|---|---|
400 | The campaign isn't API-triggered, isn't active, has no published content, or carries more than one message |
403 | Native Delivery isn't enabled for the workspace |
404 | No campaign with that ID in this workspace |
422 | The payload didn't validate: more than 100 recipients, missing fields, or unrecognized ones |
429 | Too many requests. The limit is shared across all Hightouch API calls from your workspace |
To test before activating, pass "draft": true and send from a draft campaign. Content and recipient validation still run, as do all send limits, so a draft send to an unsubscribed recipient is still skipped.