Helpers around campaigns: filing in folders, renames, a CSV export, the checks the dashboard runs before a send, and AI writing help. Every POST, PATCH and DELETE here needs the send_sms permission and refuses test keys (403 test_key_not_allowed), even the ones that only check or count. Nothing here sends a text.
Campaigns are filed in the same folders as your automations, the ones on the Automations page. A folder is identified by its id. File a campaign in a folder with Rename or move a campaign; deleting a folder takes its campaigns out of it and deletes none of them.
Your campaigns as a CSV file, newest first, with the columns id, name, message_body, total_recipients, total_sent, total_failed, status, scheduled_at, created_at, total_delivered, total_replies, total_skipped, completed_at, message_body_b, message_variants, media_url and followup_message. The last four are the other A/B versions, the picture link and the follow-up text. The counts are the saved ones, not the live delivery counts. The file stops at 50,000 campaigns with a last line starting # that says so; List campaigns returns all of them. Exports share your account’s limit of 6 bulk exports an hour with the dashboard; past it you get 429 rate_limited. A failed export answers 500 CAMPAIGN_EXPORT_UNAVAILABLE.
What sending an unsaved campaign now would do: how many it reaches, how many can get it now, how many are in quiet hours, opted out, or not textable, whether the message passes our content check, and whether it reads as time-bound. Free; nothing is saved.
Parameters
Takes the audience parameters of Create a campaign (recipient_filter, segment_id, recipient_rules, recipient_contact_ids), plus scheduled_at, timezone, is_ab_test and message_variants.
message_bodystringRequired
The message to check.
Returns
data.totalinteger
Everyone in the audience.
data.sendable_nowinteger
How many could get it right now.
data.quiet_hoursobject
count in quiet hours now, and next_open_at_by_tz, when each time zone’s quiet hours end.
data.opted_outinteger
Opted out.
data.invalid_or_landlineinteger
Not textable numbers.
data.contentobject
verdict (ok or blocked) and reason. The send checks again.
data.timingobject
kind (evergreen, time_bound or unknown), deadline_at when it is time_bound (otherwise null), and hold_hours, how long a text waiting out quiet hours is held before it goes.
Which merge fields would come out blank, and for how many recipients, before you send. Pass a saved campaign_id, or an unsaved message_body with the audience parameters of Create a campaign. Free; nothing is saved.
Parameters
campaign_idinteger
Check a saved campaign’s message and audience.
message_bodystring
The message to check when you don’t pass campaign_id.
Returns
data.totalinteger
Recipients checked.
data.blank_recipientsinteger
Recipients with at least one blank field.
data.empty[]object[]
Per blank field, most first: token, count and sample_contact_ids.
data.did_you_meanobject[]
Fields you may have meant, when a token looks like a typo.
data.sample_renderobject | null
One recipient’s text as it would go out: contact_id and body.
Roughly what share of an audience has each merge field filled in, from a sample of the first 3,000 contacts. Takes the audience parameters of Create a campaign. Free; nothing is saved.
Parameters
fieldsstring[]Required
Up to 10 merge fields, like first_name or custom.city. A field we don’t fill is left out of the answer.
Returns
data.sampledinteger
Contacts sampled.
data.fields[]object[]
field, populated (how many have it) and populatedPct.
How many segments a message comes to across an audience, with merge fields filled in and your footer on. Takes the audience parameters of Create a campaign. For the price, use quote. Free; nothing is saved.
Parameters
message_bodystringRequired
Up to 3,800 characters.
recipient_filterstringRequired
As in create.
suppress_optoutboolean
true counts without the opt-out line. suppress_sender_signature does the same for your sender name.
Returns
data.contactsinteger
Contacts counted.
data.average_segmentsnumber
Segments per text, on average.
data.longestobject
The longest text: chars and segments.
data.up_toboolean
true when the count includes a footer line, so real texts can come out shorter.
Errors
HTTP
Code
When
400
invalid_field
No message_body or recipient_filter, or a field has the wrong type.
How many of an audience are on T-Mobile, and whether that is more than T-Mobile lets your number send in a day, so you know if the campaign will take several days. Takes the audience parameters of Create a campaign, plus phone_number_id or phone_number_ids for the number it sends from. Free; nothing is saved.
Returns
data.appliesboolean
false when no daily T-Mobile limit applies. The answer then has nothing else.
data.capinteger
Texts T-Mobile accepts from you a day.
data.tmobileinteger
Recipients on T-Mobile, out of total (known is how many carriers we know).
data.overboolean
true when they won’t all get it in one day. days is about how many it takes, and warning says it in a sentence.
Errors
HTTP
Code
When
400
AUDIENCE_TOO_LARGE
The audience is larger than one campaign can send to.
500
TMOBILE_ESTIMATE_UNAVAILABLE
We couldn’t estimate it. Your campaign can still send.
Each call asks a model for help. Drafts aren’t saved; put what you keep into create or update. These calls share your account’s limit of 30 AI requests every 15 minutes with other AI features; past it you get 429. When AI help is off on our side, a call answers 503 with AI_UNAVAILABLE, AI_BUDGET_EXCEEDED or the code of the budget check; a model answer we couldn’t read is 502 AI_BAD_OUTPUT, and any other failure 500 AI_FAILED.
Writes a campaign from a description: a name, the message, optional A/B versions, an audience stage, a send time if you asked for one, and reply sorting instructions if you asked for them. It costs 1 credit, the price of one text segment at your per-segment rate, taken from your balance only after a draft is made; a failed draft costs nothing, and accounts that don’t pay for texts aren’t charged. Send an Idempotency-Key header to retry without paying twice (Idempotency).
Parameters
descriptionstringRequired
What you want to send, up to 2,000 characters.
Returns
dataobject
name, message, variants, recipient_filter, schedule (date, time, timezone, each null when you didn’t ask) and classification_prompt.
billingobject
charged is 1 when the draft was charged and 0 when it was free. new_balance is about how many one-segment texts your balance still covers after the charge, not money (null when nothing was taken). Also cost. For your balance in money, use Get your balance.
Errors
HTTP
Code
When
400
DESCRIPTION_REQUIRED
No description.
400
DESCRIPTION_TOO_LONG
Over 2,000 characters.
402
insufficient_credits
Your balance doesn’t cover the draft. Nothing was charged.
Rewrites a message with a few interchangeable words written as {a|b|c}; at send time one option is picked for each text, so a big send isn’t identical copies. Numbers, links, merge fields and the opt-out line stay as they are. Free. When the rewrite would drop your opt-out line or come out garbled, you get your message back unchanged with a note.
A grade and suggestions for a campaign, based on its message and its real delivery numbers. Free. Needs the view_reports permission.
Deprecated: use Analyze a campaign’s results, which the dashboard uses and which also reads the replies. This endpoint keeps working, but its grade can differ from that one for the same campaign.
Returns
data.gradestring | null
A to F.
data.summarystring
How it did, in a sentence or two.
data.suggestions[]object[]
title, detail and severity (high, medium, low), most important first.
data.statsobject
The numbers it was based on: recipients, delivered, failed, pending, delivered_pct, failed_pct, chars, segments, has_opt_out.
Errors
HTTP
Code
When
400
INVALID_ID
The id isn’t a whole number.
404
CAMPAIGN_NOT_FOUND
No campaign with that id on your account.
409
CAMPAIGN_NOT_SENT
The campaign hasn’t sent any texts yet, so there’s nothing to grade. Send it, then ask again.
Whether to send more like it, change it, or stop, from its delivery numbers, replies and a sample of the replies. Free. Needs the view_reports permission.
Returns
data.verdictenum
scale, iterate or stop. grade says the same as A, C or F.
data.headlinestring
The verdict in a sentence. summary repeats it.
data.highlightsstring[]
Up to 4 things that stood out.
data.next_stepsstring[]
Up to 4 things to try.
data.statsobject
recipients, delivered_pct, failed_pct and replies.
Errors
HTTP
Code
When
400
INVALID_ID
The id isn’t a whole number.
404
CAMPAIGN_NOT_FOUND
No campaign with that id on your account.
409
CAMPAIGN_NOT_SENT
The campaign hasn’t sent any texts yet, so there’s nothing to grade. Send it, then ask again.
503
CAMPAIGN_STATS_UNAVAILABLE
We couldn’t read its replies. Try again in a minute.