Skip to main content
Glama

Setup Email Sequence

setup_email_sequence
Destructive

For single personal emails, use draft_email + send_email.

A campaign already sending without a flow keeps calling this tool without node_id. Every other campaign sends through its flow, even a single email: create it with define_sequence and add the prospects with track_prospects, and the flow's send steps call this tool with their node_id. A call without node_id on an agent that has no flow and hasn't sent any outreach yet is refused.

An agent is required — create one via create_agent if none exists. This tool creates tracking items for the recipients it queues, so don't also call track_prospects for them. Only call this AFTER the user has explicitly confirmed the outreach.

Follow-ups thread on their own. A recipient this agent has already emailed gets a reply in that email's thread, sent from the mailbox that sent it (whatever mailbox says), so one call can mix first emails and follow-ups across mailboxes; write their body as a reply. Everyone else gets a new email from mailbox. If the mailbox that last emailed someone is no longer connected, they get a new email too. Dict with queued_count, follow_up_recipients (the queued recipients sent as replies in their existing thread), skipped items, a preview of the first queued email, first_send_at / last_send_at (see below), and tracking_skipped — recipients already tracked in another campaign, so nothing queued for them. Each entry names existing_agent_title / existing_agent_id; surface these and let the user place them.

This tool QUEUES; it never sends. Nothing has been delivered when it returns — a background beat drains each mailbox in derived order at a human-like pace, minutes to days later. first_send_at / last_send_at are ISO timestamps bounding the projected send window (from the read-time forecast), and awaiting_approval counts rows held for the user's approval. Gated rows are excluded from the window; when every row is gated both bounds are null and nothing is projected until the user approves.

So report queued work as queued, with the window: "Queued 4, first lands 12:02pm, last 12:11pm." Do NOT tell the user these were sent.

Writing a marker into a campaign's own tracker (a sheet, a CRM field) at queue time is fine and is usually what its dedup depends on — skip it and the next run re-queues the same people. But that marker records handoff, not delivery: don't cite it back as proof a send happened, and don't let it turn into "sent" in your summary. For what actually went out, read the queue (manage_email_outreach_queue(action='status')); to act at real send time, the agent needs a sent_email trigger.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
enrichNoWhen True, look up each recipient's LinkedIn URL via Apollo (using their `email` plus optional `first_name` / `last_name`) and stamp it on the tracking row. Costs 1 Sliq credit per verified hit; free with BYO Apollo. Recipients that already carry `linkedin_url`, those missing inputs, and those past the credit limit are silently skipped. Reverse of `setup_linkedin_sequence(enrich=True)`.
mailboxNoEmail address of a connected mailbox (e.g. 'alice@acme.io') for new emails. Omit to use the user's default mailbox. When the user has multiple mailboxes connected, ask which to use rather than guessing — surfacing the choice is the agent's job, not a silent fallback. For segmented delivery ("first third from A, second from B, last from C"), call this tool once per segment with the segment's recipients and the segment's mailbox. The tool does not rotate across calls; per-call recipient lists are how segmentation is expressed.
node_idNoOptional. The id of the sequence-DAG node this batch enacts (a node in the agent's `sequence`, authored via `define_sequence`). Stamped on every queued row so the Campaign Flow view can place each prospect and count per node. Pass it whenever the agent has a sequence. Rejected with ModelRetry if it isn't a node in the agent's sequence.
agent_idYesRequired. Agent ID to associate with the queued items.
recipientsYesList of dicts. Required: `email`. Optional per-recipient `subject` / `body` (final literal text, apart from any [[slot]] the user asked to fill in themselves while approving; placeholder syntax like `{first_name}`, `[name]`, `<<name>>`, `{{name}}` is rejected with ModelRetry; overrides templates). Other fields (`first_name`, `last_name`, `company`, `title`) are stored on the tracking item for later lookup — NOT substituted into templates. Other extras are ignored, except the LinkedIn identifiers: `linkedin_url` is honored from either the top-level key OR a `data` sub-dict (validated / canonicalized before storage), while `linkedin_provider_id` is honored from the top-level key only. Both land on the prospect's LinkedIn fields. For mixed-channel campaigns, also pass `linkedin_url` (and `linkedin_provider_id` when known) on each recipient so this tool can dedup against any existing LinkedIn-keyed tracking row for the same person — both identifiers land on one row. When `find_email` returned `{email, linkedin_url}` for this recipient, forward both fields here verbatim. On a flow email node this is also how a just-discovered address binds to the prospect resting on the node: passing their `linkedin_url` resolves the send to that existing row instead of orphaning a new one.
on_behalf_ofNoAn agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account.
body_templateNoBatch fallback body for recipients without their own `body`. Final literal text, as for a per-recipient `body`. Bodies (this and per-recipient `body`) are Markdown, rendered to HTML at send time for every provider — write hyperlinks as [text](url) so the link reads as its anchor text rather than a raw URL.
subject_templateNoBatch fallback subject for recipients without their own `subject`. Final literal text, as for a per-recipient `subject`.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changed
    • changedInput schema / properties / body_template / description
      Previous value: -"Batch fallback body for recipients without their own\n`body`. Final literal text — placeholder syntax rejected.\nBodies (this and per-recipient `body`) are Markdown, rendered to\nHTML at send time for every provider — write hyperlinks as\n[text](url) so the link reads as its anchor text rather than a\nraw URL."New value: +"Batch fallback body for recipients without their own\n`body`. Final literal text, as for a per-recipient `body`.\nBodies (this and per-recipient `body`) are Markdown, rendered to\nHTML at send time for every provider — write hyperlinks as\n[text](url) so the link reads as its anchor text rather than a\nraw URL."
    • changedInput schema / properties / recipients / description
      Previous value: -"List of dicts. Required: `email`. Optional per-recipient\n`subject` / `body` (final literal text; placeholder syntax\n`{first_name}`, `[name]`, `<<name>>`, `{{name}}` is rejected with\nModelRetry; overrides templates). Other fields (`first_name`,\n`last_name`, `company`, `title`) are stored on the tracking item\nfor later lookup — NOT substituted into templates. Other extras are\nignored, except the LinkedIn identifiers: `linkedin_url` is honored\nfrom either the top-level key OR a `data` sub-dict (validated /\ncanonicalized before storage), while `linkedin_provider_id` is\nhonored from the top-level key only. Both land on the prospect's\nLinkedIn fields.\nFor mixed-channel campaigns, also pass `linkedin_url` (and\n`linkedin_provider_id` when known) on each recipient so this\ntool can dedup against any existing LinkedIn-keyed tracking\nrow for the same person — both identifiers land on one row.\nWhen `find_email` returned `{email, linkedin_url}` for this\nrecipient, forward both fields here verbatim. On a flow email node\nthis is also how a just-discovered address binds to the prospect\nresting on the node: passing their `linkedin_url` resolves the send\nto that existing row instead of orphaning a new one."New value: +"List of dicts. Required: `email`. Optional per-recipient\n`subject` / `body` (final literal text, apart from any [[slot]] the user\nasked to fill in themselves while approving; placeholder syntax like\n`{first_name}`, `[name]`, `<<name>>`, `{{name}}` is rejected with ModelRetry;\noverrides templates). Other fields (`first_name`,\n`last_name`, `company`, `title`) are stored on the tracking item\nfor later lookup — NOT substituted into templates. Other extras are\nignored, except the LinkedIn identifiers: `linkedin_url` is honored\nfrom either the top-level key OR a `data` sub-dict (validated /\ncanonicalized before storage), while `linkedin_provider_id` is\nhonored from the top-level key only. Both land on the prospect's\nLinkedIn fields.\nFor mixed-channel campaigns, also pass `linkedin_url` (and\n`linkedin_provider_id` when known) on each recipient so this\ntool can dedup against any existing LinkedIn-keyed tracking\nrow for the same person — both identifiers land on one row.\nWhen `find_email` returned `{email, linkedin_url}` for this\nrecipient, forward both fields here verbatim. On a flow email node\nthis is also how a just-discovered address binds to the prospect\nresting on the node: passing their `linkedin_url` resolves the send\nto that existing row instead of orphaning a new one."
    • changedInput schema / properties / subject_template / description
      Previous value: -"Batch fallback subject for recipients without their\nown `subject`. Final literal text — placeholder syntax rejected."New value: +"Batch fallback subject for recipients without their\nown `subject`. Final literal text, as for a per-recipient `subject`."
  2. Changed1 schema field changed
    • addedInput schema / properties / on_behalf_of
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
      +}
  3. Changed2 schema fields changed
    • removedInput schema / properties / is_follow_up
      Removed value: -{
      -  "default": false,
      -  "description": "If True, send as a threaded reply to the original outreach\nemail instead of a new email. Requires recipients to have been\npreviously emailed via this agent.",
      -  "type": "boolean"
      -}
    • changedInput schema / properties / mailbox / description
      Previous value: -"Email address of a connected mailbox (e.g. 'alice@acme.io').\nOmit to use the user's default mailbox. When the user has\nmultiple mailboxes connected, ask which to use rather than\nguessing — surfacing the choice is the agent's job, not a\nsilent fallback.\n\nFor segmented delivery (\"first third from A, second from B,\nlast from C\"), call this tool once per segment with the\nsegment's recipients and the segment's mailbox. The tool does\nnot rotate across calls; per-call recipient lists are how\nsegmentation is expressed."New value: +"Email address of a connected mailbox (e.g. 'alice@acme.io')\nfor new emails. Omit to use the user's default mailbox. When the user has\nmultiple mailboxes connected, ask which to use rather than\nguessing — surfacing the choice is the agent's job, not a\nsilent fallback.\n\nFor segmented delivery (\"first third from A, second from B,\nlast from C\"), call this tool once per segment with the\nsegment's recipients and the segment's mailbox. The tool does\nnot rotate across calls; per-call recipient lists are how\nsegmentation is expressed."
  4. Changed1 schema field changed
    • changedInput schema / properties / node_id / description
      Previous value: -"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the agent's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the agent has a\nsequence; omit for flowless/ad-hoc sends. Rejected with ModelRetry\nif it isn't a node in the agent's sequence."New value: +"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the agent's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the agent has a\nsequence. Rejected with ModelRetry if it isn't a node in the agent's\nsequence."
  5. Changed5 schema fields changed
    • addedInput schema / properties / agent_id
      Added value: +{
      +  "description": "Required. Agent ID to associate with the queued items.",
      +  "type": "integer"
      +}
    • changedInput schema / properties / is_follow_up / description
      Previous value: -"If True, send as a threaded reply to the original outreach\nemail instead of a new email. Requires recipients to have been\npreviously emailed via this task."New value: +"If True, send as a threaded reply to the original outreach\nemail instead of a new email. Requires recipients to have been\npreviously emailed via this agent."
    • changedInput schema / properties / node_id / description
      Previous value: -"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the task's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the task has a\nsequence; omit for flowless/ad-hoc sends. Rejected with ModelRetry\nif it isn't a node in the task's sequence."New value: +"Optional. The id of the sequence-DAG node this batch enacts\n(a node in the agent's `sequence`, authored via `define_sequence`).\nStamped on every queued row so the Campaign Flow view can place\neach prospect and count per node. Pass it whenever the agent has a\nsequence; omit for flowless/ad-hoc sends. Rejected with ModelRetry\nif it isn't a node in the agent's sequence."
    • removedInput schema / properties / task_id
      Removed value: -{
      -  "description": "Required. Task ID to associate with the queued items.",
      -  "type": "integer"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "recipients",
      -  "task_id"
      -]New value: +[
      +  "recipients",
      +  "agent_id"
      +]
  6. First observed

TDQS

A4.8/5.0
Behavior5/5

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

Annotations correctly indicate this is a mutation and potentially destructive action, and the description adds substantial context beyond them: the tool only queues and never sends, sends are drained later by a background process, tracking rows are created automatically, approval gating affects projections, and follow-up recipients reply in existing threads. It also warns not to call track_prospects for queued recipients and explains that tracking markers are not proof of delivery.

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?

The description is well-structured and front-loads the purpose and main usage contrast before diving into edge cases and return behavior. It is long, but most of the length is justified by the tool's complexity; some repetition around queueing versus sending could be tightened.

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

Completeness5/5

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

Complete for a complex mutation tool with no output schema: the embedded returns section explains queued_count, follow_up_recipients, skipped items, first_send_at/last_send_at, awaiting_approval, tracking_skipped, and how to report timing to users. It also covers prerequisites, approval gating, tracking side effects, and the distinction between queued work and actual sends.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter behavior not fully captured in the schema: node_id connects a batch to a defined sequence node, mailbox segmentation is expressed by separate calls, one call can mix first emails and follow-ups across mailboxes, and recipient linkedin_url can dedup against existing rows. These semantics materially help correct invocation beyond field-level schema text.

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

Purpose5/5

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

States a specific verb and resource: "Queue bulk email outreach with rate-limited staggered sending and automatic tracking." It explicitly distinguishes the campaign/batch tool from single personal emails handled by draft_email + send_email, so an agent can identify the right tool without opening schemas.

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

Usage Guidelines5/5

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

Provides explicit when-to-use and when-not-to-use guidance: use draft_email + send_email for single personal emails, use define_sequence and track_prospects when a flow exists, omit node_id only for a campaign already sending without a flow, and call only after the user explicitly confirms outreach. It also states the agent prerequisite and points to create_agent.

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