Skip to main content
About this page · Updated 2026-10-27 15:04 UTC

What this page is: Customer-facing Marketing Communications Block API endpoints for lists, contacts, templates, campaigns, suppressions, tracking, analytics, and data controls.

How far to trust this page: the build checked 8 names, paths, tools, scopes and error codes against the real product (0 do not match). Reviewed claim by claim on 2026-10-22 (documentation truth audit).

Updated 2026-10-27 15:04 UTCLatest change: Communications MCP: manage workspaces and webhooksRecently updated pages

Communications API Reference

Use the Communications API to manage workspaces, contact lists, subscribers, templates, campaigns, consent, suppressions, and reporting. The base URL is:

https://motorical.com/comm-api/api

Start here​

For normal Communications operations you need two values:

  1. An account API key. In the Motorical dashboard, open Settings → Account API Keys, create a key, and copy the ak_live_... value when it is shown.
  2. A workspace ID (also called the tenant ID). In the dashboard, open Comm → Workspaces and use the copy button beside the workspace ID.

You can also discover the workspace ID through the API. This bootstrap request needs only the account API key:

export MOTORICAL_API_KEY="ak_live_YOUR_KEY"

curl -sS "https://api.motorical.com/api/comm/tenant" \
-H "Authorization: ApiKey $MOTORICAL_API_KEY" \
| jq .

The response contains data.tenantId for the default workspace and a data.workspaces array for every workspace on the account:

{
"success": true,
"data": {
"tenantId": "3f6c1a2e-0000-0000-0000-000000000000",
"workspaces": [
{
"id": "3f6c1a2e-0000-0000-0000-000000000000",
"name": "Default"
}
]
}
}

Save the id of the workspace you want and make your first Communications call:

export COMM_TENANT_ID="3f6c1a2e-0000-0000-0000-000000000000"

curl -sS "https://motorical.com/comm-api/api/lists" \
-H "Authorization: ApiKey $MOTORICAL_API_KEY" \
-H "X-Tenant-Id: $COMM_TENANT_ID" \
-H "Accept: application/json" \
| jq .

jq . pretty-prints the JSON. Do not put a trailing \ after the final command line. Keep real API keys out of source code, screenshots, and support messages.

Authentication rules​

Nearly every customer operation requires both headers:

Authorization: ApiKey ak_live_YOUR_KEY
X-Tenant-Id: YOUR_WORKSPACE_ID
  • Authorization is either an account API key (ApiKey ak_live_..., created in the dashboard under Settings → Account API Keys) or a dashboard JWT (Bearer ...).
  • X-Tenant-Id is your workspace ID. “Tenant” and “workspace” refer to the same value in the API.
  • Account-scoped workspace-management operations are the exception: they use the account API key but do not require X-Tenant-Id. The health check requires neither header.
SituationResult
X-Tenant-Id header missing400 Missing X-Tenant-Id
X-Tenant-Id sent without valid Authorization401 for an existing, active workspace id (an unknown or inactive id returns 403 first). The workspace header alone is never accepted.
Workspace ID does not belong to the authenticated account403
Campaign create or schedule with a Motor Block that is not marketing type400 (error message names the block's type)
Campaign create or schedule with a Motor Block not assigned to the workspace400 code MOTOR_BLOCK_NOT_ASSIGNED
Campaign create or schedule with a Motor Block owned by another account403 code MOTOR_BLOCK_FORBIDDEN
Another workspace's campaign, list or template id404 (the same answer as a missing one)
A malformed id in the path400 code INVALID_ID

An account API key plus a workspace is the way to use a marketing Motor Block: marketing blocks have no SMTP login or API key of their own. See Sending with a manual marketing block.

How to read endpoint syntax​

  • {id} is a placeholder. Replace it with the real ID and remove the braces. For campaign ID abc-123, /campaigns/{id} becomes /campaigns/abc-123.
  • Query parameters follow ?, for example /campaigns?compact=true.
  • GET reads data, POST creates or starts an action, PATCH changes selected fields, PUT replaces a value or collection, and DELETE removes data.
  • Send Content-Type: application/json only when the request has a JSON body.
  • The tables below show the path after https://motorical.com.

Internal provisioning routes and public tracking link callbacks are intentionally excluded from this reference. They are used by Motorical services and email links, not by customer API integrations.

Workspace discovery and management​

These account-scoped calls require Authorization: ApiKey ak_live_... but do not require X-Tenant-Id, except for current/motor-blocks as noted.

MethodEndpointPurpose
GEThttps://api.motorical.com/api/comm/tenantProvision if needed and return the default workspace ID plus all workspaces.
GET/comm-api/api/workspacesList workspaces.
POST/comm-api/api/workspacesCreate a workspace.
PATCH/comm-api/api/workspaces/{id}Rename a workspace or make it the default.
GET/comm-api/api/workspaces/{id}/motor-blocksList Motor Blocks assigned to a workspace.
POST/comm-api/api/workspaces/{id}/motor-blocks/from-domainFind or create the companion marketing Motor Block for a sending domain.
POST/comm-api/api/workspaces/{id}/motor-blocks/addFind or create the marketing Motor Block for a verified sending domain and add it to the workspace (never replaces or removes; dry_run: true changes nothing).
PUT/comm-api/api/workspaces/{id}/motor-blocksReplace a workspace's Motor Block assignments.
GET/comm-api/api/workspaces/current/motor-blocksList marketing Motor Blocks for the workspace in X-Tenant-Id; this call requires both headers.
GET/comm-api/api/workspaces/{id}/settings/consentRead click-as-consent settings.
PATCH/comm-api/api/workspaces/{id}/settings/consentChange click-as-consent settings.
GET/comm-api/api/workspaces/{id}/settings/open-trackingRead open-tracking settings.
PATCH/comm-api/api/workspaces/{id}/settings/open-trackingChange open-tracking settings.
GET/comm-api/api/workspaces/{id}/settings/unsubscribeRead the unsubscribe landing page of a workspace.
PATCH/comm-api/api/workspaces/{id}/settings/unsubscribeSet or clear it (details).

Lists and Contacts​

MethodEndpointPurpose
GET/comm-api/api/listsList contact lists.
POST/comm-api/api/listsCreate a list.
POST/comm-api/api/lists/from-filterCreate a smart list from filters.
PATCH/comm-api/api/lists/{id}Update a list.
DELETE/comm-api/api/lists/{id}Delete a list.
DELETE/comm-api/api/listsBulk delete lists.
GET/comm-api/api/lists/{id}One list as a card: description, kind, provenance, computed counts, used_by.
POST/comm-api/api/lists/{id}/contacts/bulkAdd up to 500 contacts as JSON, with dry_run and a result for every row.
POST/comm-api/api/lists/{id}/contacts/bulk-removeTake contacts off a list by id (contactIds), or everything matching a search; membership only, the contacts are never deleted.
POST/comm-api/api/lists/deriveMake a list from other lists (union, intersection, difference).
GET/comm-api/api/lists/compareCompare two lists (a, b): only in one, in both.
GET/comm-api/api/lists/{id}/relationsWhere a list came from and what was made from it.
GET/comm-api/api/mapThe workspace at a glance: lists, campaigns, templates and their connections.
GET/comm-api/api/readinessThe first thing missing before a campaign can be sent.
GET/comm-api/api/lists/{id}/contactsList contacts in a list.
POST/comm-api/api/lists/{id}/contactsAdd contacts to a list.
DELETE/comm-api/api/lists/{id}/contacts/{contactId}Remove one contact from a list.
POST/comm-api/api/lists/{id}/contacts/importImport contacts from CSV.
GET/comm-api/api/lists/{id}/contacts/exportExport contacts from a list.
POST/comm-api/api/contacts/upsertCreate or update a contact.
DELETE/comm-api/api/contacts/{id}Delete a contact.
POST/comm-api/api/contacts/{id}/unsubscribeUnsubscribe a contact.
PUT/comm-api/api/contacts/{id}/resubscribeResubscribe a contact.

Templates​

MethodEndpointPurpose
GET/comm-api/api/templatesList templates.
POST/comm-api/api/templates/validateValidate and preview a template without saving. Warnings marked blocksScheduling tell you scheduling would be refused.
POST/comm-api/api/templatesCreate a template.
GET/comm-api/api/templates/{id}Get template content.
PATCH/comm-api/api/templates/{id}Update a template.
DELETE/comm-api/api/templates/{id}Delete a template.
DELETE/comm-api/api/templatesBulk delete templates.
POST/comm-api/api/templates/{id}/duplicateDuplicate a template; the copy remembers its parent (derived_from).
POST/comm-api/api/templates/previewRender a template for a contact or a sample, with and without a name.
GET/comm-api/api/templates/rulesThe template rulebook, generated from the validator's constants.
POST/comm-api/api/templates/lintFindings (closed vocabulary) with locations and ready fixes.
POST/comm-api/api/templates/fixApply the safe fixes in memory; never saves.
POST/comm-api/api/templates/check-linksEvery link with tracking status and problems.
GET/comm-api/api/templates/startersThe seven starter skeletons and their slots.
GET/comm-api/api/templates/starters/{key}One starter.
POST/comm-api/api/templates/starters/{key}/renderRender a starter with the brand kit; saves nothing.
POST/comm-api/api/templates/from-starterCreate a template from a starter and the brand kit.
GET/comm-api/api/settings/brand-kitGet the workspace brand kit.
PATCH/comm-api/api/settings/brand-kitUpdate the brand kit (partial; null clears a field).

Campaigns​

MethodEndpointPurpose
GET/comm-api/api/campaignsList campaigns. Supports compact=true.
POST/comm-api/api/campaignsCreate a campaign.
GET/comm-api/api/campaigns/{id}Get campaign details.
PATCH/comm-api/api/campaigns/{id}/settingsUpdate campaign settings; what can change depends on the status.
POST/comm-api/api/campaigns/{id}/scheduleSchedule a draft: re-checks and compiles it, then sends now or at scheduled_at.
POST/comm-api/api/campaigns/{id}/cancelCancel a draft, scheduled or sending campaign. Final.
POST/comm-api/api/campaigns/{id}/duplicateCopy a campaign of any status into a new draft.
POST/comm-api/api/campaigns/{id}/test-sendSend the campaign as a test to the account owner only (details).
DELETE/comm-api/api/campaigns/{id}Move a campaign to the recycle bin (a scheduled one is cancelled).
GET/comm-api/api/campaigns/deletedList deleted campaigns.
PATCH/comm-api/api/campaigns/{id}/restoreRestore a deleted campaign.
DELETE/comm-api/api/campaigns/{id}/permanentPermanently delete a campaign.
DELETE/comm-api/api/campaigns/cleanupCleanup old deleted campaigns.
POST/comm-api/api/campaigns/{id}/compileOptional preview of the compiled message and audience. Scheduling compiles by itself.
GET/comm-api/api/campaigns/{id}/eventsList campaign delivery events.
GET/comm-api/api/campaigns/{id}/statsCampaign statistics, including unsubscribed.
GET/comm-api/api/campaigns/{id}/reportPer-recipient delivery report: the source the other result endpoints read.
GET/comm-api/api/campaigns/{id}/analyticsCampaign analytics summary.
GET/comm-api/api/campaigns/{id}/click-breakdownURL-level click analytics.
GET/comm-api/api/campaigns/{id}/recipientsCampaign recipient list.
GET/comm-api/api/campaigns/{id}/recipient-consentCampaign recipient consent status.
GET/comm-api/api/campaigns/{id}/clicksCampaign click events.

Recipients​

MethodEndpointPurpose
GET/comm-api/api/recipientsSearch recipients across lists/campaigns.
GET/comm-api/api/recipients/campaignsCampaign recipient grouping data.
GET/comm-api/api/recipients/facetsValues and counts available for recipient filtering.
POST/comm-api/api/recipients/filterFilter recipients with criteria.
POST/comm-api/api/recipients/exportExport filtered recipients.
POST/comm-api/api/recipients/bulk-deleteSoft-delete recipients in bulk.
POST/comm-api/api/recipients/bulk-delete-by-filterSoft-delete all recipients matching a filter.
POST/comm-api/api/recipients/bulk-moveMove recipients between lists.
POST/comm-api/api/recipients/restoreRestore recipients in bulk.
GET/comm-api/api/recipients/deletedList deleted recipients.
PATCH/comm-api/api/recipients/{id}/restoreRestore one deleted recipient.
DELETE/comm-api/api/recipients/{id}/permanentPermanently delete a recipient.
POST/comm-api/api/recipients/bulk-permanent-deletePermanently delete several deleted recipients; body { "recipient_ids": [...] }, returns { deleted_count }. Irreversible.
DELETE/comm-api/api/recipients/cleanupCleanup old deleted recipients.

Suppressions​

MethodEndpointPurpose
GET/comm-api/api/suppressionsList suppressions.
POST/comm-api/api/suppressionsAdd a suppression (account-wide).
DELETE/comm-api/api/suppressions/{id}Remove a suppression. A spam-complaint one needs acknowledge_complaint=true.
POST/comm-api/api/suppressions/bulkBulk import suppressions.
GET/comm-api/api/suppressions/statsSuppression statistics.
POST/comm-api/api/suppressions/import-bouncesImport bounces into suppressions.

Tracking and Unsubscribe Analytics​

MethodEndpointPurpose
GET/comm-api/api/tracking/eventsSearch visible tracking events.
GET/comm-api/api/tracking/exportExport the same visible tracking-event population.
GET/comm-api/api/tracking/statsCounts for the same visible tracking-event population.
GET/comm-api/api/unsubscribe-eventsList unsubscribe events.
GET/comm-api/api/unsubscribe-analyticsUnsubscribe analytics.
GET/comm-api/api/unsubscribe-trendsUnsubscribe trends.
MethodEndpointPurpose
GET/comm-api/api/settings/consentRead click-as-consent setting for the current workspace.
PATCH/comm-api/api/settings/consentChange click-as-consent setting for the current workspace.
GET/comm-api/api/settings/open-trackingRead open-tracking setting for the current workspace.
PATCH/comm-api/api/settings/open-trackingChange open-tracking setting for the current workspace.
GET/comm-api/api/settings/unsubscribeRead the landing page recipients are sent to after unsubscribing.
PATCH/comm-api/api/settings/unsubscribeSet or clear that landing page (details).
GET/comm-api/api/consent-eventsList consent events.
GET/comm-api/api/consent-events/analyticsConsent-event analytics.

The three tracking endpoints share event_type, campaign_id, contact_email, date_from, and date_to filters. Comma-separate multiple event types or campaign IDs. They hide an earlier queued lifecycle row after the same campaign recipient has a later outcome, so summary counts match the event list and CSV export. Tracking stats cover all visible events when no date filter is supplied; pass days for an explicit rolling-day window.

All three also accept view (chronological | recipient, default chronological -- omitting it keeps today's raw per-event behavior). view=recipient collapses each campaign recipient to one row at their furthest send-funnel stage (queued, sent, delivered-or-bounced, opened, clicked); complained and unsubscribed are returned as independent boolean flags on that row rather than competing funnel stages, since a recipient can complain or unsubscribe at any point after delivery regardless of whether they also opened or clicked. In this view, event_type filters on the recipient's current stage (or flag), not on raw per-event rows -- exporting is exactly the current view and filters, so a by-recipient export and a chronological export of the same search never mix rows from the other population. /stats in recipient view returns by_status (the non-overlapping funnel breakdown) plus separate complained/unsubscribed counts, in place of the raw by_type breakdown.

Webhooks​

Webhooks tell your server when a campaign completes, a message is delivered, bounces or draws a complaint, or a contact unsubscribes or gives consent. {id} must be your own workspace ID, the same value as X-Tenant-Id. Full guide: Webhooks.

MethodEndpointPurpose
GET/comm-api/api/workspaces/{id}/webhooksList the workspace's webhooks.
POST/comm-api/api/workspaces/{id}/webhooksCreate a webhook; the signing secret is shown once.
PATCH/comm-api/api/workspaces/{id}/webhooks/{webhookId}Change url, events or enabled.
DELETE/comm-api/api/workspaces/{id}/webhooks/{webhookId}Delete a webhook.
POST/comm-api/api/workspaces/{id}/webhooks/{webhookId}/testQueue a test event.

Analytics and Data Controls​

MethodEndpointPurpose
GET/comm-api/api/analytics/overviewCommunications analytics overview.
POST/comm-api/api/gdpr/exportEmail a contact their GDPR data export.
GET/comm-api/api/gdpr/exportDeprecated form of the same call (it sends mail, so use POST).
GET/comm-api/api/healthCommunications API health check.

Response Shape​

Most Communications endpoints return:

{
"success": true,
"data": {}
}

Failures return success: false, a sentence in error, and (for the errors a program should react to) a stable code:

{
"success": false,
"error": "This campaign has no eligible recipients (lists are empty, or everyone is unsubscribed, suppressed or not yet eligible).",
"code": "EMPTY_AUDIENCE"
}

Branch on code, not on the text of error. The campaign codes are in Campaigns: Errors; the template codes (TEMPLATE_INVALID, TYPE_IMMUTABLE) are in Templates; the suppression and unsubscribe rules are in Suppressions.

Retrying safely​

The create-style calls POST /campaigns, POST /campaigns/{id}/schedule and POST /campaigns/{id}/duplicate accept an Idempotency-Key header. Repeating a request with the same key and body replays the first answer instead of doing the work twice (response header Idempotent-Replay: true). Details: Campaigns: Retrying safely.

Rules that apply to every call​

  • Campaign and settings writes check ownership. A template, list, Motor Block or campaign from another workspace is refused, never partly used.
  • Campaigns have a status machine. completed and cancelled are final; duplicate a campaign to send it again (states).
  • The From address is bound to the Motor Block's domain (create rules).
  • Merge variables are a closed set. A template that uses a {{tag}} outside it is refused at scheduling (UNKNOWN_MERGE_VARIABLE, with the offending tags in variables); see Merge variables. Check with POST /templates/validate first.
  • Every campaign needs {{unsubscribe_url}} in its template body or scheduling is refused (UNSUBSCRIBE_LINK_MISSING). You can send recipients to your own page afterwards (landing page) and be told about each unsubscribe by webhook.
  • Suppressions are account-wide and a spam-complaint suppression needs an explicit acknowledgement to remove (details).