Build a campaign (saved as a draft)
create_campaignBuilds a campaign as the Campaigns page's New campaign does, always saved as a draft: name, the messages (steps: the opener first, each { template, delayDays?, channel? text | email | call }), offer, audience (audience_filter, or audience_source { kind: list | view | people | filter }), caps and send window. Templates use Sergio's merge tags ({{Client First Name}}, {{User Company}}, {{Offer}}, {{Offer Expiry Date}} and the rest listed on steps.template), never a hard-coded business or client name. audience_filter takes the builder's own keys (visits, days since last booking, last booking total, days since last contact, frequency, services); audience_mode rolling (default) keeps adding people as they qualify (evergreen), fixed freezes the audience at start. read campaigns returns this vocabulary and existing campaigns to copy from. Sergio's audience rules apply unless your person asks otherwise, so leave those fields out: it leaves out anyone with an upcoming booking (audience_filter.noUpcomingBooking) and anyone another campaign reached lately (contact_floor_days, the business's window, 30 days unless changed), and new matches join on their own (audience_mode rolling). The people a rule leaves out show in the campaign as Excluded by Sergio, where a person can include one anyway, so another campaign running over the same people is not something to warn about. The answer's audience_rules says all this in one sentence: tell your person that sentence when you report the draft. A credit your person names is the offer the message describes (see offer_text); never ask them to apply credits. Nobody is enrolled and nothing is sent until it starts: start_campaign (your person sees every message first and says yes), or Start campaign in the app; turning on automatic sending stays the owner's. Pass the same idempotency_key when retrying; a repeat returns the same draft.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"name": "<name>",
"idempotency_key": "<idempotency_key>"
},
"name": "create_campaign"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| color | No | ||
| steps | No | ||
| auto_send | No | ||
| daily_cap | No | ||
| offer_text | No | offer_text is the offer, written the way the builder writes it ("$25 off your next booking", "10% off your next booking", "a free fridge clean"); {{Offer}} sends those exact words to every recipient. A percent offer is NOT priced per person: "10% off your next booking" goes out as written, and there is no per-client dollar amount on a campaign. When your person asks for "10% of their booking as a credit", use the percent offer and tell them each client sees "10% off" rather than a dollar figure; a dollars offer is one amount for everyone. offer_expiry_days N: the offer is good for N days after each person's first message goes out; {{Offer Expiry Date}} quotes that date and every later step quotes the same one. When your person says to give clients a credit ("a $29.86 credit in their account"), that is the campaign's offer, described in the message: write the message the way they asked (for example "you have a $29.86 credit on your account, good until {{Offer Expiry Date}}"), with offer_text naming the credit and offer_expiry_days the days it lasts. Building or starting a campaign never puts money on anyone's account and nothing has to be applied first: the business honors the credit when the client books. So never tell your person to apply or add credits before sending, and never call the wording a problem. | |
| short_code | No | ||
| audience_mode | No | ||
| cooldown_days | No | ||
| audience_filter | No | Who the campaign is for, as the builder's Audience section sets it. Use these keys only; every key given must hold. | |
| audience_source | No | Instead of audience_filter, follow Contacts: { kind: "list", listId } | { kind: "view", viewId } | { kind: "people", clientIds: [...] } | { kind: "filter", query } (a Contacts filter). | |
| idempotency_key | Yes | Your id for this operation (8-128 of A-Z a-z 0-9 . _ : -). Reuse it on every retry of the same operation; use a new one for a new operation. | |
| send_time_local | No | ||
| enrollment_source | No | ||
| offer_expiry_days | No | Days the offer stays good after each person's first message; {{Offer Expiry Date}} quotes the date. | |
| queue_expiry_days | No | ||
| send_window_hours | No | ||
| contact_floor_days | No | ||
| min_days_since_visit | No | ||
| text_by_email_subject | No | The one email subject every text step takes when it goes by email to someone Sergio may not text. An email step keeps its own. | |
| hold_negative_feedback | No |