Skip to main content
Glama

Manage campaigns

campaigns_manage
Destructive

Email/SMS campaigns and newsletters. Actions: list (kind=newsletter for newsletters), get, create (draft; template_id starts a newsletter from newsletter_templates), update, delete, duplicate, test_send, send_now, schedule, pause, resume, stop, add_recipients, preview_audience, newsletter_templates. send_now/schedule/resume message real people: confirm first.

Input Schema

NameRequiredDescriptionDefault
qNolist: name search.
idNoCampaign id; needed for all but list, create, preview_audience.
toNotest_send recipient.
kindNolist filter: newsletters, or everything else.
nameNo
roleNonewsletter_templates: another industry's set (e.g. real estate, mortgage, insurance). Default: the user's own.
limitNo
stepsNoDrip steps (send_mode 'drip'): [{ delay_seconds, content_json, exit_on?, channel? }]. Step 0 sends at start; each later step sends delay_seconds after that recipient's previous actual send. exit_on { reply?, open?, click?, meeting?, status? } skips the rest for a contact who did that. channel overrides the campaign channel for the step.
actionYes
statusNolist filter.
channelNo
contentNoSingle send. Email: { subject, preheader?, from_name?, from_email?, reply_to?, blocks: [{ type, props }] }; block types text {html}, header {html, level 1-3}, image {src, alt, width}, button {text, url, bgColor?}, divider {color?}, spacer {height}. from_email: a domain verified in Resend (resend_manage domains). reply_to: a mailbox connected in Groundbase, so drip reply-exit sees replies. SMS: { body, media_urls? }. Merge tags like {{first_name}}, {{cf.<key>}} work in any string. Newsletter: content.kind 'newsletter' (one email, sent once). Extra blocks: section_header {title}; agent_card {photoUrl, name, title, phone, email, website, brokerage, note}; listing_card {imageUrl, status, price, address, beds, baths, sqft, url, buttonText}; market_stats {title, stats:[{label,value,change}], footnote}; quote {quote, author, role, avatarUrl}; footer {businessName, businessAddress, tagline, unsubText}. Refused until the footer has a business name and mailing address and no [bracketed] or $— placeholder is left; the refusal names the first.
audienceNoduplicate: copy goes only to people the (sent, email) campaign reached who did not open it.
send_modeNo
contact_idsNoadd_recipients: up to 500.
template_idNocreate: start a newsletter from this template (newsletter_templates). name defaults to the template name.
scheduled_atNoUnix seconds.
enrolment_modeNo'once' (default) fixes its audience at start. 'ongoing' keeps sending until stop and takes new entrants (re-checked every 5 min).
sending_windowNo{ days: [0-6, 0=Sunday], start_hour: 0-23, end_hour: 1-24 (exclusive), timezone?: IANA or 'auto' (account tz) }. Sends due outside the window wait for the next open hour. null = no restriction.
audience_filterNo{ all: [rules], any?: [rules], include_contacts?: [ids] }. all ANDs, any ORs. Rules: {type:"all_contacts"}; {type:"tag",op:"has"|"has_not",value:name}; {type:"behavior",event:"campaign_sent"|"campaign_opened"|"campaign_clicked"|"campaign_replied"|"campaign_bounced",campaign_id,within_days?,inverse?}; {type:"custom_field",field:key,op:"equals"|"not_equals"|"contains"|"gt"|"lt",value}; {type:"last_contacted",op:"gt"|"lt",days}; {type:"status",op?:"is_not",value:[names|"none"]}. include_contacts alone = exactly those people.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / steps / description
      Previous value: -"Drip steps (send_mode 'drip'): [{ delay_seconds, content_json, exit_on?, channel? }]. Step 0 sends at start; each later step sends delay_seconds after that recipient's previous actual send. exit_on { reply?, open?, click?, meeting? } skips the rest for a contact who did that. channel overrides the campaign channel for the step."New value: +"Drip steps (send_mode 'drip'): [{ delay_seconds, content_json, exit_on?, channel? }]. Step 0 sends at start; each later step sends delay_seconds after that recipient's previous actual send. exit_on { reply?, open?, click?, meeting?, status? } skips the rest for a contact who did that. channel overrides the campaign channel for the step."
  2. Changed3 schema fields changed
    • changedInput schema / properties / audience_filter / description
      Previous value: -"{ all: [rules], any?: [rules], include_contacts?: [ids] }. all ANDs, any ORs. Rules: {type:\"all_contacts\"}; {type:\"tag\",op:\"has\"|\"has_not\",value:name}; {type:\"behavior\",event:\"campaign_sent\"|\"campaign_opened\"|\"campaign_clicked\"|\"campaign_replied\"|\"campaign_bounced\",campaign_id,within_days?,inverse?}; {type:\"custom_field\",field:key,op:\"equals\"|\"not_equals\"|\"contains\"|\"gt\"|\"lt\",value}; {type:\"last_contacted\",op:\"gt\"|\"lt\",days}. include_contacts alone = exactly those people."New value: +"{ all: [rules], any?: [rules], include_contacts?: [ids] }. all ANDs, any ORs. Rules: {type:\"all_contacts\"}; {type:\"tag\",op:\"has\"|\"has_not\",value:name}; {type:\"behavior\",event:\"campaign_sent\"|\"campaign_opened\"|\"campaign_clicked\"|\"campaign_replied\"|\"campaign_bounced\",campaign_id,within_days?,inverse?}; {type:\"custom_field\",field:key,op:\"equals\"|\"not_equals\"|\"contains\"|\"gt\"|\"lt\",value}; {type:\"last_contacted\",op:\"gt\"|\"lt\",days}; {type:\"status\",op?:\"is_not\",value:[names|\"none\"]}. include_contacts alone = exactly those people."
    • changedInput schema / properties / content / description
      Previous value: -"Single send. Email: { subject, preheader?, from_name?, from_email?, reply_to?, blocks: [{ type, props }] }; block types text {html}, header {html, level 1-3}, image {src, alt, width}, button {text, url, bgColor?}, divider {color?}, spacer {height}. from_email must be on a domain verified in Resend (resend_manage domains) or it fails per recipient at send time. reply_to should be a mailbox connected in Groundbase, or drip reply-exit cannot see replies. SMS: { body, media_urls? }. Merge tags like {{first_name}}, {{cf.<key>}} work in any string. Newsletter: content.kind 'newsletter' (one email, sent once). Extra blocks: section_header {title}; agent_card {photoUrl, name, title, phone, email, website, brokerage, note}; listing_card {imageUrl, status, price, address, beds, baths, sqft, url, buttonText}; market_stats {title, stats:[{label,value,change}], footnote}; quote {quote, author, role, avatarUrl}; footer {businessName, businessAddress, tagline, unsubText}. It will not send until the footer has a real business name and mailing address and no [bracketed] or $— placeholder is left anywhere; the refusal names the first one."New value: +"Single send. Email: { subject, preheader?, from_name?, from_email?, reply_to?, blocks: [{ type, props }] }; block types text {html}, header {html, level 1-3}, image {src, alt, width}, button {text, url, bgColor?}, divider {color?}, spacer {height}. from_email: a domain verified in Resend (resend_manage domains). reply_to: a mailbox connected in Groundbase, so drip reply-exit sees replies. SMS: { body, media_urls? }. Merge tags like {{first_name}}, {{cf.<key>}} work in any string. Newsletter: content.kind 'newsletter' (one email, sent once). Extra blocks: section_header {title}; agent_card {photoUrl, name, title, phone, email, website, brokerage, note}; listing_card {imageUrl, status, price, address, beds, baths, sqft, url, buttonText}; market_stats {title, stats:[{label,value,change}], footnote}; quote {quote, author, role, avatarUrl}; footer {businessName, businessAddress, tagline, unsubText}. Refused until the footer has a business name and mailing address and no [bracketed] or $— placeholder is left; the refusal names the first."
    • changedInput schema / properties / enrolment_mode / description
      Previous value: -"'once' (default) fixes its audience at start. 'ongoing' keeps sending until stop and takes new entrants (filter re-run every 5 min, add_recipients, workflow add_to_campaign)."New value: +"'once' (default) fixes its audience at start. 'ongoing' keeps sending until stop and takes new entrants (re-checked every 5 min)."
  3. Changed1 schema field changed
    • changedInput schema / properties / audience_filter / description
      Previous value: -"{ all: [rules], any?: [rules] }. Rules: { type: \"all_contacts\" } or { type: \"tag\", op: \"has\"|\"has_not\", value: \"<tag name>\" }. all[] ANDs, any[] ORs."New value: +"{ all: [rules], any?: [rules], include_contacts?: [ids] }. all ANDs, any ORs. Rules: {type:\"all_contacts\"}; {type:\"tag\",op:\"has\"|\"has_not\",value:name}; {type:\"behavior\",event:\"campaign_sent\"|\"campaign_opened\"|\"campaign_clicked\"|\"campaign_replied\"|\"campaign_bounced\",campaign_id,within_days?,inverse?}; {type:\"custom_field\",field:key,op:\"equals\"|\"not_equals\"|\"contains\"|\"gt\"|\"lt\",value}; {type:\"last_contacted\",op:\"gt\"|\"lt\",days}. include_contacts alone = exactly those people."
  4. Changed1 schema field changed
    • addedInput schema / properties / contact_ids / maxItems
      Added value: +500
  5. Changed5 schema fields changed
    • changedInput schema / properties / action / enum
      Previous value: -[
      -  "list",
      -  "get",
      -  "create",
      -  "update",
      -  "delete",
      -  "duplicate",
      -  "test_send",
      -  "send_now",
      -  "schedule",
      -  "pause",
      -  "resume",
      -  "stop",
      -  "add_recipients",
      -  "preview_audience"
      -]New value: +[
      +  "list",
      +  "get",
      +  "create",
      +  "update",
      +  "delete",
      +  "duplicate",
      +  "test_send",
      +  "send_now",
      +  "schedule",
      +  "pause",
      +  "resume",
      +  "stop",
      +  "add_recipients",
      +  "preview_audience",
      +  "newsletter_templates"
      +]
    • changedInput schema / properties / content / description
      Previous value: -"Single send. Email: { subject, preheader?, from_name?, from_email?, reply_to?, blocks: [{ type, props }] }; block types text {html}, header {html, level 1-3}, image {src, alt, width}, button {text, url, bgColor?}, divider {color?}, spacer {height}. from_email must be on a domain verified in Resend (resend_manage domains) or it fails per recipient at send time. reply_to should be a mailbox connected in Groundbase, or drip reply-exit cannot see replies. SMS: { body, media_urls? }. Merge tags like {{first_name}}, {{cf.<key>}} work in any string."New value: +"Single send. Email: { subject, preheader?, from_name?, from_email?, reply_to?, blocks: [{ type, props }] }; block types text {html}, header {html, level 1-3}, image {src, alt, width}, button {text, url, bgColor?}, divider {color?}, spacer {height}. from_email must be on a domain verified in Resend (resend_manage domains) or it fails per recipient at send time. reply_to should be a mailbox connected in Groundbase, or drip reply-exit cannot see replies. SMS: { body, media_urls? }. Merge tags like {{first_name}}, {{cf.<key>}} work in any string. Newsletter: content.kind 'newsletter' (one email, sent once). Extra blocks: section_header {title}; agent_card {photoUrl, name, title, phone, email, website, brokerage, note}; listing_card {imageUrl, status, price, address, beds, baths, sqft, url, buttonText}; market_stats {title, stats:[{label,value,change}], footnote}; quote {quote, author, role, avatarUrl}; footer {businessName, businessAddress, tagline, unsubText}. It will not send until the footer has a real business name and mailing address and no [bracketed] or $— placeholder is left anywhere; the refusal names the first one."
    • addedInput schema / properties / kind
      Added value: +{
      +  "description": "list filter: newsletters, or everything else.",
      +  "enum": [
      +    "newsletter",
      +    "campaign"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / role
      Added value: +{
      +  "description": "newsletter_templates: another industry's set (e.g. real estate, mortgage, insurance). Default: the user's own.",
      +  "type": "string"
      +}
    • addedInput schema / properties / template_id
      Added value: +{
      +  "description": "create: start a newsletter from this template (newsletter_templates). name defaults to the template name.",
      +  "type": "string"
      +}
  6. Changed1 schema field changed
    • addedInput schema / properties / audience
      Added value: +{
      +  "description": "duplicate: copy goes only to people the (sent, email) campaign reached who did not open it.",
      +  "enum": [
      +    "non_openers"
      +  ],
      +  "type": "string"
      +}
  7. Changed18 schema fields changed
    • removedInput schema / properties / action / description
      Removed value: -"'list' returns user's campaigns. 'get' returns one by id. 'create' makes a draft (requires name + channel). 'update' patches a draft. 'delete' soft-deletes. 'duplicate' clones as a draft. 'test_send' fires one message to `to` without queuing recipients. 'send_now' resolves the audience and queues all recipients for dispatch. 'schedule' sets scheduled_at. 'pause' freezes. 'resume' un-pauses. 'stop' ends an ONGOING campaign (queued people dropped, sent ones kept). 'add_recipients' names contacts on a campaign by id: on a draft, scheduled or paused one they join the audience; on an ongoing campaign that is sending they are queued now, at the first message, under the campaign's rules."
    • changedInput schema / properties / action / enum
      Previous value: -[
      -  "list",
      -  "get",
      -  "create",
      -  "update",
      -  "delete",
      -  "duplicate",
      -  "test_send",
      -  "send_now",
      -  "schedule",
      -  "pause",
      -  "resume",
      -  "stop",
      -  "add_recipients"
      -]New value: +[
      +  "list",
      +  "get",
      +  "create",
      +  "update",
      +  "delete",
      +  "duplicate",
      +  "test_send",
      +  "send_now",
      +  "schedule",
      +  "pause",
      +  "resume",
      +  "stop",
      +  "add_recipients",
      +  "preview_audience"
      +]
    • changedInput schema / properties / audience_filter / description
      Previous value: -"For create/update. `audience_filter` shape: { all: [rules], any?: [rules] }. Rules are {type, ...}. Supported in v1: { type: \"all_contacts\" } — every contact with a valid channel address; { type: \"tag\", op: \"has\" | \"has_not\", value: \"<tag_name>\" } — tag membership filter. Multiple all[] rules AND together; any[] rules OR together. Behavior queries (opened campaign X / clicked X) and custom-field queries arrive in a follow-up."New value: +"{ all: [rules], any?: [rules] }. Rules: { type: \"all_contacts\" } or { type: \"tag\", op: \"has\"|\"has_not\", value: \"<tag name>\" }. all[] ANDs, any[] ORs."
    • changedInput schema / properties / contact_ids / description
      Previous value: -"For add_recipients: contact ids, at most 500."New value: +"add_recipients: up to 500."
    • changedInput schema / properties / content / description
      Previous value: -"For create/update, single-send. `content` for email = { subject, preheader?, from_name?, from_email?, reply_to?, blocks: [...] }. `from_email` may be any address on ANY domain verified in the connected Resend account — call resend_manage(action='domains') first to see which are verified. An unverified domain is not rejected here; it fails later, at send time, per recipient. `reply_to` sets where replies land — use it when replies should go somewhere other than from_email. IMPORTANT for drip campaigns: exit_on:{reply} can only fire for replies Groundbase can SEE, which means an address on a mailbox connected under Settings -> Email. Point reply_to at an unconnected address and the user still gets their replies, but reply-exit stays blind and the drip keeps sending to people who answered. Settable per step as well as per campaign. Each block is { type, props }. Block types: text (props.html: HTML), header (props.html: text, props.level: 1|2|3), image (props.src: url, props.alt, props.width), button (props.text, props.url, props.bgColor?), divider (props.color?), spacer (props.height: px). `content` for SMS = { body: text, media_urls?: [urls] }. Merge tags inside any string field: {{first_name}}, {{last_name}}, {{full_name}}, {{email}}, {{phone}}, {{company_name}}, {{cf.<custom_field_key>}}. Unknown tags render empty."New value: +"Single send. Email: { subject, preheader?, from_name?, from_email?, reply_to?, blocks: [{ type, props }] }; block types text {html}, header {html, level 1-3}, image {src, alt, width}, button {text, url, bgColor?}, divider {color?}, spacer {height}. from_email must be on a domain verified in Resend (resend_manage domains) or it fails per recipient at send time. reply_to should be a mailbox connected in Groundbase, or drip reply-exit cannot see replies. SMS: { body, media_urls? }. Merge tags like {{first_name}}, {{cf.<key>}} work in any string."
    • changedInput schema / properties / enrolment_mode / description
      Previous value: -"For create/update. 'once' (default) settles its audience when it starts and finishes when the last message goes. 'ongoing' stays sending until action='stop' and takes new entrants at any time: the audience filter is re-run every five minutes, add_recipients works while it sends, and a workflow add_to_campaign step can enrol someone. Cannot be changed to 'once' while sending — stop it instead."New value: +"'once' (default) fixes its audience at start. 'ongoing' keeps sending until stop and takes new entrants (filter re-run every 5 min, add_recipients, workflow add_to_campaign)."
    • changedInput schema / properties / id / description
      Previous value: -"Campaign UUID — required for everything except list+create."New value: +"Campaign id; needed for all but list, create, preview_audience."
    • changedInput schema / properties / q / description
      Previous value: -"Free-text name search for action=list."New value: +"list: name search."
    • changedInput schema / properties / scheduled_at / description
      Previous value: -"Unix seconds for schedule/create."New value: +"Unix seconds."
    • changedInput schema / properties / sending_window / description
      Previous value: -"For create/update. `sending_window` restricts when this campaign may send: { days: [0-6, 0=Sunday], start_hour: 0-23, end_hour: 1-24, timezone?: IANA or 'auto' }. end_hour is exclusive and must exceed start_hour. 'auto' (the default) means the account owner's timezone. A step falling due outside the window WAITS — the recipient stays queued and sends at the next open hour. Nothing fails or is skipped. Omit or pass null for no restriction."New value: +"{ days: [0-6, 0=Sunday], start_hour: 0-23, end_hour: 1-24 (exclusive), timezone?: IANA or 'auto' (account tz) }. Sends due outside the window wait for the next open hour. null = no restriction."
    • changedInput schema / properties / status / description
      Previous value: -"Filter for action=list."New value: +"list filter."
    • changedInput schema / properties / steps / description
      Previous value: -"For create/update when send_mode='drip'. `steps` authors a DRIP campaign (requires send_mode='drip'). Array of { delay_seconds, content_json, exit_on?, channel? } in order. `content_json` is the canonical key; `content` is accepted as an alias. Until 2026-08-30 this doc said `content` while the dispatcher read only `content_json`, so steps authored from this description silently sent step 0 every time. Step 0 fires when the campaign starts; every later step fires delay_seconds after THAT recipient's actual previous send, so a step held back by a sending window or the daily cap still spaces correctly. Each step's `content` uses the same block format as the single-send `content` param. `exit_on` is { reply?, open?, click?, meeting? } — when true and the contact has done that since the previous step, the rest of the drip is skipped for them. `channel` overrides the campaign channel for that step alone (mixed email/SMS drips); it also decides whether the step counts against the daily EMAIL cap."New value: +"Drip steps (send_mode 'drip'): [{ delay_seconds, content_json, exit_on?, channel? }]. Step 0 sends at start; each later step sends delay_seconds after that recipient's previous actual send. exit_on { reply?, open?, click?, meeting? } skips the rest for a contact who did that. channel overrides the campaign channel for the step."
    • removedInput schema / properties / steps / items / properties / channel / description
      Removed value: -"Overrides the campaign channel for this step only."
    • changedInput schema / properties / steps / items / properties / content / description
      Previous value: -"Alias for content_json, accepted for compatibility."New value: +"Alias of content_json."
    • changedInput schema / properties / steps / items / properties / content_json / description
      Previous value: -"Message for this step. Same shape as the top-level content param. Canonical key."New value: +"Same shape as content."
    • removedInput schema / properties / steps / items / properties / delay_seconds / description
      Removed value: -"Seconds after the previous step's actual send. Step 0 should be 0."
    • removedInput schema / properties / steps / items / properties / exit_on / description
      Removed value: -"Skip the rest of the drip for a contact who has replied/opened/clicked, or booked a meeting."
    • changedInput schema / properties / to / description
      Previous value: -"For action='test_send' — recipient email or phone."New value: +"test_send recipient."
  8. First observed

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, openWorldHint=true, and idempotentHint=false. The description adds material context beyond that: send_now/schedule/resume message real people and require confirmation, create is draft-only, and content is refused until the footer has a business name/address with no placeholders.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Given 15 actions and 20 parameters, the description is compact and front-loads the resource and action list. It is dense but every clause carries action or safety information; no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex, 20-param, nested mega-tool with no output schema, the description covers all actions and critical behavioral warnings. The detailed input schema handles most parameter meaning, though delete semantics and return values are not described.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 75% and the schema itself is richly documented, including nested content, steps, audience_filter, and sending_window. The description adds a few action-specific hints (kind=newsletter, template_id) but does not serve as primary parameter documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the resource ('Email/SMS campaigns and newsletters') and enumerates all 15 actions, giving an agent a precise picture of scope. It never names the sibling campaigns_analytics or templates_manage, so sibling differentiation is only implicit through the action list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Action-level guidance is present: 'list (kind=newsletter for newsletters)', 'create (draft; template_id starts a newsletter...)', and a confirmation warning for send_now/schedule/resume. It does not explicitly say when to use this tool versus campaigns_analytics or templates_manage, but the context for each action is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources