API reference

Everything a workspace does in Zeebra, from your own code or an AI agent. Create a key in Settings → API and send it as Authorization: Bearer <key>. Results are in data; errors answer {"error": {"code", "message"}}. Each key can make 300 requests a minute.

Every operation is also a tool of the MCP server. Sending can’t be undone: review a campaign first and pass its recipient count as confirm_recipients. More in API and MCP.

Base URL
https://zeebra.ai/api/v1
MCP server
https://zeebra.ai/mcp
For language models
llms-full.txt

Workspace

Get the overview

GET/api/v1/overviewMCP tool get_overview

The workspace at a glance: contacts by lead type and status, lists, segments, campaigns by status, email results of the last 30 days, and whether it can send. A good first call.

curl "https://zeebra.ai/api/v1/overview" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Contacts

List contacts

GET/api/v1/contactsMCP tool list_contacts

Lists the workspace's contacts, newest first, a page at a time. Filter on any filterable property, custom ones included (filter, or a rule tree in where, like segments), and sort on any sortable property (prefix with - for descending).

ParameterTypeDescription
filterobjectqueryProperty key => value. Keys: q (search: every word in a name, email, company or searchable property), email, tag (name), list_id, account_id (their company, see list_accounts), segment_id, created_after (date), or any filterable property key (get_segment_fields lists them; custom ones are field:<key>). A value is "operator:value" with the property's operators (contains:acme, gte:10, between:1,5, in_period:last_30_days, within_last:7, is_set:), or just a value: is for text, in for choices (comma separated), eq for numbers, has_any for multiple choice, true or false for yes-or-no.
whereobjectqueryA rule tree like a segment's, for AND/OR: {"match": "all"|"any", "rules": [{"field": "city", "op": "is", "value": "London"}, {"match": "any", "rules": [...]}]}. Applied with filter.
sortstringqueryUp to 3 sortable property keys, comma separated, - for descending: -created_at (the default), email, field:plan,-last_engaged_at.
pageintegerqueryPage number, from 1.
per_pageintegerqueryItems per page, up to 100.
curl "https://zeebra.ai/api/v1/contacts?filter%5Bstatus%5D=active&filter%5Blead_type%5D=opted_in&filter%5Bfield%3Aplan%5D=is%3APro&filter%5Bcreated_at%5D=in_period%3Athis_month&where%5Bmatch%5D=any&where%5Brules%5D%5B0%5D%5Bfield%5D=city&where%5Brules%5D%5B0%5D%5Bop%5D=is&where%5Brules%5D%5B0%5D%5Bvalue%5D=London&where%5Brules%5D%5B1%5D%5Bfield%5D=field%3Aplan&where%5Brules%5D%5B1%5D%5Bop%5D=in&where%5Brules%5D%5B1%5D%5Bvalue%5D%5B0%5D=pro" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Contact in data, a page at a time (meta has the totals).

Get a contact

GET/api/v1/contacts/{id}MCP tool get_contact

Gets one contact with its tags, custom property values, country, consent record, the ids of the lists it is on, and its history: every status change with who made it and how (the newest 100), opt-ins with their consent, and list cleanup's emails. list_contact_emails has the emails it got.

ParameterTypeDescription
id*integerpathThe contact's id.
curl "https://zeebra.ai/api/v1/contacts/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Contact in data.

List a contact's emails

GET/api/v1/contacts/{id}/emailsMCP tool list_contact_emails

Lists the campaign emails a contact got, newest first, a page at a time: each with its campaign, when it was sent and delivered or bounced, their first real open and click (machine opens never count), the links they clicked, and whether they unsubscribed or marked it as spam from it.

ParameterTypeDescription
id*integerpathThe contact's id.
pageintegerqueryPage number, from 1.
per_pageintegerqueryItems per page, up to 100.
curl "https://zeebra.ai/api/v1/contacts/123/emails" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of ContactEmail in data, a page at a time (meta has the totals).

Add a contact

POST/api/v1/contactsMCP tool add_contact

Adds one contact. Opted-in contacts get campaigns and need a consent note saying how they agreed; prospects never get campaigns. Refuses an address that already exists or is on a do-not-email list. Use add_signup for people signing up on a website.

ParameterTypeDescription
email*stringbody
first_namestringbody
last_namestringbody
titlestringbody
companystringbodyTheir company's name: they join the account of that name, made if there is none. Left out, a work email's domain finds or makes it.
account_idintegerbodyTheir company: an account of the workspace (list_accounts). Wins over company.
phonestringbody
citystringbody
lead_typestringbodyopted_in: agreed to get your emails. prospect: not yet (the default).opted_in · prospect
consent_notestringbodyRequired for opted_in: how they agreed, e.g. "Signed up at our booth, 3 May".
fieldsobjectbodyCustom property values by key (see list_properties), checked against each property's type and rules. Required properties need a value.
tagsarraybodyTag names; new ones are created.
list_idsarraybodyLists to add the contact to.
curl -X POST https://zeebra.ai/api/v1/contacts \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"sara@example.com","first_name":"Sara","lead_type":"opted_in","consent_note":"Signed up at our booth","fields":{"ticket_type":"VIP"}}'

Returns Contact in data.

Update a contact

PATCH/api/v1/contacts/{id}MCP tool update_contact

Changes a contact's name, company (account_id, or a company name) and other properties. Only what you send changes; in fields, a null value clears that custom property. The email address, lead type and status have their own rules and can't be changed here. A contact shared with the business group changes in every workspace of it; in a workspace with view access its shared fields (names, company, job title, phone, city) are refused.

ParameterTypeDescription
id*integerpathThe contact's id.
first_namestring or nullbody
last_namestring or nullbody
titlestring or nullbody
companystring or nullbodyTheir company's name: they join the account of that name (made if there is none); null takes them out of theirs.
account_idinteger or nullbodyTheir company: an account of the workspace (list_accounts), or null for none. Wins over company.
phonestring or nullbody
citystring or nullbody
fieldsobjectbodyCustom property values by key; null clears one.
curl -X PATCH https://zeebra.ai/api/v1/contacts/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fields":{"ticket_type":"VIP"}}'

Returns Contact in data.

Change a contact's status

POST/api/v1/contacts/{id}/statusMCP tool change_contact_status

Sets a contact's status. Unsubscribed, bounced and complained are final: the address goes on the do-not-email list and only the person can come back by signing up again.

ParameterTypeDescription
id*integerpathThe contact's id.
status*stringbodyactive · pending · dormant · unsubscribed · bounced · complained
notestringbodyWhy, kept in the contact's history.
curl -X POST https://zeebra.ai/api/v1/contacts/123/status \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"unsubscribed"}'

Returns Contact in data.

Tag contacts

POST/api/v1/contacts/tagMCP tool tag_contacts

Adds a tag to up to 1,000 contacts. The tag is created if it doesn't exist (names match without case).

ParameterTypeDescription
contact_ids*arraybody
tag*stringbody
curl -X POST https://zeebra.ai/api/v1/contacts/tag \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_ids":[12,13],"tag":"VIP"}'

Untag contacts

POST/api/v1/contacts/untagMCP tool untag_contacts

Takes a tag off up to 1,000 contacts. The tag itself stays (delete_tag removes it everywhere).

ParameterTypeDescription
contact_ids*arraybody
tag*stringbody
curl -X POST https://zeebra.ai/api/v1/contacts/untag \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_ids":[12,13],"tag":"VIP"}'

Delete a contact

Can’t be undone

DELETE/api/v1/contacts/{id}MCP tool delete_contact

Deletes a contact and its history. If they had unsubscribed, bounced or complained, the address stays on the do-not-email list.

ParameterTypeDescription
id*integerpathThe contact's id.
curl -X DELETE https://zeebra.ai/api/v1/contacts/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Import contacts from CSV

POST/api/v1/contacts/importsMCP tool import_contacts

Imports a CSV (with a header row) in the background, cleaning addresses like the import page: typos flagged, role, throwaway and undeliverable addresses skipped, suppressed ones never added. Columns are matched by their headers unless you give a mapping. Existing contacts are updated, and their status only gets stricter. Check progress with get_import.

ParameterTypeDescription
csv*stringbodyThe file's text, up to 10 MB.
mappingobjectbodyHeader => email, first_name, last_name, name, title, company, company_domain, company_industry, company_city, phone, city, country, status, unsubscribed, tags, field:<custom property key>, or null to skip. Left out: guessed from the headers.
lead_type*stringbodyopted_in · prospect
consent_confirmedbooleanbodyRequired true for opted_in: you confirm these people agreed to get your emails.
consent_notestringbodyHow they agreed (kept on each contact).
list_idintegerbodyAdd everyone imported to this list.
tag_idsarraybodyTag everyone imported.
keep_role_addressesbooleanbodyKeep info@, sales@… addresses (skipped by default).
file_namestringbodyA name for the import, shown on the import page.
curl -X POST https://zeebra.ai/api/v1/contacts/imports \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"csv":"email,first_name\nsara@example.com,Sara","lead_type":"prospect"}'

Returns Import in data.

Get an import

GET/api/v1/contacts/imports/{id}MCP tool get_import

Shows an import's progress and report: created, updated, skipped and flagged counts by reason.

ParameterTypeDescription
id*integerpathThe import's id.
curl "https://zeebra.ai/api/v1/contacts/imports/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Import in data.

Export contacts as CSV

GET/api/v1/contacts/exportMCP tool export_contacts

Returns the contacts matching the filters (as in list_contacts) as CSV text that import_contacts reads back, up to 50,000 rows. Emails and phones found through Zeebra are left out where their licence forbids export.

ParameterTypeDescription
filterobjectqueryThe same filters as list_contacts.
whereobjectqueryThe same rule tree as list_contacts.
curl "https://zeebra.ai/api/v1/contacts/export" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Accounts

List accounts

GET/api/v1/accountsMCP tool list_accounts

Lists the workspace's companies (accounts), by name, a page at a time, each with how many contacts it has. Filter on any filterable account property, custom ones included, like list_contacts (filter, or a rule tree in where), and sort on any sortable one. list_contacts with filter account_id lists an account's contacts.

ParameterTypeDescription
qstringquerySearch: every word in the name, domain or a searchable property.
filterobjectqueryProperty key => value, as in list_contacts: name, domain, industry, city, country, owner_id, employees, contacts_count, created_at, field:<key>… A value is "operator:value" (contains:tech, gte:10, is_set:) or just a value.
whereobjectqueryA rule tree like a segment's, for AND/OR, on account properties.
sortstringqueryUp to 3 sortable property keys, comma separated, - for descending: name (the default), -contacts_count, -created_at.
pageintegerqueryPage number, from 1.
per_pageintegerqueryItems per page, up to 100.
curl "https://zeebra.ai/api/v1/accounts?filter%5Bcity%5D=London&filter%5Bcontacts_count%5D=gt%3A0" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Account in data, a page at a time (meta has the totals).

Get an account

GET/api/v1/accounts/{id}MCP tool get_account

Gets one of the workspace's companies with its custom property values and how many contacts it has.

ParameterTypeDescription
id*integerpathThe account's id.
curl "https://zeebra.ai/api/v1/accounts/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Account in data.

Add an account

POST/api/v1/accountsMCP tool add_account

Adds a company to the workspace. Its domain is cleaned (https://www.acme.com/about becomes acme.com), must be the company's own (not gmail.com or a social profile) and one account's only; a website fills in a missing domain. Contacts added later with a work email at the domain join it.

ParameterTypeDescription
name*stringbody
domainstringbody
websitestringbody
industrystringbody
citystringbody
countrystringbodyA country code (SA) or name.
phonestringbodyWithout a "+", read as a number of the workspace's default country.
employeesintegerbody
descriptionstringbody
owner_idintegerbodyA team member's user id. Defaults to the API key's member, when it has one.
fieldsobjectbodyCustom property values by key (list_properties with entity account). Required properties need a value.
curl -X POST https://zeebra.ai/api/v1/accounts \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Northwind Events","domain":"riyadhevents.sa"}'

Returns Account in data.

Update an account

PATCH/api/v1/accounts/{id}MCP tool update_account

Changes a company's properties. Only what you send changes; null clears a property (not the name). Renaming it renames it on its contacts (their company).

ParameterTypeDescription
id*integerpathThe account's id.
namestringbody
domainstring or nullbody
websitestring or nullbody
industrystring or nullbody
citystring or nullbody
countrystring or nullbodyA country code (SA) or name.
phonestring or nullbodyWithout a "+", read as a number of the workspace's default country.
employeesinteger or nullbody
descriptionstring or nullbody
owner_idinteger or nullbodyA team member's user id.
fieldsobjectbodyCustom property values by key (list_properties with entity account); null clears one.
curl -X PATCH https://zeebra.ai/api/v1/accounts/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"riyadhevents.sa"}'

Returns Account in data.

Delete an account

Can’t be undone

DELETE/api/v1/accounts/{id}MCP tool delete_account

Deletes a company. Its contacts are kept, without a company link (their company name stays).

ParameterTypeDescription
id*integerpathThe account's id.
curl -X DELETE https://zeebra.ai/api/v1/accounts/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Duplicates

List duplicate suggestions

GET/api/v1/duplicatesMCP tool list_duplicates

Lists pairs of contacts or accounts that look like the same one, newest first, with both records' values and why they matched (email, phone, domain, name, rule:<id>, manual). Pairs come from scans: scan_duplicates runs one, and a workspace that scanned is scanned again daily.

ParameterTypeDescription
typestringquerycontact or account.contact · account
statusstringquerypending · rejected · merged
pageintegerqueryPage number, from 1.
per_pageintegerqueryItems per page, up to 100.
curl "https://zeebra.ai/api/v1/duplicates" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Scan for duplicates

POST/api/v1/duplicates/scanMCP tool scan_duplicates

Starts a scan of the workspace's contacts or accounts for duplicates in the background; list_duplicates shows what it finds.

ParameterTypeDescription
typestringbodycontact or account.contact · account
curl -X POST https://zeebra.ai/api/v1/duplicates/scan \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Reject a duplicate suggestion

POST/api/v1/duplicates/{id}/rejectMCP tool reject_duplicate

Marks a suggested pair as not duplicates: it leaves the list and scans don't suggest it again.

ParameterTypeDescription
id*integerpathThe suggestion's id (list_duplicates).
curl -X POST https://zeebra.ai/api/v1/duplicates/123/reject \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Merge two contacts

Can’t be undone

POST/api/v1/contacts/{id}/mergeMCP tool merge_contacts

Merges a duplicate contact (id) into another (into_id, the one kept) and deletes the duplicate. The kept contact keeps its values, and takes the duplicate's where it has none or where you name the property in take. Lists, tags, topics, campaign emails and history move to it. The strictest status wins (unsubscribed, bounced or complained is never undone), and an opted-in contact's consent is kept. unmerge_duplicates undoes it.

ParameterTypeDescription
id*integerpathThe duplicate: merged into the other, then deleted.
into_id*integerbodyThe record kept.
takearraybodyProperty keys whose value comes from the duplicate even where the kept record has one ("phone", "field:plan").
curl -X POST https://zeebra.ai/api/v1/contacts/123/merge \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Contact in data.

Merge two accounts

Can’t be undone

POST/api/v1/accounts/{id}/mergeMCP tool merge_accounts

Merges a duplicate company (id) into another (into_id, the one kept) and deletes the duplicate. The kept account keeps its values, and takes the duplicate's where it has none or where you name the property in take. Its contacts move to the kept account, and segments filtering on the duplicate's id use the kept one. Shared with a business group: the kept account keeps its shared link, or takes the duplicate's. unmerge_duplicates undoes it.

ParameterTypeDescription
id*integerpathThe duplicate: merged into the other, then deleted.
into_id*integerbodyThe record kept.
takearraybodyProperty keys whose value comes from the duplicate even where the kept record has one ("phone", "field:plan").
curl -X POST https://zeebra.ai/api/v1/accounts/123/merge \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Account in data.

Undo a merge

POST/api/v1/duplicates/merges/{id}/unmergeMCP tool unmerge_duplicates

Undoes a merge (its merge_id from merge_contacts or merge_accounts): the merged record comes back and everything that moved goes back. Refused, with the reason, when something the merge changed was changed again since.

ParameterTypeDescription
id*integerpathThe merge's merge_id.
curl -X POST https://zeebra.ai/api/v1/duplicates/merges/123/unmerge \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Business groups

Get the business group

GET/api/v1/business-groupMCP tool get_business_group

Gets the business group the workspace is in (business_group, null when it isn't in one): its name, this workspace's access (edit: changes to shared records reach every workspace; view: their common fields can't be changed here), whether new contacts and companies are shared at once, the names of its workspaces, and how many contacts and companies it shares.

curl "https://zeebra.ai/api/v1/business-group" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Share a contact

POST/api/v1/contacts/{id}/shareMCP tool share_contact

Shares a contact (and its company) with every workspace of the business group. A workspace that has a contact with the same email gets it linked, not copied; the others get a copy. The copies keep one profile (name, email, phone, job title, company, city, country) and one consent: opted in anywhere is opted in everywhere, and unsubscribing, bouncing or complaining anywhere applies everywhere. Tags, lists, notes and custom properties stay each workspace's own.

ParameterTypeDescription
id*integerpathThe contact's id.
curl -X POST https://zeebra.ai/api/v1/contacts/123/share \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Contact in data.

Stop sharing a contact

DELETE/api/v1/contacts/{id}/shareMCP tool unshare_contact

Stops sharing this workspace's copy of a contact: it stays here as a plain contact, and the other workspaces keep theirs. Nothing is deleted.

ParameterTypeDescription
id*integerpathThe contact's id.
curl -X DELETE https://zeebra.ai/api/v1/contacts/123/share \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Contact in data.

Share an account

POST/api/v1/accounts/{id}/shareMCP tool share_account

Shares a company (account) with every workspace of the business group. A workspace with a company of the same domain (else the same name) gets it linked; the others get a copy. The copies keep one name, domain, website, industry, size, phone and location; owner, notes and custom properties stay each workspace's own.

ParameterTypeDescription
id*integerpathThe account's id.
curl -X POST https://zeebra.ai/api/v1/accounts/123/share \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Account in data.

Stop sharing an account

DELETE/api/v1/accounts/{id}/shareMCP tool unshare_account

Stops sharing this workspace's copy of a company: it stays here as a plain company. Nothing is deleted.

ParameterTypeDescription
id*integerpathThe account's id.
curl -X DELETE https://zeebra.ai/api/v1/accounts/123/share \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Account in data.

Tags

List tags

GET/api/v1/tagsMCP tool list_tags

Lists the workspace's tags with how many contacts have each.

curl "https://zeebra.ai/api/v1/tags" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Tag in data.

Create a tag

POST/api/v1/tagsMCP tool create_tag

Creates a tag. tag_contacts also creates tags as it goes.

ParameterTypeDescription
name*stringbody
colorstringbodyDefaults to one picked from the name.zinc · red · amber · green · blue · violet · pink
curl -X POST https://zeebra.ai/api/v1/tags \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"VIP"}'

Returns Tag in data.

Rename or recolour a tag

PATCH/api/v1/tags/{id}MCP tool update_tag

Changes a tag's name or colour.

ParameterTypeDescription
id*integerpathThe tag's id.
namestringbody
colorstringbodyzinc · red · amber · green · blue · violet · pink
curl -X PATCH https://zeebra.ai/api/v1/tags/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Tag in data.

Delete a tag

Can’t be undone

DELETE/api/v1/tags/{id}MCP tool delete_tag

Deletes a tag and takes it off every contact. The contacts stay.

ParameterTypeDescription
id*integerpathThe tag's id.
curl -X DELETE https://zeebra.ai/api/v1/tags/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Properties

List properties

GET/api/v1/propertiesMCP tool list_properties

Lists everything a contact (or an account, one of your companies) has, in order: system properties (email, first_name, status, tags…) and the workspace's custom ones, keyed "field:<key>". A custom property's <key> is what contacts' fields use, and {{custom.<key>}} puts its value in an email. Each says its type, rules, options and segment operators.

ParameterTypeDescription
entitystringquerycontact (the default) or account: your companies (the Companies page; see list_accounts).contact · account
include_archivedbooleanqueryAlso list archived custom properties.
curl "https://zeebra.ai/api/v1/properties" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Property in data.

Create a property

POST/api/v1/propertiesMCP tool create_property

Adds a custom property. Its key is made from the label ("Ticket type" becomes ticket_type) unless you give one, and never changes. Select and multi_select properties need their options.

ParameterTypeDescription
entitystringbodycontact (the default) or account: your companies (the Companies page; see list_accounts).contact · account
label*stringbody
type*stringbodytext · long_text · number · currency · date · select · multi_select · boolean · email · phone · url · country · user
keystringbodyLowercase letters, digits and underscores. Defaults to one made from the label.
descriptionstringbody
optionsarraybodyChoices of a select or multi_select property: {label, color?}, and the value when changing an existing option (values never change, labels can).
settingsobjectbodyValidation by type. text: min_length, max_length, regex. long_text: min_length, max_length. number: min, max, decimals. currency: currency (like EUR; default the billing currency), min, max, decimals. date: date_rule (any, past, future, range), date_from, date_to. multi_select: min_selections, max_selections.
groupstring or nullbodyA group key from list_properties (contact_info, work, other…). Left out on create: "other".
requiredbooleanbodyContacts can't be added or saved without it (imports and signups skip it).
searchablebooleanbodyText types only: the contacts search matches it.
curl -X POST https://zeebra.ai/api/v1/properties \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"Ticket type","type":"select","options":[{"label":"VIP","color":"amber"},{"label":"Standard"}],"settings":{"max_length":40}}'

Returns Property in data.

Update a property

PATCH/api/v1/properties/{id}MCP tool update_property

Changes a custom property's label, description, options, rules, group or flags. Its key and type stay, so stored values, segments and merge tags keep working. System properties: use update_system_property.

ParameterTypeDescription
id*integerpathThe custom property's id (from list_properties).
labelstringbody
descriptionstring or nullbody
optionsarraybodyChoices of a select or multi_select property: {label, color?}, and the value when changing an existing option (values never change, labels can).
settingsobjectbodyValidation by type. text: min_length, max_length, regex. long_text: min_length, max_length. number: min, max, decimals. currency: currency (like EUR; default the billing currency), min, max, decimals. date: date_rule (any, past, future, range), date_from, date_to. multi_select: min_selections, max_selections.
groupstring or nullbodyA group key from list_properties (contact_info, work, other…). Left out on create: "other".
requiredbooleanbody
searchablebooleanbody
curl -X PATCH https://zeebra.ai/api/v1/properties/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"options":[{"label":"VIP","color":"amber"},{"label":"Standard"}],"settings":{"max_length":40}}'

Returns Property in data.

Archive a property

POST/api/v1/properties/{id}/archiveMCP tool archive_property

Hides a custom property everywhere. Its values stay on records and come back with restore_property; segments using it keep working.

ParameterTypeDescription
id*integerpathThe custom property's id (from list_properties).
curl -X POST https://zeebra.ai/api/v1/properties/123/archive \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Property in data.

Restore a property

POST/api/v1/properties/{id}/restoreMCP tool restore_property

Brings an archived custom property back, with its values.

ParameterTypeDescription
id*integerpathThe custom property's id (from list_properties).
curl -X POST https://zeebra.ai/api/v1/properties/123/restore \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Property in data.

Change a system property

PATCH/api/v1/properties/system/{key}MCP tool update_system_property

Changes how a system property (first_name, company, city…) shows in this workspace: its label (null: the default), description, group, whether it is required and whether record pages hide it. Never its type.

ParameterTypeDescription
key*stringpathThe system property's key, like company.
entitystringbodycontact (the default) or account: your companies (the Companies page; see list_accounts).contact · account
labelstring or nullbody
descriptionstring or nullbody
groupstring or nullbodyA group key from list_properties (contact_info, work, other…). Left out on create: "other".
requiredbooleanbody
hiddenbooleanbody
curl -X PATCH https://zeebra.ai/api/v1/properties/system/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Property in data.

Topics

List topics

GET/api/v1/topicsMCP tool list_topics

Lists the kinds of email people can unsubscribe from on their own (newsletter, event invites…).

curl "https://zeebra.ai/api/v1/topics" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Topic in data.

Create a topic

POST/api/v1/topicsMCP tool create_topic

Adds a topic. Contacts are in it unless they leave, or only once they join when default_subscribed is false.

ParameterTypeDescription
name*stringbody
descriptionstringbody
default_subscribedbooleanbody
curl -X POST https://zeebra.ai/api/v1/topics \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Event invites"}'

Returns Topic in data.

Update a topic

PATCH/api/v1/topics/{id}MCP tool update_topic

Changes a topic's name, description or default.

ParameterTypeDescription
id*integerpathThe topic's id.
namestringbody
descriptionstring or nullbody
default_subscribedbooleanbody
curl -X PATCH https://zeebra.ai/api/v1/topics/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Topic in data.

Delete a topic

Can’t be undone

DELETE/api/v1/topics/{id}MCP tool delete_topic

Deletes a topic and people's choices for it. Templates and campaigns that used it go to everyone again.

ParameterTypeDescription
id*integerpathThe topic's id.
curl -X DELETE https://zeebra.ai/api/v1/topics/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Suppressions

List suppressions

GET/api/v1/suppressionsMCP tool list_suppressions

Lists the workspace's do-not-email list, newest first: unsubscribes, bounces, complaints and addresses added by hand.

ParameterTypeDescription
qstringqueryPart of an address.
reasonstringquerybounce · complaint · unsubscribe · manual
pageintegerqueryPage number, from 1.
per_pageintegerqueryItems per page, up to 100.
curl "https://zeebra.ai/api/v1/suppressions" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Suppression in data, a page at a time (meta has the totals).

Add to the do-not-email list

POST/api/v1/suppressionsMCP tool add_suppressions

Adds up to 1,000 addresses to the do-not-email list. Contacts with these addresses are unsubscribed. Invalid addresses are skipped.

ParameterTypeDescription
emails*arraybody
notestringbody
curl -X POST https://zeebra.ai/api/v1/suppressions \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"emails":["someone@example.com"]}'

Remove from the do-not-email list

Can’t be undone

DELETE/api/v1/suppressions/{id}MCP tool remove_suppression

Removes an address added by hand. Unsubscribes, bounces and complaints can't be removed: the person has to sign up again.

ParameterTypeDescription
id*integerpathThe suppression's id.
curl -X DELETE https://zeebra.ai/api/v1/suppressions/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Lists

List lists

GET/api/v1/listsMCP tool list_lists

Lists the workspace's contact lists with how many contacts each has. list_contacts with filter list_id shows who is on one.

curl "https://zeebra.ai/api/v1/lists" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of List in data.

Get a list

GET/api/v1/lists/{id}MCP tool get_list

Gets one list and its contact count.

ParameterTypeDescription
id*integerpathThe list's id.
curl "https://zeebra.ai/api/v1/lists/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns List in data.

Create a list

POST/api/v1/listsMCP tool create_list

Creates an empty list. Add contacts with add_to_list.

ParameterTypeDescription
name*stringbody
curl -X POST https://zeebra.ai/api/v1/lists \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Berlin expo 2026"}'

Returns List in data.

Rename a list

PATCH/api/v1/lists/{id}MCP tool update_list

Renames a list.

ParameterTypeDescription
id*integerpathThe list's id.
name*stringbody
curl -X PATCH https://zeebra.ai/api/v1/lists/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns List in data.

Delete a list

Can’t be undone

DELETE/api/v1/lists/{id}MCP tool delete_list

Deletes a list. Its contacts are kept.

ParameterTypeDescription
id*integerpathThe list's id.
curl -X DELETE https://zeebra.ai/api/v1/lists/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Add contacts to a list

POST/api/v1/lists/{id}/contactsMCP tool add_to_list

Adds up to 1,000 of the workspace's contacts to a list. Contacts already on it are left as they are.

ParameterTypeDescription
id*integerpathThe list's id.
contact_ids*arraybody
curl -X POST https://zeebra.ai/api/v1/lists/123/contacts \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_ids":[12,13]}'

Returns List in data.

Remove contacts from a list

DELETE/api/v1/lists/{id}/contactsMCP tool remove_from_list

Takes up to 1,000 contacts off a list. The contacts are kept.

ParameterTypeDescription
id*integerpathThe list's id.
contact_ids*arraybody
curl -X DELETE https://zeebra.ai/api/v1/lists/123/contacts \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contact_ids":[12,13]}'

Returns List in data.

Segments

List segments

GET/api/v1/segmentsMCP tool list_segments

Lists the workspace's segments with their last counted size (count_segment counts again).

curl "https://zeebra.ai/api/v1/segments" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Segment in data.

Get segment fields

GET/api/v1/segments/fieldsMCP tool get_segment_fields

Lists what segment rules can filter on (every filterable contact property, custom ones as field:<key>) and the operators of each kind of field. Read this before writing rules.

curl "https://zeebra.ai/api/v1/segments/fields" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Get a segment

GET/api/v1/segments/{id}MCP tool get_segment

Gets a segment and its rules.

ParameterTypeDescription
id*integerpathThe segment's id.
curl "https://zeebra.ai/api/v1/segments/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Segment in data.

Create a segment

POST/api/v1/segmentsMCP tool create_segment

Saves a segment. Rules that can't be worked out are refused, never dropped (a dropped rule would widen who gets emailed).

ParameterTypeDescription
name*stringbody
rules*objectbodyA rule tree: {"match": "all"|"any", "rules": [{"field": "lead_type", "op": "in", "value": ["opted_in"]}, {"match": "any", "rules": [...]}]}. Groups nest up to 3 deep. Dates also take {"op": "in_period", "value": "last_month"} and between ["2026-01-01", "2026-03-31"]; numbers between [1, 5]. get_segment_fields lists the fields, operators and periods.
curl -X POST https://zeebra.ai/api/v1/segments \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Opted in, last 30 days","rules":{"match":"all","rules":[{"field":"lead_type","op":"in","value":["opted_in"]},{"field":"created_at","op":"within_last","value":30}]}}'

Returns Segment in data.

Update a segment

PATCH/api/v1/segments/{id}MCP tool update_segment

Renames a segment or replaces its rules.

ParameterTypeDescription
id*integerpathThe segment's id.
namestringbody
rulesobjectbodyA rule tree: {"match": "all"|"any", "rules": [{"field": "lead_type", "op": "in", "value": ["opted_in"]}, {"match": "any", "rules": [...]}]}. Groups nest up to 3 deep. Dates also take {"op": "in_period", "value": "last_month"} and between ["2026-01-01", "2026-03-31"]; numbers between [1, 5]. get_segment_fields lists the fields, operators and periods.
curl -X PATCH https://zeebra.ai/api/v1/segments/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"rules":{"match":"all","rules":[{"field":"lead_type","op":"in","value":["opted_in"]},{"field":"created_at","op":"within_last","value":30}]}}'

Returns Segment in data.

Delete a segment

Can’t be undone

DELETE/api/v1/segments/{id}MCP tool delete_segment

Deletes a segment. Refused while another segment uses it. Its contacts are kept.

ParameterTypeDescription
id*integerpathThe segment's id.
curl -X DELETE https://zeebra.ai/api/v1/segments/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Count a segment

GET/api/v1/segments/{id}/countMCP tool count_segment

Counts the contacts in a segment now, and how many of them can be emailed (active, with a good address).

ParameterTypeDescription
id*integerpathThe segment's id.
curl "https://zeebra.ai/api/v1/segments/123/count" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Templates

List templates

GET/api/v1/templatesMCP tool list_templates

Lists the workspace's email templates, last changed first (without their content; get_template has it).

curl "https://zeebra.ai/api/v1/templates" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Template in data.

Get a template

GET/api/v1/templates/{id}MCP tool get_template

Gets a template with its content.

ParameterTypeDescription
id*integerpathThe template's id.
curl "https://zeebra.ai/api/v1/templates/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Template in data.

Create a template

POST/api/v1/templatesMCP tool create_template

Saves reusable email content. Write markdown (format markdown), full or partial HTML (format html), or an editor document (format editor). Merge tags: {{first_name|there}} (with a fallback), {{email}}, {{custom.<key>}}. The unsubscribe footer with your postal address is added for you.

ParameterTypeDescription
name*stringbody
format*stringbodymarkdown, html or editor.editor · html · markdown
subjectstringbody
preview_textstring or nullbodyShown next to the subject in the inbox.
markdownstring or nullbody
htmlstring or nullbody
documentobject or nullbodyA Tiptap editor document (format editor).
topic_idinteger or nullbodyThe topic it belongs to (list_topics); people who left the topic don't get it.
themestring or nullbodyA colour theme; null uses the workspace's.standard · letter · newsroom · oasis · sand · night · typewriter · poster
curl -X POST https://zeebra.ai/api/v1/templates \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Monthly newsletter","format":"markdown","subject":"What'\''s new in May","markdown":"Hi {{first_name|there}},\n\nHere is what'\''s new.\n\n[Read more](https://example.com \"button\")"}'

Returns Template in data.

Update a template

PATCH/api/v1/templates/{id}MCP tool update_template

Changes a template. Only the fields you send change. Campaigns made from it keep their own copy. Merge tags: {{first_name|there}} (with a fallback), {{email}}, {{custom.<key>}}. The unsubscribe footer with your postal address is added for you.

ParameterTypeDescription
id*integerpathThe template's id.
namestringbody
formatstringbodymarkdown, html or editor.editor · html · markdown
subjectstringbody
preview_textstring or nullbodyShown next to the subject in the inbox.
markdownstring or nullbody
htmlstring or nullbody
documentobject or nullbodyA Tiptap editor document (format editor).
topic_idinteger or nullbodyThe topic it belongs to (list_topics); people who left the topic don't get it.
themestring or nullbodyA colour theme; null uses the workspace's.standard · letter · newsroom · oasis · sand · night · typewriter · poster
curl -X PATCH https://zeebra.ai/api/v1/templates/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format":"markdown","subject":"What'\''s new in May","markdown":"Hi {{first_name|there}},\n\nHere is what'\''s new.\n\n[Read more](https://example.com \"button\")"}'

Returns Template in data.

Delete a template

Can’t be undone

DELETE/api/v1/templates/{id}MCP tool delete_template

Deletes a template. Campaigns made from it keep their content.

ParameterTypeDescription
id*integerpathThe template's id.
curl -X DELETE https://zeebra.ai/api/v1/templates/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Preview a template

POST/api/v1/templates/{id}/previewMCP tool preview_template

Renders a template as it will be sent: subject, preheader, HTML, plain text, size and content warnings, filled in for one of your contacts or with sample values.

ParameterTypeDescription
id*integerpathThe template's id.
contact_idintegerbodyFill merge tags for this contact.
curl -X POST https://zeebra.ai/api/v1/templates/123/preview \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Send a test of a template

POST/api/v1/templates/{id}/testMCP tool send_test_template

Emails a test of the template to a member of the workspace (never to contacts), from one of its senders.

ParameterTypeDescription
id*integerpathThe template's id.
to*stringbodyA workspace member's address.
sender_idintegerbodyWho it is from (list_senders); the default sender when left out.
curl -X POST https://zeebra.ai/api/v1/templates/123/test \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"you@company.com"}'

Campaigns

List campaigns

GET/api/v1/campaignsMCP tool list_campaigns

Lists campaigns, newest first, with their status and counts (without content).

ParameterTypeDescription
statusstringquerydraft · scheduled · sending · paused · sent · canceled
pageintegerqueryPage number, from 1.
per_pageintegerqueryItems per page, up to 100.
curl "https://zeebra.ai/api/v1/campaigns" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Campaign in data, a page at a time (meta has the totals).

Get a campaign

GET/api/v1/campaigns/{id}MCP tool get_campaign

Gets a campaign with its content, audience and counts.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl "https://zeebra.ai/api/v1/campaigns/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Campaign in data.

Create a campaign

POST/api/v1/campaignsMCP tool create_campaign

Creates a draft from a template (template_id) or with new content, from the default sender. Choose the audience with lists and segments; exclusions are taken out. Only opted-in contacts who can be emailed get it. Merge tags: {{first_name|there}} (with a fallback), {{email}}, {{custom.<key>}}. The unsubscribe footer with your postal address is added for you.

ParameterTypeDescription
name*stringbody
template_idintegerbodyCopy this template's content.
formatstringbodymarkdown, html or editor.editor · html · markdown
subjectstringbody
preview_textstring or nullbodyShown next to the subject in the inbox.
markdownstring or nullbody
htmlstring or nullbody
documentobject or nullbodyA Tiptap editor document (format editor).
topic_idinteger or nullbodyThe topic it belongs to (list_topics); people who left the topic don't get it.
themestring or nullbodyA colour theme; null uses the workspace's.standard · letter · newsroom · oasis · sand · night · typewriter · poster
sender_idintegerbodyWho it is from (list_senders).
audienceobjectbodyWho gets it: {"lists": [ids], "segments": [ids]}.
exclusionsobjectbodyWho never gets it, even if in the audience: {"lists": [ids], "segments": [ids]}.
audience_limitinteger or nullbodyOnly the N most engaged recipients; send_campaign_to_more reaches the rest later.
publish_tostringbodyWhere it goes: email (the default), both (email, and the workspace's public pages once it goes out) or web (the public pages only: no email goes out, and send_campaign takes confirm_recipients 0).email · both · web
curl -X POST https://zeebra.ai/api/v1/campaigns \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"May newsletter","format":"markdown","subject":"What'\''s new in May","markdown":"Hi {{first_name|there}},\n\nHere is what'\''s new.\n\n[Read more](https://example.com \"button\")","audience":{"lists":[3],"segments":[]},"publish_to":"both"}'

Returns Campaign in data.

Update a campaign

PATCH/api/v1/campaigns/{id}MCP tool update_campaign

Changes a draft or scheduled campaign that hasn't started: content, sender, topic, audience. Only the fields you send change. Merge tags: {{first_name|there}} (with a fallback), {{email}}, {{custom.<key>}}. The unsubscribe footer with your postal address is added for you.

ParameterTypeDescription
id*integerpathThe campaign's id.
namestringbody
formatstringbodymarkdown, html or editor.editor · html · markdown
subjectstringbody
preview_textstring or nullbodyShown next to the subject in the inbox.
markdownstring or nullbody
htmlstring or nullbody
documentobject or nullbodyA Tiptap editor document (format editor).
topic_idinteger or nullbodyThe topic it belongs to (list_topics); people who left the topic don't get it.
themestring or nullbodyA colour theme; null uses the workspace's.standard · letter · newsroom · oasis · sand · night · typewriter · poster
sender_idintegerbodyWho it is from (list_senders).
audienceobjectbodyWho gets it: {"lists": [ids], "segments": [ids]}.
exclusionsobjectbodyWho never gets it, even if in the audience: {"lists": [ids], "segments": [ids]}.
audience_limitinteger or nullbodyOnly the N most engaged recipients; send_campaign_to_more reaches the rest later.
publish_tostringbodyWhere it goes: email (the default), both (email, and the workspace's public pages once it goes out) or web (the public pages only: no email goes out, and send_campaign takes confirm_recipients 0).email · both · web
curl -X PATCH https://zeebra.ai/api/v1/campaigns/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format":"markdown","subject":"What'\''s new in May","markdown":"Hi {{first_name|there}},\n\nHere is what'\''s new.\n\n[Read more](https://example.com \"button\")","audience":{"lists":[3],"segments":[]},"publish_to":"both"}'

Returns Campaign in data.

Delete a campaign

Can’t be undone

DELETE/api/v1/campaigns/{id}MCP tool delete_campaign

Deletes a draft. A campaign that was scheduled or sent can be canceled instead.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl -X DELETE https://zeebra.ai/api/v1/campaigns/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Duplicate a campaign

POST/api/v1/campaigns/{id}/duplicateMCP tool duplicate_campaign

Makes a new draft with the same content, sender, topic and audience.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/duplicate \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Campaign in data.

Send a test of a campaign

POST/api/v1/campaigns/{id}/testMCP tool send_test_campaign

Emails a test to a member of the workspace (never to contacts), so the user can check it before sending.

ParameterTypeDescription
id*integerpathThe campaign's id.
to*stringbodyA workspace member's address.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/test \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"you@company.com"}'

Review a campaign

GET/api/v1/campaigns/{id}/reviewMCP tool review_campaign

Runs the checks before sending. blocks must be fixed first; warnings can be accepted. checks lists every check: id, level (block or warning), passed, message (what to fix) and label (a short line for the ones that passed, or null). A web-only campaign (web_only true) only gets the content checks, and its audience is 0. audience.recipients is the count to show the user and pass as confirm_recipients, with who is skipped and why. pace says how it would go out if sent now: spread_hours (0 = straight away), the domain warm-up cap when it holds part of it to later days, and first_slice, the never-emailed contacts that go first; gmail_paused says so while Gmail rate limits the sending domain (what is sent waits until gmail_paused_until).

ParameterTypeDescription
id*integerpathThe campaign's id.
curl "https://zeebra.ai/api/v1/campaigns/123/review" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Get tips for more opens

GET/api/v1/campaigns/{id}/tipsMCP tool get_campaign_tips

Tips on the saved content that tend to land email in spam or cost opens: a subject in capitals, runs of !!! or ???, classic spam phrases, a fake Re: or Fwd:, link shorteners, images with little text, too many links, no preview text, and up to three more from the AI content check when it runs. They never stop a send. tips: at most 3, most important first, each with code (the rule, or ai), field (subject, preview_text or content), quote (the exact words it is about) and tip (what to change). Empty when nothing stands out. ai: whether the AI check read this content.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl "https://zeebra.ai/api/v1/campaigns/123/tips" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Send a campaign now

Can’t be undone

POST/api/v1/campaigns/{id}/sendMCP tool send_campaign

Starts sending to the reviewed audience. Can't be undone: show the user the subject and the recipient count from review_campaign, and get their go-ahead first. A web-only campaign goes on the public pages instead (confirm_recipients 0).

ParameterTypeDescription
id*integerpathThe campaign's id.
confirm_recipients*integerbodyThe recipient count from review_campaign, after the user confirmed it. Refused if the audience changed since. 0 for a web-only campaign (publish_to web): nobody gets an email.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/send \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm_recipients":1250}'

Returns Campaign in data.

Schedule a campaign

Can’t be undone

POST/api/v1/campaigns/{id}/scheduleMCP tool schedule_campaign

Schedules sending for later (up to a year ahead). A time without an offset is in the workspace's timezone. The audience is worked out when sending starts. Confirm with the user first, like send_campaign.

ParameterTypeDescription
id*integerpathThe campaign's id.
confirm_recipients*integerbodyThe recipient count from review_campaign, after the user confirmed it. Refused if the audience changed since. 0 for a web-only campaign (publish_to web): nobody gets an email.
scheduled_at*stringbody
curl -X POST https://zeebra.ai/api/v1/campaigns/123/schedule \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm_recipients":1250,"scheduled_at":"2026-11-02T09:00:00+03:00"}'

Returns Campaign in data.

Unschedule a campaign

POST/api/v1/campaigns/{id}/unscheduleMCP tool unschedule_campaign

Takes a scheduled campaign that hasn't started back to a draft. Nothing was sent.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/unschedule \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Campaign in data.

Send a campaign to more people

Can’t be undone

POST/api/v1/campaigns/{id}/send-moreMCP tool send_campaign_to_more

Raises the audience limit of a sent or sending campaign (null: everyone), and the next people in the same order get it. Can't be undone: confirm with the user first.

ParameterTypeDescription
id*integerpathThe campaign's id.
audience_limitinteger or nullbodyThe new total; null sends to everyone in the audience.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/send-more \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Campaign in data.

Pause a campaign

POST/api/v1/campaigns/{id}/pauseMCP tool pause_campaign

Pauses a scheduled or sending campaign. Emails already handed to Amazon SES still go; the rest wait.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/pause \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Campaign in data.

Resume a campaign

Can’t be undone

POST/api/v1/campaigns/{id}/resumeMCP tool resume_campaign

Resumes a paused campaign, so sending carries on. If it paused itself for high bounce or complaint rates, resuming accepts those rates. Confirm with the user first.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/resume \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Campaign in data.

Cancel a campaign

Can’t be undone

POST/api/v1/campaigns/{id}/cancelMCP tool cancel_campaign

Stops a campaign for good: nobody else gets it. A scheduled campaign that hasn't started goes back to a draft.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl -X POST https://zeebra.ai/api/v1/campaigns/123/cancel \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Campaign in data.

Get a campaign's report

GET/api/v1/campaigns/{id}/reportMCP tool get_campaign_report

The results of a campaign: counts (sent, delivered, opens, clicks, bounces, complaints, unsubscribes), rates, the most clicked links, results by inbox provider, opens hour by hour, and how it compares with recent campaigns. Machine opens (Apple Mail Privacy Protection and the like) are counted apart.

ParameterTypeDescription
id*integerpathThe campaign's id.
curl "https://zeebra.ai/api/v1/campaigns/123/report" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Sending

List sending domains

GET/api/v1/domainsMCP tool list_domains

Lists the domains the workspace sends from, whether each is verified, and the DNS records to add (DKIM, MAIL FROM, DMARC) with which are in place.

curl "https://zeebra.ai/api/v1/domains" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Domain in data.

Add a sending domain

POST/api/v1/domainsMCP tool add_domain

Adds a domain to send from and returns the DNS records to add at its DNS provider. Use a subdomain such as news.example.com, so campaigns never affect the main domain's email. Then check_domain once the records are in.

ParameterTypeDescription
domain*stringbody
curl -X POST https://zeebra.ai/api/v1/domains \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"news.example.com"}'

Returns Domain in data.

Check a sending domain

POST/api/v1/domains/{id}/checkMCP tool check_domain

Asks Amazon SES and DNS whether the domain's records are in place, and returns its status. DNS changes can take up to an hour to show.

ParameterTypeDescription
id*integerpathThe domain's id.
curl -X POST https://zeebra.ai/api/v1/domains/123/check \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns Domain in data.

List senders

GET/api/v1/sendersMCP tool list_senders

Lists who the workspace's emails can be from (name, address, reply-to), the default first. Senders on an unverified domain can't send yet.

curl "https://zeebra.ai/api/v1/senders" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of Sender in data.

List test emails

GET/api/v1/test-emailsMCP tool list_test_emails

The newest test emails, from any member or key, with what became of each from Amazon SES's events: sent, delivered, delayed, bounced (detail says why; suppressed when the address is on SES's suppression list), complained, rejected or failed. Check it after a send_test_* to see whether the test arrived. Give campaign_id or template_id for that one's tests.

ParameterTypeDescription
campaign_idintegerqueryOnly tests of this campaign.
template_idintegerqueryOnly tests of this template.
limitintegerqueryHow many, newest first.
curl "https://zeebra.ai/api/v1/test-emails" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of TestEmail in data.

Signups

List signup forms

GET/api/v1/signup-formsMCP tool list_signup_forms

Lists the workspace's signup forms with their public id (form_id in add_signup), signup counts, and whether our content check holds their own confirmation or welcome email ("in_review", or "rejected" with our team's note): meanwhile new signups get the built-in confirmation email and no welcome email.

curl "https://zeebra.ai/api/v1/signup-forms" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of SignupForm in data.

Add a signup

Signups keys too

POST/api/v1/signupsMCP tool add_signup

Signs someone up to the workspace's emails, like a signup form. With confirmed opt-in (the default, and always for someone who unsubscribed before) they get a confirmation email and stay pending until they click it. People already subscribed are left as they are. Works with a signups key.

ParameterTypeDescription
email*stringbody
first_namestringbody
form_idstringbodyA signup form's public id: its list, topics, confirmation and welcome emails apply.
topicsarraybodyWithout a form: topic ids or keys to join.
double_opt_inbooleanbodyWithout a form: false skips the confirmation email (only if you confirmed them yourself).
page_urlstringbodyWhere they signed up, kept with their consent.
keep_emailbooleanbodySign up an address that looks like a typo anyway.
fieldsobjectbodyCustom contact property values by key (see list_properties), checked like add_contact. Required properties aren't required here.
curl -X POST https://zeebra.ai/api/v1/signups \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"sara@example.com","first_name":"Sara","fields":{"ticket_type":"VIP"}}'

Public pages

Set the public pages' domain

POST/api/v1/website-domainMCP tool set_website_domain

Puts the workspace's public pages (its published campaigns) on a subdomain of its own, like news.example.com. The answer lists the two DNS records to add: a CNAME that points the subdomain at Zeebra, and a TXT record that proves it's yours. It goes live once check_website_domain finds both, and right away when they're already in. One domain at a time: remove a live one first. Tracked and unsubscribe links stay on Zeebra's own domain.

ParameterTypeDescription
host*stringbodyA subdomain of a domain you manage. A link works too.
curl -X POST https://zeebra.ai/api/v1/website-domain \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"host":"news.example.com"}'

Returns WebsiteDomain in data.

Check the public pages' domain

POST/api/v1/website-domain/checkMCP tool check_website_domain

Looks up the DNS records of the public pages' domain again. One being set up goes live once both are in; for a live one, says whether its CNAME still points at Zeebra. DNS changes can take up to an hour to show.

curl -X POST https://zeebra.ai/api/v1/website-domain/check \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns WebsiteDomain in data.

Remove the public pages' domain

Can’t be undone

DELETE/api/v1/website-domainMCP tool remove_website_domain

Takes the public pages off their own domain: they go back to their address on Zeebra, and links to the domain redirect there while it still points at Zeebra.

curl -X DELETE https://zeebra.ai/api/v1/website-domain \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Webhooks

List webhook event types

GET/api/v1/webhooks/eventsMCP tool list_webhook_event_types

Lists the events an endpoint can subscribe to, with what each means. Every event is sent as {id, type, created_at, workspace_id, data}, signed in the Zeebra-Signature header.

curl "https://zeebra.ai/api/v1/webhooks/events" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of WebhookEventType in data.

List webhook endpoints

GET/api/v1/webhooksMCP tool list_webhooks

Lists the workspace's webhook endpoints, the events each gets, and whether it is on.

curl "https://zeebra.ai/api/v1/webhooks" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of WebhookEndpoint in data.

Get a webhook endpoint

GET/api/v1/webhooks/{id}MCP tool get_webhook

Gets one webhook endpoint (never its secret).

ParameterTypeDescription
id*integerpathThe webhook endpoint's id.
curl "https://zeebra.ai/api/v1/webhooks/123" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns WebhookEndpoint in data.

Add a webhook endpoint

POST/api/v1/webhooksMCP tool create_webhook

Adds an HTTPS endpoint that gets the chosen events. The answer has its signing secret (secret): store it now, it is never shown again. Verify each delivery's Zeebra-Signature header with it. Up to 10 endpoints per workspace.

ParameterTypeDescription
url*stringbodyAn https:// URL on a public host.
events*arraybodyEvent types (list_webhook_event_types), or ["*"] for every event, including ones added later.
descriptionstring or nullbodyWhat it is for, e.g. "atndr CRM sync".
curl -X POST https://zeebra.ai/api/v1/webhooks \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/webhooks/zeebra","events":["contact.unsubscribed","email.bounced"]}'

Returns WebhookEndpoint in data.

Change a webhook endpoint

PATCH/api/v1/webhooks/{id}MCP tool update_webhook

Changes an endpoint's URL, events or description, or switches it off or back on (enabled). Switching it on clears its failures. Only the fields sent change.

ParameterTypeDescription
id*integerpathThe webhook endpoint's id.
urlstringbody
eventsarraybodyEvent types (list_webhook_event_types), or ["*"] for every event, including ones added later.
descriptionstring or nullbodyWhat it is for, e.g. "atndr CRM sync".
enabledbooleanbody
curl -X PATCH https://zeebra.ai/api/v1/webhooks/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events":["contact.unsubscribed","email.bounced"]}'

Returns WebhookEndpoint in data.

Delete a webhook endpoint

Can’t be undone

DELETE/api/v1/webhooks/{id}MCP tool delete_webhook

Deletes an endpoint and its delivery log. It gets nothing more.

ParameterTypeDescription
id*integerpathThe webhook endpoint's id.
curl -X DELETE https://zeebra.ai/api/v1/webhooks/123 \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Rotate a webhook secret

POST/api/v1/webhooks/{id}/secretMCP tool rotate_webhook_secret

Gives the endpoint a new signing secret (in secret, shown once). For 24 hours deliveries carry a signature with the old secret too, so the receiver can switch without missing events.

ParameterTypeDescription
id*integerpathThe webhook endpoint's id.
curl -X POST https://zeebra.ai/api/v1/webhooks/123/secret \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns WebhookEndpoint in data.

Send a test event

POST/api/v1/webhooks/{id}/testMCP tool test_webhook

Sends a webhook.test event to the endpoint now (once, no retries; even when it is switched off) and returns the delivery: its status, the endpoint's HTTP status and answer.

ParameterTypeDescription
id*integerpathThe webhook endpoint's id.
curl -X POST https://zeebra.ai/api/v1/webhooks/123/test \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns WebhookDelivery in data.

List webhook deliveries

GET/api/v1/webhooks/{id}/deliveriesMCP tool list_webhook_deliveries

The endpoint's newest deliveries first (the last 100 are kept): each event, its status, attempts, and the endpoint's last answer. A retrying delivery says when it is tried next.

ParameterTypeDescription
id*integerpathThe webhook endpoint's id.
statusstringqueryOnly deliveries with this status.pending · sending · retrying · succeeded · failed
pageintegerqueryPage number, from 1.
per_pageintegerqueryItems per page, up to 100.
curl "https://zeebra.ai/api/v1/webhooks/123/deliveries" \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns list of WebhookDelivery in data, a page at a time (meta has the totals).

Resend a webhook delivery

POST/api/v1/webhooks/{id}/deliveries/{delivery_id}/resendMCP tool resend_webhook_delivery

Sends a delivery's event to its endpoint again, now, with the same event id (so the receiver can tell it's a repeat). Returns the new delivery.

ParameterTypeDescription
id*integerpathThe webhook endpoint's id.
delivery_id*integerpathThe delivery's id.
curl -X POST https://zeebra.ai/api/v1/webhooks/123/deliveries/123/resend \
  -H "Authorization: Bearer $ZEEBRA_KEY"

Returns WebhookDelivery in data.