Skip to content

MCP setup

Connect your agent to Sendocado with one command. It gets 96 tools, the same as the app: contacts, campaigns, broadcasts, replies and stats.

View as Markdown

The server

Sendocado runs a hosted MCP server (MCP is the Model Context Protocol). There is nothing to install. In Claude Code, run this command: claude mcp add --transport http sendocado https://sendocado.com/api/mcp

Server URL
https://sendocado.com/api/mcp

There are two ways to sign in:

  • Sign in with your browser. Clients that support OAuth open a Sendocado page where you approve the connection. You can disconnect it later in Settings.
  • An API key. Create one in Settings → API keys and send it in the Authorization header as Bearer sc_….

Claude Code

Add the server
claude mcp add --transport http sendocado https://sendocado.com/api/mcp

Then type /mcp in Claude Code and choose Sendocado to sign in. To use an API key instead:

Add the server with an API key
claude mcp add --transport http sendocado https://sendocado.com/api/mcp \
  --header "Authorization: Bearer $SENDOCADO_API_KEY"

Cursor

Add the server to ~/.cursor/mcp.json (or .cursor/mcp.json in a project), then sign in when Cursor asks.

Cursor MCP config
{
  "mcpServers": {
    "sendocado": {
      "url": "https://sendocado.com/api/mcp"
    }
  }
}

Claude on the web and desktop

Open Settings → Connectors, choose Add custom connector, paste the server URL and sign in.

Other clients

Any client that can reach a remote MCP server over HTTP works. Use the server URL above with OAuth, or send the API key as a header.

Try it

Once it is connected, ask in plain words:

  • “Show me my Sendocado projects.”
  • “How many contacts tagged trial signed up this week, and how many of them opened anything?”
  • “Set up a three-email welcome campaign for trial users, from Maya, and show it to me before it goes live.”
  • “Which email in Trial welcome gets the most replies?”

Set up onboarding emails

“Set up welcome emails for my product in Sendocado. New users get a welcome email today, and a follow-up on day 3 if they haven't connected their first project. Stop when they connect it. Add the API calls to my signup code. Show me everything before it goes live.”

Step-by-step guide: Send activation and onboarding emails with your agent

What your agent can and can’t do

  • Every tool matches a route in the API reference and runs the same checks as the app, including readiness checks before a campaign goes live.
  • Tools that send email are marked as such, so your client can ask you before it runs them.
  • Agents can’t delete your account. That is only possible in the app or with the REST API.
  • Connecting Gmail and signing in happen in the app, in your browser.
  • The API and MCP share one rate limit per account: 120 requests every 60 seconds.

Tools

The server has 96 tools. This list is read from the server itself, so it is always the list your agent sees. Send every argument that has no default; use null where you have no value. Errors come back as <type>: <message>, the same types as the API.

list_projects

List projects. List every project on the account. A project holds its own contacts, campaigns, sender and settings; every other tool needs a project_id from here. Call this first.

Changes nothing.

No arguments.

get_project

Get project. Read one project: its name, default time zone, postal address (required before any marketing email goes out) and inbound address.

Changes nothing.

Argument Type Notes
project_id string Required. From list_projects

create_project

Create project. Create a project: a separate space with its own contacts, campaigns, sender and settings. Use one per product or brand. The plan caps how many projects an account has.

Changes data.

Argument Type Notes
name string Required
timezone string Required. IANA time zone, e.g. "America/New_York". New campaigns start in it
postal_address string | null Required. The mailing address printed in marketing email footers. Null for now; marketing email cannot go out until it is set

update_project

Update project. Replace a project's name, time zone and postal address. Send every field: read the project with get_project first and change only what you mean to.

Changes data.

Argument Type Notes
project_id string Required
name string Required
timezone string Required. IANA time zone
postal_address string | null Required. Null removes it, which stops marketing email until it is set again

delete_project

Delete project. Permanently delete a project with all of its contacts, campaigns, broadcasts, templates, sends and settings. This cannot be undone: confirm with the user, naming the project, before you call it.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
project_id string Required

get_account

Get account. Read the account: the plan, this month's usage and the plan's limits on contacts, monthly sends and projects. Check it before a large import or broadcast.

Changes nothing.

No arguments.

create_checkout_session

Create checkout link. Get a Stripe checkout link for a paid plan, for an account on the free plan. Give the link to the user to open and pay; nothing is charged until they do. To change or cancel a paid plan, use create_billing_portal_session.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
plan_id "starter" | "growth" Required
interval "month" | "year" Required

create_billing_portal_session

Create billing portal link. Get a Stripe billing portal link where the user changes or cancels their plan, updates the card and reads invoices. Give the link to the user to open.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

No arguments.

list_api_keys

List API keys. List the account's API keys with their names, prefixes and when each was last used. The keys themselves cannot be read back.

Changes nothing.

No arguments.

create_api_key

Create API key. Create an API key for the REST API (Authorization: Bearer sc_…). It reaches every project of the account. The key is in this result only and cannot be read again, so hand it to the user to store as a secret.

Changes data.

Argument Type Notes
name string Required. Where the key is used, e.g. "Production app"

revoke_api_key

Revoke API key. Delete an API key. Every request that uses it fails from now on, including the app or script it was made for. Confirm with the user first.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
api_key_id string Required. From list_api_keys

list_connected_apps

List connected apps. List the MCP clients the user signed in to Sendocado with OAuth, and when each was connected.

Changes nothing.

No arguments.

disconnect_connected_app

Disconnect app. Sign an MCP client out of the account: its tokens are deleted and its next call fails until the user connects it again. If it is the client running this tool, the agent loses access. Confirm with the user first.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
oauth_client_id string Required. From list_connected_apps

search_contacts

Search contacts. Page through a project's contacts, newest first, optionally narrowed by one of: a search of names and emails, a tag, or an exact email (the way to look a contact up by address). Pass next_cursor back as cursor for the next page.

Changes nothing.

Argument Type Notes
project_id string Required
search string | null Default null. Words matched against email, first name and last name
tag string | null Default null. Only contacts with this tag
email string | null Default null. The one contact with this exact address
cursor string | null Default null. next_cursor from the previous page
limit integer Default 50

get_contact

Get contact. Read one contact by id, with tags, attributes and whether the address is unsubscribed or blocked. To find a contact by email, use search_contacts with email.

Changes nothing.

Argument Type Notes
contact_id string Required

create_contact

Create contact. Add one contact to a project, for example from the user's signup code. Fails with contact_exists when the address is already there; use upsert_contacts to add or update many in one call. A new contact is enrolled in every live campaign whose audience it matches.

Changes data.

Argument Type Notes
project_id string Required
email string Required
first_name string | null Required
last_name string | null Required
timezone string | null Required. IANA time zone. Null to send on each campaign's own time zone
tags string[] Required
attributes object of string Required. Keys are lowercase letters, digits and _

upsert_contacts

Add or update contacts. Add or update up to 1000 contacts in one call, matched by email: the import tool. All or nothing: one bad contact refuses the batch and the error names its index. For an existing contact a null field keeps what is stored, tags are added (never removed) and attributes are set. Contacts are enrolled in every live campaign whose audience they match. Adding a tag such as activated here takes the contact out of campaigns whose audience excludes that tag.

Changes data.

Argument Type Notes
project_id string Required
contacts object[] Required
contacts[].email string Required
contacts[].first_name string | null Required
contacts[].last_name string | null Required
contacts[].timezone string | null Required. IANA time zone
contacts[].tags string[] Required
contacts[].attributes object of string Required

update_contact

Update contact. Replace a contact's names, time zone, tags and attributes; the email never changes. Tags and attributes become exactly the sets sent, so read the contact with get_contact first and send back what should stay. Unsubscribing and blocking are separate tools (unsubscribe_email, block_email).

Changes data.

Argument Type Notes
contact_id string Required
first_name string | null Required
last_name string | null Required
timezone string | null Required. IANA time zone
tags string[] Required
attributes object of string Required

delete_contact

Delete contact. Delete a contact with its tags and attributes. The address stays unsubscribed or blocked if it was. To remove every trace of a person (a privacy request), use erase_email instead.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
contact_id string Required

get_contact_activity

Get contact activity. Page through every email sent to a contact's address, newest first, each with what happened to it (delivered, opened, clicked, replied, bounced, unsubscribed). Includes sends from before the contact was created.

Changes nothing.

Argument Type Notes
contact_id string Required
cursor string | null Default null
limit integer Default 50

list_contact_enrollments

List contact enrollments. List every campaign a contact is in or has been in: whether they are still active, why they left, and the next step and when it is due. A contact enters each campaign at most once.

Changes nothing.

Argument Type Notes
contact_id string Required

export_contact

Export contact. Everything held about a contact and its address, for a subject-access request: the contact, enrollments, every send with its events, replies, unsubscribes, bounces, complaints, blocks and consents. It can be long for a contact who got many emails.

Changes nothing.

Argument Type Notes
contact_id string Required

erase_email

Erase email address. Erase an address from a project for a privacy request: the contact, its enrollments, every send to it and its replies are deleted in the background. Its unsubscribes and blocks are kept without the address, so it stays unmailable. This cannot be undone: confirm with the user first. Poll get_contact_erasure until it is completed.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
email string Required

get_contact_erasure

Get contact erasure. Read an erasure started with erase_email: pending while it runs, completed once every row of the address is gone.

Changes nothing.

Argument Type Notes
contact_erasure_id string Required

list_contact_tags

List contact tags. List the distinct tags on a project's contacts, to build audience filters and branch conditions with tags that exist. is_complete is false when there are more than one call lists.

Changes nothing.

Argument Type Notes
project_id string Required

list_contact_attribute_keys

List contact attribute keys. List the distinct attribute keys on a project's contacts, for audience filters, branch conditions and {{ attributes. }} merge tags. is_complete is false when there are more than one call lists.

Changes nothing.

Argument Type Notes
project_id string Required

unsubscribe_email

Unsubscribe email address. Unsubscribe an address, contact or not: it gets no campaign or broadcast email from this project until it resubscribes. Transactional email still goes out. Use it when a person asks to stop getting marketing email.

Changes data.

Argument Type Notes
project_id string Required
email string Required

resubscribe_email

Resubscribe email address. Opt an unsubscribed address back in to marketing email. Only do this when the person asked to, and send that proof as consent: what they agreed to, from where and when. Never resubscribe someone on the user's say-so alone.

Changes data.

Argument Type Notes
project_id string Required
email string Required
consent object Required
consent.wording string | null Required. What the person agreed to, as they saw it
consent.ip_address string | null Required
consent.user_agent string | null Required
consent.consented_at integer Required. When they agreed, in epoch milliseconds

block_email

Block email address. Block an address: it gets no email at all from this project, transactional included, until it is unblocked. For a person who only wants no marketing email, use unsubscribe_email.

Changes data.

Argument Type Notes
project_id string Required
email string Required
reason string | null Required. A note for whoever reads the blocked list later

unblock_email

Unblock email address. Clear the bounces, spam complaints and manual blocks recorded for an address so far. An address that reported spam needs consent (proof the person asked) to be unblocked. An unsubscribe is not cleared here; use resubscribe_email.

Changes data.

Argument Type Notes
project_id string Required
email string Required
reason string | null Required
consent object | null Required. Required when the address reported spam; else null
consent.wording string | null Required
consent.ip_address string | null Required
consent.user_agent string | null Required
consent.consented_at integer Required. When they agreed, in epoch milliseconds

Record consent. Store proof that an address opted in, as the user's signup form saw it: what the person agreed to, from where and when. It does not resubscribe or unblock anyone by itself.

Changes data.

Argument Type Notes
project_id string Required
email string Required
wording string | null Required
ip_address string | null Required
user_agent string | null Required
consented_at integer Required. When they agreed, in epoch milliseconds

list_blocked_emails

List blocked emails. Page through the addresses a project cannot email for one reason, newest first: unsubscribed, bounced, complained (reported spam) or manual (blocked by the user). is_still_blocked is false once a later unblock or resubscribe cleared it.

Changes nothing.

Argument Type Notes
project_id string Required
reason "unsubscribed" | "bounced" | "complained" | "manual" Required
cursor string | null Default null
limit integer Default 50

list_campaigns

List campaigns. Page through a project's campaigns, newest first: name, status (draft, live or paused), audience, schedule and how many people are in each. Steps are left out; read one campaign with get_campaign for its emails.

Changes nothing.

Argument Type Notes
project_id string Required
status "draft" | "live" | "paused" | null Default null
cursor string | null Default null
limit integer Default 50

get_campaign

Get campaign. Read a campaign with every step in walk order (from the first step along next; at a branch, the true path before the false one). Each step has its id: use the ids to edit steps, never re-create a step to change it.

Changes nothing.

Argument Type Notes
campaign_id string Required

create_campaign

Create campaign. Create a campaign as a draft: its audience, schedule and emails, sent in the order given with delay_days before each (the first counts from when a contact enters). Nothing sends while it is a draft. Show the user the result and the audience (preview_audience) before set_campaign_status live. Add branches afterwards with update_campaign_step. For activation emails, exclude the tag the user's app adds at finished setup, so people leave once they are set up.

Changes data.

Argument Type Notes
project_id string Required
name string Required
timezone string Required. IANA time zone the send window is read in, unless the contact has its own
send_window object | null Required. When emails may go out, in the contact's time zone (else the campaign's). Null sends at any hour
send_window.days integer[] Required. Days of the week, 0 = Sunday
send_window.start_hour integer Required
send_window.end_hour integer Required
is_stopped_on_reply boolean Required. True ends a contact's campaign when they reply to any of its emails
filter_groups object[] Required. A contact matches when it passes every filter of at least one group. An empty list means every contact in the project
filter_groups[].filters object[] Required
filter_groups[].filters[].type "tag" Required. When type is "tag"
filter_groups[].filters[].op "has" | "has_not" Required. When type is "tag"
filter_groups[].filters[].tag string Required. When type is "tag"
filter_groups[].filters[].type "attribute" Required. When type is "attribute"
filter_groups[].filters[].op "equals" | "not_equals" Required. When type is "attribute"
filter_groups[].filters[].key string Required. When type is "attribute"
filter_groups[].filters[].value string Required. When type is "attribute"
filter_groups[].filters[].type "added_days_ago" Required. When type is "added_days_ago"
filter_groups[].filters[].op "less_than" | "more_than" Required. When type is "added_days_ago"
filter_groups[].filters[].days integer Required. When type is "added_days_ago"
filter_groups[].filters[].type "opened" | "clicked" Required. When type is "opened" | "clicked"
filter_groups[].filters[].op "within" | "not_within" Required. When type is "opened" | "clicked"
filter_groups[].filters[].days integer Required. When type is "opened" | "clicked"
filter_groups[].filters[].type "campaign" Required. When type is "campaign"
filter_groups[].filters[].op "is_enrolled" | "is_not_enrolled" | "was_enrolled" | "was_never_enrolled" Required. When type is "campaign"
filter_groups[].filters[].campaign_id string Required. When type is "campaign"
steps object[] Required
steps[].delay_days integer Required
steps[].subject string | null Required
steps[].preview_text string | null Required. The line inbox previews show after the subject
steps[].html string | null Required. The body as HTML. Null when you send markdown or a template_id
steps[].markdown string | null Required. The body as markdown. Null when you send html or a template_id
steps[].template_id string | null Required. Copy a template's subject, preview text and body; then subject, preview_text, html and markdown are null

update_campaign

Update campaign. Replace a campaign's name, time zone, send window, reply handling and audience, all at once: every field is required and none has a default. Read the campaign with get_campaign first and send back what should stay; filter_groups [] means every contact. On a live campaign the new audience applies at once: new matches are enrolled and people who stop matching leave. The status never changes here (set_campaign_status), and steps are edited with the step tools.

Changes data.

Argument Type Notes
campaign_id string Required
name string Required
timezone string Required. IANA time zone
send_window object | null Required. When emails may go out, in the contact's time zone (else the campaign's). Null sends at any hour
send_window.days integer[] Required. Days of the week, 0 = Sunday
send_window.start_hour integer Required
send_window.end_hour integer Required
is_stopped_on_reply boolean Required
filter_groups object[] Required. A contact matches when it passes every filter of at least one group. An empty list means every contact in the project
filter_groups[].filters object[] Required
filter_groups[].filters[].type "tag" Required. When type is "tag"
filter_groups[].filters[].op "has" | "has_not" Required. When type is "tag"
filter_groups[].filters[].tag string Required. When type is "tag"
filter_groups[].filters[].type "attribute" Required. When type is "attribute"
filter_groups[].filters[].op "equals" | "not_equals" Required. When type is "attribute"
filter_groups[].filters[].key string Required. When type is "attribute"
filter_groups[].filters[].value string Required. When type is "attribute"
filter_groups[].filters[].type "added_days_ago" Required. When type is "added_days_ago"
filter_groups[].filters[].op "less_than" | "more_than" Required. When type is "added_days_ago"
filter_groups[].filters[].days integer Required. When type is "added_days_ago"
filter_groups[].filters[].type "opened" | "clicked" Required. When type is "opened" | "clicked"
filter_groups[].filters[].op "within" | "not_within" Required. When type is "opened" | "clicked"
filter_groups[].filters[].days integer Required. When type is "opened" | "clicked"
filter_groups[].filters[].type "campaign" Required. When type is "campaign"
filter_groups[].filters[].op "is_enrolled" | "is_not_enrolled" | "was_enrolled" | "was_never_enrolled" Required. When type is "campaign"
filter_groups[].filters[].campaign_id string Required. When type is "campaign"

set_campaign_status

Set campaign status. Start, pause or restart a campaign. live enrolls every contact the audience matches and starts sending them real email; only set it after the user has seen the campaign and its audience and said yes. A draft goes live, a live campaign pauses (nothing more is sent until it is live again), and a paused one goes live again. Going live fails with the first problem get_campaign_readiness would list.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
campaign_id string Required
status "draft" | "live" | "paused" Required

delete_campaign

Delete campaign. Delete a campaign and its steps. Only a campaign nobody was ever enrolled in can be deleted (conflict otherwise); pause the others with set_campaign_status.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
campaign_id string Required

get_campaign_readiness

Check campaign readiness. List what stands between a campaign and going live: no_steps, step_missing_subject, step_missing_content, sender_not_ready, domain_not_verified, postal_address_missing, audience_references_missing_campaign. An empty list means it can go live now.

Changes nothing.

Argument Type Notes
campaign_id string Required

add_campaign_step

Add campaign step. Add an email to a campaign at a chosen point. delay_days is the wait after the email before it (or after the contact enters, for the first). On a live campaign the step applies at once to people who have not reached that point.

Changes data.

Argument Type Notes
campaign_id string Required
insert_after object Required. Where the step goes: at the start, after a step, or first on a branch's true or false path. What followed that point now follows this step
insert_after.type "start" Required. When type is "start"
insert_after.type "step" | "if_true" | "if_false" Required. When type is "step" | "if_true" | "if_false"
insert_after.step_id string Required. When type is "step" | "if_true" | "if_false"
delay_days integer Required
subject string | null Required
preview_text string | null Required. The line inbox previews show after the subject
html string | null Required. The body as HTML. Null when you send markdown or a template_id
markdown string | null Required. The body as markdown. Null when you send html or a template_id
template_id string | null Required. Copy a template's subject, preview text and body; then subject, preview_text, html and markdown are null

get_campaign_step

Get campaign step. Read one email of a campaign with its full content, merge tags and what follows it (the next step, the end, or a branch).

Changes nothing.

Argument Type Notes
step_id string Required

update_campaign_step

Update campaign step. Replace one email's delay and content, and set or clear its branch: an if/else check delay_days after the send, whose true path starts empty (add steps to it with add_campaign_step if_true) and whose false path keeps what followed the step. branch null clears a branch whose true path is empty, or keeps a plain step plain. Every field is required: read the step with get_campaign_step first and send back what should stay. The step keeps its id and stats.

Changes data.

Argument Type Notes
step_id string Required
delay_days integer Required
subject string | null Required
preview_text string | null Required. The line inbox previews show after the subject
html string | null Required. The body as HTML. Null when you send markdown or a template_id
markdown string | null Required. The body as markdown. Null when you send html or a template_id
template_id string | null Required. Copy a template's subject, preview text and body; then subject, preview_text, html and markdown are null
branch object | null Required
branch.delay_days integer Required
branch.condition object Required. What is checked about the contact when the branch is reached
branch.condition.type "opened_step" | "clicked_step" Required. When type is "opened_step" | "clicked_step"
branch.condition.step_id string Required. When type is "opened_step" | "clicked_step". This step or one before it on the same path
branch.condition.type "has_tag" Required. When type is "has_tag"
branch.condition.tag string Required. When type is "has_tag"
branch.condition.type "attribute_equals" Required. When type is "attribute_equals"
branch.condition.key string Required. When type is "attribute_equals"
branch.condition.value string Required. When type is "attribute_equals"

move_campaign_step

Move campaign step. Move an email, with its id and stats, to another point in the campaign; the steps around its old place close the gap.

Changes data.

Argument Type Notes
step_id string Required
insert_after object Required. Where the step goes: at the start, after a step, or first on a branch's true or false path. What followed that point now follows this step
insert_after.type "start" Required. When type is "start"
insert_after.type "step" | "if_true" | "if_false" Required. When type is "step" | "if_true" | "if_false"
insert_after.step_id string Required. When type is "step" | "if_true" | "if_false"

delete_campaign_step

Delete campaign step. Remove an email from a campaign. People waiting for it continue with what followed it. A step with a branch, or one a branch condition names, cannot be removed (conflict): clear the branch first.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
step_id string Required

list_campaign_enrollments

List campaign enrollments. Page through the people in a campaign. active lists those still in it, soonest next email first; completed, unsubscribed, blocked and exited list those who left, newest first, with the reason. Each row has the enrollment id remove_campaign_enrollment takes.

Changes nothing.

Argument Type Notes
campaign_id string Required
status "active" | "completed" | "unsubscribed" | "blocked" | "exited" Default "active"
cursor string | null Default null
limit integer Default 50

remove_campaign_enrollment

Remove person from campaign. Take one person out of a campaign now: they get none of its remaining emails and never join it again. Their other campaigns are untouched.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
enrollment_id string Required. From list_campaign_enrollments or list_contact_enrollments

get_campaign_stats

Get campaign stats. How a campaign is doing over a range of UTC days (the last 30 by default): sends, deliveries, opens, clicks, replies, bounces, complaints and unsubscribes in total, per step and per day, the rates, clicks per link, and how many people are in it and why they left. Counts land on the day each email was sent. Stats are folded about once a minute.

Changes nothing.

Argument Type Notes
campaign_id string Required
from_day string | null Default null. YYYY-MM-DD, UTC
to_day string | null Default null. YYYY-MM-DD, UTC

preview_audience

Preview audience. Count the contacts a set of filter groups matches right now, with a sample of who they are: show it to the user before a campaign goes live or a broadcast sends. When is_complete is false, call again with the returned cursor; the counts of the calls add up. Counts everyone who matches, including addresses that are blocked or unsubscribed and will be skipped.

Changes nothing.

Argument Type Notes
project_id string Required
filter_groups object[] Required. A contact matches when it passes every filter of at least one group. An empty list means every contact in the project
filter_groups[].filters object[] Required
filter_groups[].filters[].type "tag" Required. When type is "tag"
filter_groups[].filters[].op "has" | "has_not" Required. When type is "tag"
filter_groups[].filters[].tag string Required. When type is "tag"
filter_groups[].filters[].type "attribute" Required. When type is "attribute"
filter_groups[].filters[].op "equals" | "not_equals" Required. When type is "attribute"
filter_groups[].filters[].key string Required. When type is "attribute"
filter_groups[].filters[].value string Required. When type is "attribute"
filter_groups[].filters[].type "added_days_ago" Required. When type is "added_days_ago"
filter_groups[].filters[].op "less_than" | "more_than" Required. When type is "added_days_ago"
filter_groups[].filters[].days integer Required. When type is "added_days_ago"
filter_groups[].filters[].type "opened" | "clicked" Required. When type is "opened" | "clicked"
filter_groups[].filters[].op "within" | "not_within" Required. When type is "opened" | "clicked"
filter_groups[].filters[].days integer Required. When type is "opened" | "clicked"
filter_groups[].filters[].type "campaign" Required. When type is "campaign"
filter_groups[].filters[].op "is_enrolled" | "is_not_enrolled" | "was_enrolled" | "was_never_enrolled" Required. When type is "campaign"
filter_groups[].filters[].campaign_id string Required. When type is "campaign"
cursor string | null Default null. From the previous call while is_complete is false

draft_campaign

Draft campaign with AI. Ask Sendocado's writer for a campaign draft from a brief: who it is for and what it should achieve. It answers at once with status pending; read it with get_campaign_draft every few seconds until it is ready or failed. A ready draft's name, filter_groups and steps fit create_campaign (each step as html). It creates nothing by itself. If you can write the emails yourself, you do not need it.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
prompt string Required. The brief, in plain words

get_campaign_draft

Get campaign draft. Read a draft started with draft_campaign: pending while it is written, then ready with a result to pass to create_campaign, or failed with an error.

Changes nothing.

Argument Type Notes
campaign_draft_id string Required

list_broadcasts

List broadcasts. Page through a project's broadcasts, newest first, without their bodies: name, status, audience, subject and schedule. Read one with get_broadcast for its html.

Changes nothing.

Argument Type Notes
project_id string Required
status "draft" | "scheduled" | "sending" | "sent" | "canceled" | null Default null
cursor string | null Default null
limit integer Default 50

get_broadcast

Get broadcast. Read a broadcast with its audience, content, merge tags, status and schedule.

Changes nothing.

Argument Type Notes
broadcast_id string Required

create_broadcast

Create broadcast. Create a broadcast as a draft: one email to everyone the audience matches, sent once. Nothing sends while it is a draft. Preview the audience (preview_audience), send a test (send_test_email) and show the user before set_broadcast_status.

Changes data.

Argument Type Notes
project_id string Required
name string Required
filter_groups object[] Required. A contact matches when it passes every filter of at least one group. An empty list means every contact in the project
filter_groups[].filters object[] Required
filter_groups[].filters[].type "tag" Required. When type is "tag"
filter_groups[].filters[].op "has" | "has_not" Required. When type is "tag"
filter_groups[].filters[].tag string Required. When type is "tag"
filter_groups[].filters[].type "attribute" Required. When type is "attribute"
filter_groups[].filters[].op "equals" | "not_equals" Required. When type is "attribute"
filter_groups[].filters[].key string Required. When type is "attribute"
filter_groups[].filters[].value string Required. When type is "attribute"
filter_groups[].filters[].type "added_days_ago" Required. When type is "added_days_ago"
filter_groups[].filters[].op "less_than" | "more_than" Required. When type is "added_days_ago"
filter_groups[].filters[].days integer Required. When type is "added_days_ago"
filter_groups[].filters[].type "opened" | "clicked" Required. When type is "opened" | "clicked"
filter_groups[].filters[].op "within" | "not_within" Required. When type is "opened" | "clicked"
filter_groups[].filters[].days integer Required. When type is "opened" | "clicked"
filter_groups[].filters[].type "campaign" Required. When type is "campaign"
filter_groups[].filters[].op "is_enrolled" | "is_not_enrolled" | "was_enrolled" | "was_never_enrolled" Required. When type is "campaign"
filter_groups[].filters[].campaign_id string Required. When type is "campaign"
subject string | null Required
preview_text string | null Required. The line inbox previews show after the subject
html string | null Required. The body as HTML. Null when you send markdown or a template_id
markdown string | null Required. The body as markdown. Null when you send html or a template_id
template_id string | null Required. Copy a template's subject, preview text and body; then subject, preview_text, html and markdown are null

update_broadcast

Update broadcast. Replace a draft or scheduled broadcast's name, audience and content, all at once: every field is required. Read it with get_broadcast first and send back what should stay; filter_groups [] means every contact. Once a broadcast is sending it cannot be edited. The status never changes here (set_broadcast_status).

Changes data.

Argument Type Notes
broadcast_id string Required
name string Required
filter_groups object[] Required. A contact matches when it passes every filter of at least one group. An empty list means every contact in the project
filter_groups[].filters object[] Required
filter_groups[].filters[].type "tag" Required. When type is "tag"
filter_groups[].filters[].op "has" | "has_not" Required. When type is "tag"
filter_groups[].filters[].tag string Required. When type is "tag"
filter_groups[].filters[].type "attribute" Required. When type is "attribute"
filter_groups[].filters[].op "equals" | "not_equals" Required. When type is "attribute"
filter_groups[].filters[].key string Required. When type is "attribute"
filter_groups[].filters[].value string Required. When type is "attribute"
filter_groups[].filters[].type "added_days_ago" Required. When type is "added_days_ago"
filter_groups[].filters[].op "less_than" | "more_than" Required. When type is "added_days_ago"
filter_groups[].filters[].days integer Required. When type is "added_days_ago"
filter_groups[].filters[].type "opened" | "clicked" Required. When type is "opened" | "clicked"
filter_groups[].filters[].op "within" | "not_within" Required. When type is "opened" | "clicked"
filter_groups[].filters[].days integer Required. When type is "opened" | "clicked"
filter_groups[].filters[].type "campaign" Required. When type is "campaign"
filter_groups[].filters[].op "is_enrolled" | "is_not_enrolled" | "was_enrolled" | "was_never_enrolled" Required. When type is "campaign"
filter_groups[].filters[].campaign_id string Required. When type is "campaign"
subject string | null Required
preview_text string | null Required. The line inbox previews show after the subject
html string | null Required. The body as HTML. Null when you send markdown or a template_id
markdown string | null Required. The body as markdown. Null when you send html or a template_id
template_id string | null Required. Copy a template's subject, preview text and body; then subject, preview_text, html and markdown are null

get_broadcast_readiness

Check broadcast readiness. List what would refuse scheduling or sending a broadcast now: missing_subject, missing_content, audience_references_missing_campaign, postal_address_missing, sender_not_ready, domain_not_verified. An empty list means it can send.

Changes nothing.

Argument Type Notes
broadcast_id string Required

set_broadcast_status

Send, schedule or cancel broadcast. Move a broadcast along: sending emails everyone the audience matches within seconds and cannot be undone, so only call it after the user has seen the email and the audience and said yes. scheduled sends at scheduled_for (a future time); draft unschedules a scheduled one; canceled stops one that is sending (emails already handed over still go). Scheduling or sending fails with the first problem get_broadcast_readiness would list.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
broadcast_id string Required
status "draft" | "scheduled" | "sending" | "canceled" Required
scheduled_for integer | null Required. Epoch milliseconds, for scheduled; else null

delete_broadcast

Delete broadcast. Delete a draft broadcast. A broadcast that was scheduled or sent stays as the record of what went out (conflict).

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
broadcast_id string Required

get_broadcast_stats

Get broadcast stats. How a broadcast did: sends, deliveries, opens, clicks, replies, bounces, complaints and unsubscribes in total and per day, the rates (fractions of sent_count, null before anything was sent) and clicks per link. Numbers trail the events by about a minute.

Changes nothing.

Argument Type Notes
broadcast_id string Required

send_transactional_email

Send transactional email. Send one email to one address now, for something the person did or asked for (a receipt, a login link, a notice), never marketing. It goes out within seconds and cannot be recalled. Merge tags read variables as {{ variables. }}. Pass an idempotency_key so a retry never sends twice: the same key returns the first email and sends nothing. A blocked address (recipient_blocked) or a sender that cannot send yet (sender_not_ready) is refused.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
to string Required
subject string | null Required
preview_text string | null Required. The line inbox previews show after the subject
html string | null Required. The body as HTML. Null when you send markdown or a template_id
markdown string | null Required. The body as markdown. Null when you send html or a template_id
template_id string | null Required. Copy a template's subject, preview text and body; then subject, preview_text, html and markdown are null
variables object of string Required. Values for {{ variables. }} merge tags
reply_to string | null Required. Null uses the sender's reply-to address
idempotency_key string | null Required. Your own unique id for this email, e.g. "order-1234-receipt"

get_transactional_email

Get transactional email. Read a transactional email with its content and status: queued, sent, then what the provider reported (delivered, bounced, failed and so on). get_send with its send_id shows each event and failure.

Changes nothing.

Argument Type Notes
transactional_email_id string Required

list_transactional_emails

List transactional emails. Page through a project's transactional emails, newest first, without their bodies.

Changes nothing.

Argument Type Notes
project_id string Required
cursor string | null Default null
limit integer Default 50

send_test_email

Send test email. Send a test of an email to the user's own inbox, so they see the real thing before anyone else gets it. Merge tags use contact_id's values when it is set. Test emails are never tracked and have a tighter rate limit. Send it only to an address the user gave you as theirs.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
to string Required. The user's own address
subject string | null Required
preview_text string | null Required. The line inbox previews show after the subject
html string | null Required. The body as HTML. Null when you send markdown or a template_id
markdown string | null Required. The body as markdown. Null when you send html or a template_id
template_id string | null Required. Copy a template's subject, preview text and body; then subject, preview_text, html and markdown are null
contact_id string | null Required. Fill merge tags from this contact; null leaves them as their fallbacks

get_test_email

Get test email. Read a test email's status: queued, sent, then what the provider reported. A failed one usually means the sender is not set up yet; get_send with its send_id shows why.

Changes nothing.

Argument Type Notes
test_email_id string Required

list_sends

List sends. Page through every email a project sent, newest first, from every source, with its status and whether it was opened, clicked or replied to. Narrow by one of: an address, a contact, a campaign, a broadcast, or status failed.

Changes nothing.

Argument Type Notes
project_id string Required
email string | null Default null
contact_id string | null Default null
campaign_id string | null Default null
broadcast_id string | null Default null. A broadcast's sends are its recipients
status "failed" | null Default null. failed lists only sends that were given up
cursor string | null Default null
limit integer Default 50

get_send

Get send. Read one email that was sent, with every event (delivered, opened, clicked, bounced and so on, oldest first) and every failed provider attempt with the provider's message: the place to find out why an email did not arrive.

Changes nothing.

Argument Type Notes
send_id string Required

list_email_events

List email events. Page through a project's events of one type, newest first: delivered, delivery_delayed, soft_bounced, opened, clicked, bounced, complained, unsubscribed or failed. For everything that happened to one email, use get_send.

Changes nothing.

Argument Type Notes
project_id string Required
type "delivered" | "delivery_delayed" | "soft_bounced" | "opened" | "clicked" | "bounced" | "complained" | "unsubscribed" | "failed" Required
cursor string | null Default null
limit integer Default 50

list_replies

List replies. Page through what people wrote back, newest first, with a short preview of each; narrow by one of a campaign, a contact, a broadcast, or unread only. Auto-replies (out of office) are marked. Read one with get_reply for the full text.

Changes nothing.

Argument Type Notes
project_id string Required
campaign_id string | null Default null
contact_id string | null Default null
broadcast_id string | null Default null
is_unread true | null Default null. true lists only replies nobody has read
cursor string | null Default null
limit integer Default 50

get_reply

Get reply. Read one reply with its full text and attachments, and the email, campaign or broadcast it answers when known. html_url serves the reply's HTML as plain text. Reading it does not mark it read; use update_reply.

Changes nothing.

Argument Type Notes
reply_id string Required

update_reply

Mark reply read or unread. Mark a reply read (is_unread false) once the user has seen it or it is handled, or unread again to come back to it.

Changes data.

Argument Type Notes
reply_id string Required
is_unread boolean Required

list_templates

List templates. Page through a project's templates, newest first: id, name and subject. Read one with get_template for its content.

Changes nothing.

Argument Type Notes
project_id string Required
cursor string | null Default null
limit integer Default 50

get_template

Get template. Read a template's subject, preview text, HTML, plain text and merge tags.

Changes nothing.

Argument Type Notes
template_id string Required

create_template

Create template. Save reusable email content in a project. Campaign steps, broadcasts and transactional emails take it by template_id and keep their own copy, so later edits to the template do not change them.

Changes data.

Argument Type Notes
project_id string Required
name string Required
subject string Required
preview_text string | null Required
html string | null Required. The body as HTML. Null when you send markdown
markdown string | null Required. The body as markdown. Null when you send html

update_template

Update template. Replace a template's name, subject, preview text and body, all at once: read it with get_template first and send back what should stay. Emails already made from it keep their own copy.

Changes data.

Argument Type Notes
template_id string Required
name string Required
subject string Required
preview_text string | null Required
html string | null Required. The body as HTML. Null when you send markdown
markdown string | null Required. The body as markdown. Null when you send html

delete_template

Delete template. Delete a template. Emails already made from it keep their own copy.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
template_id string Required

get_sender

Get sender. Read the project's sender: the provider account email goes out through, the from address, the hourly cap, and whether it is ready (status ready, failing with the reason in last_check, or unchecked). Credentials are never shown. Fails with sender_not_found when none is connected.

Changes nothing.

Argument Type Notes
project_id string Required

update_sender

Connect or update sender. Connect the project's email provider (Resend, Amazon SES, Postmark, SendGrid, Mailgun or SMTP), or change its from address or hourly cap. Credentials are tested first and nothing is saved if they fail; then Sendocado sets up the provider's delivery events. Every field is required: read get_sender first and send null credentials to keep the stored ones. Switching providers replaces the old connection and its sending domains. Credentials come from the user; never invent them.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
from_name string | null Required. The name people see the email from, e.g. "Maya at Acme"
from_email string Required. An address on a domain the provider has verified
reply_to_email string | null Required. Reply-To for transactional email; marketing replies come to Sendocado
max_sends_per_hour integer Required
provider object Required. The provider account and its credentials. A null credential keeps the one stored for the same provider. Gmail is first connected in the app
provider.type "resend" Required. When type is "resend"
provider.api_key string | null Required. When type is "resend"
provider.type "ses" Required. When type is "ses"
provider.region string Required. When type is "ses"
provider.access_key_id string | null Required. When type is "ses"
provider.secret_access_key string | null Required. When type is "ses"
provider.type "postmark" Required. When type is "postmark"
provider.server_token string | null Required. When type is "postmark"
provider.account_token string | null Required. When type is "postmark"
provider.message_stream string Required. When type is "postmark"
provider.type "sendgrid" Required. When type is "sendgrid"
provider.api_key string | null Required. When type is "sendgrid"
provider.type "mailgun" Required. When type is "mailgun"
provider.api_key string | null Required. When type is "mailgun"
provider.domain string Required. When type is "mailgun"
provider.region "us" | "eu" Required. When type is "mailgun"
provider.type "smtp" Required. When type is "smtp"
provider.host string Required. When type is "smtp"
provider.port integer Required. When type is "smtp"
provider.is_secure boolean Required. When type is "smtp"
provider.username string Required. When type is "smtp"
provider.password string | null Required. When type is "smtp"
provider.type "gmail" Required. When type is "gmail"

check_sender

Test sender credentials. Test provider credentials with the provider without saving anything, before update_sender. A null credential tests the one stored for the same provider. Answers is_ok, or the provider's reason.

Changes nothing; asks your email provider.

Argument Type Notes
project_id string Required
from_name string | null Required. The name people see the email from, e.g. "Maya at Acme"
from_email string Required. An address on a domain the provider has verified
reply_to_email string | null Required. Reply-To for transactional email; marketing replies come to Sendocado
max_sends_per_hour integer Required
provider object Required. The provider account and its credentials. A null credential keeps the one stored for the same provider. Gmail is first connected in the app
provider.type "resend" Required. When type is "resend"
provider.api_key string | null Required. When type is "resend"
provider.type "ses" Required. When type is "ses"
provider.region string Required. When type is "ses"
provider.access_key_id string | null Required. When type is "ses"
provider.secret_access_key string | null Required. When type is "ses"
provider.type "postmark" Required. When type is "postmark"
provider.server_token string | null Required. When type is "postmark"
provider.account_token string | null Required. When type is "postmark"
provider.message_stream string Required. When type is "postmark"
provider.type "sendgrid" Required. When type is "sendgrid"
provider.api_key string | null Required. When type is "sendgrid"
provider.type "mailgun" Required. When type is "mailgun"
provider.api_key string | null Required. When type is "mailgun"
provider.domain string Required. When type is "mailgun"
provider.region "us" | "eu" Required. When type is "mailgun"
provider.type "smtp" Required. When type is "smtp"
provider.host string Required. When type is "smtp"
provider.port integer Required. When type is "smtp"
provider.is_secure boolean Required. When type is "smtp"
provider.username string Required. When type is "smtp"
provider.password string | null Required. When type is "smtp"
provider.type "gmail" Required. When type is "gmail"

delete_sender

Disconnect sender. Disconnect the project's email provider. Sendocado removes the webhooks it created in the provider account first; the provider keeps its domains. Nothing can be sent until a sender is connected again, so live campaigns stop sending. Confirm with the user first.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required

list_sending_domains

List sending domains. List the project's sending domains with their verification status and the DNS records each needs (DKIM, SPF, MAIL FROM, return path, DMARC).

Changes nothing.

Argument Type Notes
project_id string Required

create_sending_domain

Add sending domain. Add a domain to send from at the project's provider, and get the DNS records the user must add at their DNS host. Verification follows once the records are live; check_sending_domain asks the provider again. Needs a connected sender.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
domain string Required

get_sending_domain

Get sending domain. Read one sending domain: whether it is verified and the status of each DNS record.

Changes nothing.

Argument Type Notes
sending_domain_id string Required

check_sending_domain

Check sending domain DNS. Ask the provider to look at a domain's DNS again, after the user added the records. The result lands within a minute; read it with get_sending_domain. DNS changes can take hours to show.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
sending_domain_id string Required

delete_sending_domain

Remove sending domain. Remove a sending domain from Sendocado. The provider account keeps it, since it may send other mail from it.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
sending_domain_id string Required

list_webhook_endpoints

List webhook endpoints. List the URLs the project posts events to, with the event types each gets and whether it is on. An endpoint that keeps failing is turned off by Sendocado.

Changes nothing.

Argument Type Notes
project_id string Required

create_webhook_endpoint

Create webhook endpoint. Have Sendocado post events (sends, deliveries, bounces, opens, clicks, replies, unsubscribes) to a public https URL of the user's app. Events are signed with Standard Webhooks; the signing secret is in this result and in get_webhook_endpoint_secret.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
url string Required. A public https URL
event_types ("email.sent" | "email.delivered" | "email.delivery_delayed" | "email.bounced" | "email.complained" | "email.failed" | "email.opened" | "email.clicked" | "reply.received" | "contact.unsubscribed")[] Required

get_webhook_endpoint

Get webhook endpoint. Read one webhook endpoint: its URL, event types and whether it is on.

Changes nothing.

Argument Type Notes
webhook_endpoint_id string Required

update_webhook_endpoint

Update webhook endpoint. Replace a webhook endpoint's URL, event types and on/off state, all at once. is_enabled true turns back on an endpoint Sendocado turned off after repeated failures.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
webhook_endpoint_id string Required
url string Required
event_types ("email.sent" | "email.delivered" | "email.delivery_delayed" | "email.bounced" | "email.complained" | "email.failed" | "email.opened" | "email.clicked" | "reply.received" | "contact.unsubscribed")[] Required
is_enabled boolean Required

delete_webhook_endpoint

Delete webhook endpoint. Stop posting events to a URL and delete the endpoint with its delivery log.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
webhook_endpoint_id string Required

get_webhook_endpoint_secret

Get webhook signing secret. Read the secret a webhook endpoint's events are signed with, for the user's app to verify them. Treat it like a password.

Changes nothing.

Argument Type Notes
webhook_endpoint_id string Required

rotate_webhook_endpoint_secret

Rotate webhook signing secret. Replace a webhook endpoint's signing secret. Every attempt from now on, retries included, is signed with the new one, so the user's app must switch at once or it will reject events. Confirm with the user first.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
webhook_endpoint_id string Required

list_webhook_deliveries

List webhook deliveries. Page through the attempts to post events to one endpoint, newest first, with the status code, error and time each took: the place to see why an endpoint failed.

Changes nothing.

Argument Type Notes
webhook_endpoint_id string Required
cursor string | null Default null
limit integer Default 50

redeliver_webhook_event

Resend webhook event. Post one past event to an endpoint again, once, now; the attempt shows in list_webhook_deliveries. The endpoint must be on.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
webhook_endpoint_id string Required
webhook_event_id string Required. From list_webhook_deliveries

upload_image

Upload image. Host a PNG, JPEG, GIF or WebP image of up to 5 MB for use in emails, and get its public url to put in an . Prefer url (Sendocado downloads it) over base64_data, which passes every byte through your context. The type is read from the file itself.

Changes data and reaches outside Sendocado: it can send email or call your provider, Stripe or a URL. Clients may ask you before they run it.

Argument Type Notes
project_id string Required
url string | null Default null. A public address Sendocado downloads the image from
base64_data string | null Default null. The image's bytes as base64, when it has no public address
file_name string | null Default null

list_images

List images. Page through the images hosted for a project's emails, newest first, with their public urls.

Changes nothing.

Argument Type Notes
project_id string Required
cursor string | null Default null
limit integer Default 50

delete_image

Delete image. Delete a hosted image. It stops loading in every email that uses it, sent ones included, so check it is unused first.

Deletes or replaces data. Clients may ask you before they run it.

Argument Type Notes
image_id string Required

get_overview

Get project overview. How a project is doing over the last 7, 30 or 90 UTC days next to the same span before: sends, deliveries, opens, clicks, replies, bounces, complaints and unsubscribes with their rates, a daily series, the top campaigns, the contact count, live campaigns, whether the sender is ready, and whether bad bounce or complaint rates have paused campaigns and broadcasts. Start here when the user asks how things are going.

Changes nothing.

Argument Type Notes
project_id string Required
days 7 | 30 | 90 Default 30