API

Everything the app does to an audience, a template or a campaign is reachable over HTTP. A script — or an agent — works on the same data as the interface, with the same rules, without a browser session. This page is the map: authentication, the shape of every answer, and the full list of endpoints. The details live in the topic pages.
TopicWhat it covers
AudienceContacts and tags
Content & SendTemplates, rendering, campaigns and sending
Recipient linksThe token URLs that live inside sent emails

Authentication

Every /api/v1 request carries an API key in the x-api-key header. A key is created from the app and belongs to one organization: that organization is the entire scope of the key.
curl -H "x-api-key: $MK_API_KEY" \
  "https://app.example.com/api/v1/tags"

The key acts as the person who created it

A key is not a second identity with its own powers. It carries the rights of the member who created it, in the organization it was created for. A key made by an admin can write; a key made by a member can read. Nothing is granted through the API that the same person could not do in the interface.
A key stops working when its owner leaves the organization. No orphan access survives a departure: the key answers 401 from that moment, exactly as if it had been revoked. Revoking the key of someone who left is therefore a cleanup, not a security fix.
Two rules decide every call, and both have to pass:
QuestionDecided byAnswer when it fails
Is the caller still a member, with which role?The key's owner and their role401 / 403
Does the resource belong to the key's organization?The key's organization404

Who can do what

OperationMinimum role in the organization
Reading anything, and POST /api/v1/rendermember
Creating, updating, deleting, tagging, sendingadmin
POST /api/v1/render is the exception among the write verbs because it writes nothing: it renders and returns, with no side effect.

Response envelope

CaseBody
Success{"success": true, "data": …}
Paginated list{"success": true, "data": [...], "pagination": {"total", "page", "limit", "totalPages"}}
Error{"error": "…"}
Validation error{"error": "…", "details": [ … Zod issues … ]}
StatusWhen
200Read or mutation done
201Resource created
202Send accepted, happening in the background — nothing is finished yet
400Invalid parameters or body — details lists the offending fields
401Missing or invalid key, or a key whose owner left the organization
403The caller is a legitimate member, but their role is too low
404Unknown resource, or a resource of another organization
409State conflict — a tag name already taken, a campaign already sent
429Rate limit of the key exceeded
500Unexpected server error
A resource of another organization answers 404, never 403. A 403 would confirm that the id exists. Same response for an unknown id and for an id you are not allowed to read — you cannot tell them apart, and that is the point. The 403 is reserved for the other question: you are in the right organization, your role is too low.

Every endpoint

Audience — details

EndpointWhat it does
GET /api/v1/contactsContacts of a list, paginated and filterable
GET /api/v1/contacts/{contactId}One contact with its tags
PATCH /api/v1/contacts/{contactId}Updates first name, last name or status
POST /api/v1/lists/{listId}/contactsAdds a contact to a list (upsert)
POST /api/v1/lists/{listId}/contacts/bulkPushes up to 500 contacts at once, line by line
POST /api/v1/contacts/{contactId}/unsubscribeUnsubscribes a contact
POST /api/v1/contacts/{contactId}/resubscribePuts a contact back to subscribed
GET /api/v1/tagsTags of the organization with their carrier count
POST /api/v1/tagsCreates a tag
POST /api/v1/contacts/{contactId}/tagsPuts a tag on a contact, by name (get-or-create)
DELETE /api/v1/contacts/{contactId}/tags/{tagId}Removes a tag from a contact

Content & Send — details

EndpointWhat it does
GET /api/v1/templatesTemplates of the organization, paginated
GET /api/v1/templates/{templateId}One template with its HTML
POST /api/v1/templatesCreates a template
PATCH /api/v1/templates/{templateId}Updates name, subject or HTML
DELETE /api/v1/templates/{templateId}Deletes a template
POST /api/v1/renderRenders HTML without sending anything
GET /api/v1/campaignsCampaigns of the organization, paginated
POST /api/v1/campaignsCreates a campaign in draft
GET /api/v1/campaigns/{campaignId}State, target, recipient count and send progress
POST /api/v1/campaigns/{campaignId}/sendTriggers the send — answers 202, never waits

Rate limit

The limit is carried by the key, not by the IP. A key over its limit answers 429 on every endpoint. Back off and retry: nothing was done.

Where to start

Read the audience first — a campaign without a list to send it to goes nowhere — then compose with templates and rendering. The whole path from an empty organization to a sent newsletter is walked end to end at the bottom of Content & Send.