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
- OpenAPI
- openapi.json
- 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).
| Parameter | Type | Description |
|---|---|---|
| filter | objectquery | Property 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. |
| where | objectquery | A 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. |
| sort | stringquery | Up to 3 sortable property keys, comma separated, - for descending: -created_at (the default), email, field:plan,-last_engaged_at. |
| page | integerquery | Page number, from 1. |
| per_page | integerquery | Items 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The contact's id. |
| page | integerquery | Page number, from 1. |
| per_page | integerquery | Items 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.
| Parameter | Type | Description |
|---|---|---|
| email* | stringbody | |
| first_name | stringbody | |
| last_name | stringbody | |
| title | stringbody | |
| company | stringbody | Their 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_id | integerbody | Their company: an account of the workspace (list_accounts). Wins over company. |
| phone | stringbody | |
| city | stringbody | |
| lead_type | stringbody | opted_in: agreed to get your emails. prospect: not yet (the default).opted_in · prospect |
| consent_note | stringbody | Required for opted_in: how they agreed, e.g. "Signed up at our booth, 3 May". |
| fields | objectbody | Custom property values by key (see list_properties), checked against each property's type and rules. Required properties need a value. |
| tags | arraybody | Tag names; new ones are created. |
| list_ids | arraybody | Lists 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The contact's id. |
| first_name | string or nullbody | |
| last_name | string or nullbody | |
| title | string or nullbody | |
| company | string or nullbody | Their company's name: they join the account of that name (made if there is none); null takes them out of theirs. |
| account_id | integer or nullbody | Their company: an account of the workspace (list_accounts), or null for none. Wins over company. |
| phone | string or nullbody | |
| city | string or nullbody | |
| fields | objectbody | Custom 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The contact's id. |
| status* | stringbody | active · pending · dormant · unsubscribed · bounced · complained |
| note | stringbody | Why, 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).
| Parameter | Type | Description |
|---|---|---|
| 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).
| Parameter | Type | Description |
|---|---|---|
| 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 undoneDELETE/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| csv* | stringbody | The file's text, up to 10 MB. |
| mapping | objectbody | Header => 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* | stringbody | opted_in · prospect |
| consent_confirmed | booleanbody | Required true for opted_in: you confirm these people agreed to get your emails. |
| consent_note | stringbody | How they agreed (kept on each contact). |
| list_id | integerbody | Add everyone imported to this list. |
| tag_ids | arraybody | Tag everyone imported. |
| keep_role_addresses | booleanbody | Keep info@, sales@… addresses (skipped by default). |
| file_name | stringbody | A 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| filter | objectquery | The same filters as list_contacts. |
| where | objectquery | The 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.
| Parameter | Type | Description |
|---|---|---|
| q | stringquery | Search: every word in the name, domain or a searchable property. |
| filter | objectquery | Property 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. |
| where | objectquery | A rule tree like a segment's, for AND/OR, on account properties. |
| sort | stringquery | Up to 3 sortable property keys, comma separated, - for descending: name (the default), -contacts_count, -created_at. |
| page | integerquery | Page number, from 1. |
| per_page | integerquery | Items 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| name* | stringbody | |
| domain | stringbody | |
| website | stringbody | |
| industry | stringbody | |
| city | stringbody | |
| country | stringbody | A country code (SA) or name. |
| phone | stringbody | Without a "+", read as a number of the workspace's default country. |
| employees | integerbody | |
| description | stringbody | |
| owner_id | integerbody | A team member's user id. Defaults to the API key's member, when it has one. |
| fields | objectbody | Custom 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).
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The account's id. |
| name | stringbody | |
| domain | string or nullbody | |
| website | string or nullbody | |
| industry | string or nullbody | |
| city | string or nullbody | |
| country | string or nullbody | A country code (SA) or name. |
| phone | string or nullbody | Without a "+", read as a number of the workspace's default country. |
| employees | integer or nullbody | |
| description | string or nullbody | |
| owner_id | integer or nullbody | A team member's user id. |
| fields | objectbody | Custom 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 undoneDELETE/api/v1/accounts/{id}MCP tool delete_account
Deletes a company. Its contacts are kept, without a company link (their company name stays).
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| type | stringquery | contact or account.contact · account |
| status | stringquery | pending · rejected · merged |
| page | integerquery | Page number, from 1. |
| per_page | integerquery | Items 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.
| Parameter | Type | Description |
|---|---|---|
| type | stringbody | contact 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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 undonePOST/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The duplicate: merged into the other, then deleted. |
| into_id* | integerbody | The record kept. |
| take | arraybody | Property 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 undonePOST/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The duplicate: merged into the other, then deleted. |
| into_id* | integerbody | The record kept. |
| take | arraybody | Property 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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"Tags
Create a tag
POST/api/v1/tagsMCP tool create_tag
Creates a tag. tag_contacts also creates tags as it goes.
| Parameter | Type | Description |
|---|---|---|
| name* | stringbody | |
| color | stringbody | Defaults 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The tag's id. |
| name | stringbody | |
| color | stringbody | zinc · 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 undoneDELETE/api/v1/tags/{id}MCP tool delete_tag
Deletes a tag and takes it off every contact. The contacts stay.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| entity | stringquery | contact (the default) or account: your companies (the Companies page; see list_accounts).contact · account |
| include_archived | booleanquery | Also 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.
| Parameter | Type | Description |
|---|---|---|
| entity | stringbody | contact (the default) or account: your companies (the Companies page; see list_accounts).contact · account |
| label* | stringbody | |
| type* | stringbody | text · long_text · number · currency · date · select · multi_select · boolean · email · phone · url · country · user |
| key | stringbody | Lowercase letters, digits and underscores. Defaults to one made from the label. |
| description | stringbody | |
| options | arraybody | Choices of a select or multi_select property: {label, color?}, and the value when changing an existing option (values never change, labels can). |
| settings | objectbody | Validation 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. |
| group | string or nullbody | A group key from list_properties (contact_info, work, other…). Left out on create: "other". |
| required | booleanbody | Contacts can't be added or saved without it (imports and signups skip it). |
| searchable | booleanbody | Text 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The custom property's id (from list_properties). |
| label | stringbody | |
| description | string or nullbody | |
| options | arraybody | Choices of a select or multi_select property: {label, color?}, and the value when changing an existing option (values never change, labels can). |
| settings | objectbody | Validation 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. |
| group | string or nullbody | A group key from list_properties (contact_info, work, other…). Left out on create: "other". |
| required | booleanbody | |
| searchable | booleanbody |
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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| key* | stringpath | The system property's key, like company. |
| entity | stringbody | contact (the default) or account: your companies (the Companies page; see list_accounts).contact · account |
| label | string or nullbody | |
| description | string or nullbody | |
| group | string or nullbody | A group key from list_properties (contact_info, work, other…). Left out on create: "other". |
| required | booleanbody | |
| hidden | booleanbody |
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.
| Parameter | Type | Description |
|---|---|---|
| name* | stringbody | |
| description | stringbody | |
| default_subscribed | booleanbody |
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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The topic's id. |
| name | stringbody | |
| description | string or nullbody | |
| default_subscribed | booleanbody |
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 undoneDELETE/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| q | stringquery | Part of an address. |
| reason | stringquery | bounce · complaint · unsubscribe · manual |
| page | integerquery | Page number, from 1. |
| per_page | integerquery | Items 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.
| Parameter | Type | Description |
|---|---|---|
| emails* | arraybody | |
| note | stringbody |
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 undoneDELETE/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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 undoneDELETE/api/v1/lists/{id}MCP tool delete_list
Deletes a list. Its contacts are kept.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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).
| Parameter | Type | Description |
|---|---|---|
| name* | stringbody | |
| rules* | objectbody | A 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The segment's id. |
| name | stringbody | |
| rules | objectbody | A 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 undoneDELETE/api/v1/segments/{id}MCP tool delete_segment
Deletes a segment. Refused while another segment uses it. Its contacts are kept.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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).
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| name* | stringbody | |
| format* | stringbody | markdown, html or editor.editor · html · markdown |
| subject | stringbody | |
| preview_text | string or nullbody | Shown next to the subject in the inbox. |
| markdown | string or nullbody | |
| html | string or nullbody | |
| document | object or nullbody | A Tiptap editor document (format editor). |
| topic_id | integer or nullbody | The topic it belongs to (list_topics); people who left the topic don't get it. |
| theme | string or nullbody | A 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The template's id. |
| name | stringbody | |
| format | stringbody | markdown, html or editor.editor · html · markdown |
| subject | stringbody | |
| preview_text | string or nullbody | Shown next to the subject in the inbox. |
| markdown | string or nullbody | |
| html | string or nullbody | |
| document | object or nullbody | A Tiptap editor document (format editor). |
| topic_id | integer or nullbody | The topic it belongs to (list_topics); people who left the topic don't get it. |
| theme | string or nullbody | A 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 undoneDELETE/api/v1/templates/{id}MCP tool delete_template
Deletes a template. Campaigns made from it keep their content.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The template's id. |
| contact_id | integerbody | Fill 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The template's id. |
| to* | stringbody | A workspace member's address. |
| sender_id | integerbody | Who 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).
| Parameter | Type | Description |
|---|---|---|
| status | stringquery | draft · scheduled · sending · paused · sent · canceled |
| page | integerquery | Page number, from 1. |
| per_page | integerquery | Items 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| name* | stringbody | |
| template_id | integerbody | Copy this template's content. |
| format | stringbody | markdown, html or editor.editor · html · markdown |
| subject | stringbody | |
| preview_text | string or nullbody | Shown next to the subject in the inbox. |
| markdown | string or nullbody | |
| html | string or nullbody | |
| document | object or nullbody | A Tiptap editor document (format editor). |
| topic_id | integer or nullbody | The topic it belongs to (list_topics); people who left the topic don't get it. |
| theme | string or nullbody | A colour theme; null uses the workspace's.standard · letter · newsroom · oasis · sand · night · typewriter · poster |
| sender_id | integerbody | Who it is from (list_senders). |
| audience | objectbody | Who gets it: {"lists": [ids], "segments": [ids]}. |
| exclusions | objectbody | Who never gets it, even if in the audience: {"lists": [ids], "segments": [ids]}. |
| audience_limit | integer or nullbody | Only the N most engaged recipients; send_campaign_to_more reaches the rest later. |
| publish_to | stringbody | Where 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The campaign's id. |
| name | stringbody | |
| format | stringbody | markdown, html or editor.editor · html · markdown |
| subject | stringbody | |
| preview_text | string or nullbody | Shown next to the subject in the inbox. |
| markdown | string or nullbody | |
| html | string or nullbody | |
| document | object or nullbody | A Tiptap editor document (format editor). |
| topic_id | integer or nullbody | The topic it belongs to (list_topics); people who left the topic don't get it. |
| theme | string or nullbody | A colour theme; null uses the workspace's.standard · letter · newsroom · oasis · sand · night · typewriter · poster |
| sender_id | integerbody | Who it is from (list_senders). |
| audience | objectbody | Who gets it: {"lists": [ids], "segments": [ids]}. |
| exclusions | objectbody | Who never gets it, even if in the audience: {"lists": [ids], "segments": [ids]}. |
| audience_limit | integer or nullbody | Only the N most engaged recipients; send_campaign_to_more reaches the rest later. |
| publish_to | stringbody | Where 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 undoneDELETE/api/v1/campaigns/{id}MCP tool delete_campaign
Deletes a draft. A campaign that was scheduled or sent can be canceled instead.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The campaign's id. |
| to* | stringbody | A 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).
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The campaign's id. |
curl "https://zeebra.ai/api/v1/campaigns/123/tips" \
-H "Authorization: Bearer $ZEEBRA_KEY"Send a campaign now
Can’t be undonePOST/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).
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The campaign's id. |
| confirm_recipients* | integerbody | The 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 undonePOST/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The campaign's id. |
| confirm_recipients* | integerbody | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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 undonePOST/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The campaign's id. |
| audience_limit | integer or nullbody | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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 undonePOST/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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 undonePOST/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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| campaign_id | integerquery | Only tests of this campaign. |
| template_id | integerquery | Only tests of this template. |
| limit | integerquery | How 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 tooPOST/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.
| Parameter | Type | Description |
|---|---|---|
| email* | stringbody | |
| first_name | stringbody | |
| form_id | stringbody | A signup form's public id: its list, topics, confirmation and welcome emails apply. |
| topics | arraybody | Without a form: topic ids or keys to join. |
| double_opt_in | booleanbody | Without a form: false skips the confirmation email (only if you confirmed them yourself). |
| page_url | stringbody | Where they signed up, kept with their consent. |
| keep_email | booleanbody | Sign up an address that looks like a typo anyway. |
| fields | objectbody | Custom 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.
| Parameter | Type | Description |
|---|---|---|
| host* | stringbody | A 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 undoneDELETE/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).
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| url* | stringbody | An https:// URL on a public host. |
| events* | arraybody | Event types (list_webhook_event_types), or ["*"] for every event, including ones added later. |
| description | string or nullbody | What 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The webhook endpoint's id. |
| url | stringbody | |
| events | arraybody | Event types (list_webhook_event_types), or ["*"] for every event, including ones added later. |
| description | string or nullbody | What it is for, e.g. "atndr CRM sync". |
| enabled | booleanbody |
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 undoneDELETE/api/v1/webhooks/{id}MCP tool delete_webhook
Deletes an endpoint and its delivery log. It gets nothing more.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The webhook endpoint's id. |
| status | stringquery | Only deliveries with this status.pending · sending · retrying · succeeded · failed |
| page | integerquery | Page number, from 1. |
| per_page | integerquery | Items 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.
| Parameter | Type | Description |
|---|---|---|
| id* | integerpath | The webhook endpoint's id. |
| delivery_id* | integerpath | The 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.