API reference
Every Buzzmark REST endpoint: parameters, responses and examples for sending email, reading your message log and managing domains.
Overview
The API is a small set of JSON endpoints over HTTPS. The base URL is https://api.buzzmarkapp.com.
Send request bodies as JSON with Content-Type: application/json; every response is JSON. Times are
ISO 8601 in UTC, and ids are opaque strings (UUIDs).
Authentication
Every endpoint here authenticates with a hive token (bzm_srv_…). Create one on the
hive’s settings page in the dashboard - it’s shown once, so store it safely. Send it either way:
X-Buzzmark-Server-Token: bzm_srv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer bzm_srv_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
A token belongs to one hive: mail you send and the message log you read are that hive’s. Domains belong to the
whole workspace, so any hive’s token can manage them. The same token is the
SMTP password. A missing, wrong or revoked token - or one for a deleted
hive - gets 401:
{ "ErrorCode": 10, "Message": "Invalid or missing server API token." }
Other credentials are for other things: the account token (bzm_acct_…) is for the
account-management MCP server, and mailbox keys (bzm_mbx_…) are for
AI assistants. Neither works on these endpoints.
Errors
Buzzmark uses standard HTTP status codes, with a JSON body that says what went wrong:
| Status | Meaning |
|---|---|
200 / 201 | Success (201 when a domain is created). |
401 | Missing or invalid token. |
404 | The message or domain doesn’t exist (or belongs to another hive / workspace). |
422 | The request was understood but refused - invalid fields, or the message can’t be sent. |
429 | Your account’s per-minute send rate was exceeded. Retry after a short wait. |
5xx | Something went wrong on our side. Safe to retry with backoff. |
Most errors use a Postmark-style envelope with a numeric ErrorCode:
| ErrorCode | Meaning |
|---|---|
0 | OK - the message was accepted. |
10 | Invalid or missing token. |
300 | Invalid request (batch items and domain endpoints). |
401 | The From domain isn’t a verified domain on your workspace. |
405 | Not allowed to send: the account is blocked, or a free plan has used its monthly allowance. |
406 | A recipient is on the hive’s suppression list. |
429 | Per-minute send rate exceeded. |
701 | Unknown message or domain. |
Field validation errors on POST /api/email and POST /api/events use a different shape - a
summary message plus an errors object keyed by field:
{
"message": "The from field is required.",
"errors": {
"From": ["The from field is required."]
}
}
Limits
| Limit | Value |
|---|---|
| Messages per batch | 500 |
Recipients field length (To, Cc, Bcc) | 5,000 characters each |
Body size (HtmlBody, TextBody) | 10 MB each |
| Attachments per message | 20 |
| Attachment size | 25 MB total, decoded. Over 10 MB, the largest files are sent as download links (kept 10 days). |
| Send rate | Per account, per minute, depending on your plan. Over it you get 429. |
| Monthly allowance | Free plans stop sending at their allowance (ErrorCode 405); paid plans are never cut off - extra mail is billed. |
| Message log page size | 500 (count) |
Message statuses
| Status | Meaning |
|---|---|
queued | Accepted and waiting to go out. |
held | Waiting for Buzzmark’s review - new accounts have their first few emails checked by a person. It sends once approved. |
sent | Handed to the recipient’s mail server. |
bounced | The recipient’s server refused it. Hard bounces add the address to your suppression list. |
failed | Couldn’t be delivered for another reason; see Error on the message. |
rejected | Not sent - rejected during Buzzmark’s review. |
Sending
Send an email
/api/email
Auth: Hive token
Sends one email. It’s accepted immediately and delivered in the background - track what happens next in
the message log or with webhooks.
Provide HtmlBody, TextBody or both.
Body
From
hello@acme.com or Acme <hello@acme.com>. The domain must be verified on your workspace. Max 255.To
Ann <ann@example.com>, bob@example.com. Max 5,000.Cc
Bcc
ReplyTo
Subject
HtmlBody
TextBody. Open and click tracking are added per the hive’s settings.TextBody
HtmlBody.Tag
receipt. Max 255.Metadata
{"order_id": "1234"}. Stored with the message and included in every webhook about it.Headers
[{"Name": "X-Campaign", "Value": "fall"}]. Names can’t contain spaces or colons; values max 2,000. Headers Buzzmark manages (From, To, Subject, Date, Message-ID, Content-*, DKIM-Signature, …) are ignored.MessageStream
outbound). It doesn’t change how mail is sent - use a separate hive to keep streams apart.Attachments
Attachments[].Name
Attachments[].Content
Attachments[].ContentType
application/pdf.Attachments[].ContentID
HtmlBody as <img src="cid:logo">. Inline files must fit in the 10 MB that travels as real attachments.Responses
-
200
Accepted.
ErrorCodeis0andMessageIDidentifies the message. A brand-new account’s message may be accepted asheldfor review. -
401
Invalid or missing token (
ErrorCode 10). -
422
Not sent. Either field validation failed (
message/errors), or the message was refused:401unverified sender domain,405not allowed to send,406suppressed recipient. -
429
Per-minute send rate exceeded (
ErrorCode 429). Retry shortly.
curl https://api.buzzmarkapp.com/api/email \
-X POST \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"From": "Acme <hello@acme.com>",
"To": "customer@example.com",
"Subject": "Your receipt",
"HtmlBody": "<p>Thanks for your order!</p>",
"TextBody": "Thanks for your order!",
"Tag": "receipt",
"Metadata": { "order_id": "1234" }
}'
{
"To": "customer@example.com",
"SubmittedAt": "2026-10-24T10:15:02+00:00",
"MessageID": "f5ab446c-18ad-4db4-b747-8e1ba70ec72d",
"ErrorCode": 0,
"Message": "OK"
}
{
"ErrorCode": 406,
"Message": "'gone@example.com' is on this server's suppression list and cannot be emailed."
}
"Attachments": [
{
"Name": "invoice.pdf",
"Content": "JVBERi0xLjcKJ...",
"ContentType": "application/pdf"
},
{
"Name": "logo.png",
"Content": "iVBORw0KGgo...",
"ContentType": "image/png",
"ContentID": "logo"
}
]
Send a batch
/api/email/batch
Auth: Hive token
Sends up to 500 emails in one request. The body is a JSON array of messages - each one exactly like
Send an email - or an object with a Messages array. Each message is checked and
sent on its own, so one bad message doesn’t stop the rest.
Body
[ … ]
{"Messages": [ … ]}. Max 500.Responses
-
200
An array with one result per message, in the order sent. Each is either the accepted object (
ErrorCode 0) or an error withErrorCode,MessageandTo- including429for messages over the rate limit. - 401 Invalid or missing token.
-
422
The batch itself is invalid: empty, or more than 500 messages (
ErrorCode 300).
curl https://api.buzzmarkapp.com/api/email/batch \
-X POST \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "From": "hello@acme.com", "To": "a@example.com",
"Subject": "Hi A", "TextBody": "Hello!" },
{ "From": "hello@acme.com", "To": "b@example.com",
"Subject": "Hi B" }
]'
[
{
"To": "a@example.com",
"SubmittedAt": "2026-10-24T10:15:02+00:00",
"MessageID": "0b7f6e2a-6d52-4a51-9c3e-1e0e6f6b3a10",
"ErrorCode": 0,
"Message": "OK"
},
{
"ErrorCode": 300,
"Message": "Provide either HtmlBody or TextBody.",
"To": "b@example.com"
}
]
Message log
List messages
/api/messages/outbound
Auth: Hive token
The hive’s sent mail, newest first.
Query parameters
count
offset
status
tag
Tag.recipient
To contains this text.Response fields
TotalCount
Messages[]
MessageID, To, From, Subject, Status, Tag, MessageStream, SubmittedAt.Responses
- 200 The page of messages.
- 401 Invalid or missing token.
curl "https://api.buzzmarkapp.com/api/messages/outbound?status=bounced&count=20" \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN"
{
"TotalCount": 1,
"Messages": [
{
"MessageID": "f5ab446c-18ad-4db4-b747-8e1ba70ec72d",
"To": "customer@example.com",
"From": "Acme <hello@acme.com>",
"Subject": "Your receipt",
"Status": "bounced",
"Tag": "receipt",
"MessageStream": "outbound",
"SubmittedAt": "2026-10-24T10:15:02+00:00"
}
]
}
Get a message
/api/messages/outbound/{MessageID}
Auth: Hive token
One message in full: everything from the list, plus bodies, headers, metadata, attachments and its event history.
Path parameters
MessageID
Response fields (in addition to the list fields)
Cc, Bcc, ReplyTo
HtmlBody, TextBody
Headers, Metadata
Error
SentAt
Attachments[]
Name, ContentType, ContentLength (bytes), ContentID. File contents aren’t returned.MessageEvents[]
Type (delivered, bounce, open, click, spam), Recipient, ReceivedAt.Responses
- 200 The message.
- 401 Invalid or missing token.
- 404 No message with that id on this hive.
curl https://api.buzzmarkapp.com/api/messages/outbound/f5ab446c-18ad-4db4-b747-8e1ba70ec72d \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN"
{
"MessageID": "f5ab446c-18ad-4db4-b747-8e1ba70ec72d",
"To": "customer@example.com",
"From": "Acme <hello@acme.com>",
"Subject": "Your receipt",
"Status": "sent",
"Tag": "receipt",
"MessageStream": "outbound",
"SubmittedAt": "2026-10-24T10:15:02+00:00",
"Cc": null,
"Bcc": null,
"ReplyTo": null,
"HtmlBody": "<p>Thanks for your order!</p>",
"TextBody": "Thanks for your order!",
"Headers": null,
"Metadata": { "order_id": "1234" },
"Error": null,
"SentAt": "2026-10-24T10:15:04+00:00",
"Attachments": [],
"MessageEvents": [
{ "Type": "open", "Recipient": "customer@example.com",
"ReceivedAt": "2026-10-24T10:21:40+00:00" },
{ "Type": "delivered", "Recipient": "customer@example.com",
"ReceivedAt": "2026-10-24T10:15:06+00:00" }
]
}
Domains
Add sending domains and get the DNS records to publish - useful for automating setup with Terraform or your own
tooling. A domain id is the same id as in its dashboard URL (/domains/{id}). Buzzmark re-checks every
domain’s DNS hourly, so it verifies itself once the records are live.
List domains
/api/domains
Auth: Hive token
Every domain on the workspace, newest first.
Response fields
Domains[].ID
Domains[].Name
acme.com.Domains[].Status
pending (not verified yet), verified, or failed (was verified, but its records have since gone).Domains[].Verified
Domains[].InboundEnabled
Domains[].LastCheckedAt
Responses
- 200 The domains.
- 401 Invalid or missing token.
curl https://api.buzzmarkapp.com/api/domains \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN"
{
"Domains": [
{
"ID": "c6504766-7867-4d5b-8568-0fdabd6e2b3f",
"Name": "acme.com",
"Status": "verified",
"Verified": true,
"InboundEnabled": false,
"LastCheckedAt": "2026-10-24T09:00:12+00:00"
}
]
}
Add a domain
/api/domains
Auth: Hive token
Adds a domain, generates its DKIM key and returns the DNS records to publish. Publish the Sending records to send from it; the Receiving MX record is only needed to receive mail on it.
Body
domain
acme.com or mail.acme.com.Response fields
ID, Name, Status, Verified
Sending.Ready
Sending.Records[]
Purpose, Type, Host (full record name), Value, Required, Verified. DKIM and Return-Path are required; SPF and DMARC are recommended.Receiving.Enabled
Receiving.ReadyToEnable
Receiving.Mode
catch_all or addresses once enabled.Receiving.Records[]
Priority.Forwarding.Address
include:spf.buzzmarkapp.com to it instead of creating a second
one - a domain can only have one. The DKIM value is about 400 characters: most DNS hosts accept it as-is, but
Amazon Route 53 needs it split into quoted strings of up to 255 characters.
Responses
- 201 Created. The full domain, including the records to publish.
- 401 Invalid or missing token.
-
422
Invalid domain name, or the domain is already on your workspace (
ErrorCode 300).
curl https://api.buzzmarkapp.com/api/domains \
-X POST \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "domain": "acme.com" }'
{
"ID": "c6504766-7867-4d5b-8568-0fdabd6e2b3f",
"Name": "acme.com",
"Status": "pending",
"Verified": false,
"VerifiedAt": null,
"LastCheckedAt": null,
"Sending": {
"Ready": false,
"Instructions": "Add these DNS records at your domain registrar ...",
"Records": [
{ "Purpose": "DKIM", "Type": "TXT",
"Host": "buzzmark._domainkey.acme.com",
"Value": "v=DKIM1; k=rsa; p=MIIBIjANBgkq...",
"Required": true, "Verified": false },
{ "Purpose": "Return-Path", "Type": "CNAME",
"Host": "bzm-bounces.acme.com", "Value": "return.buzzmarkapp.com",
"Required": true, "Verified": false },
{ "Purpose": "SPF", "Type": "TXT", "Host": "acme.com",
"Value": "v=spf1 include:spf.buzzmarkapp.com ~all",
"Required": false, "Verified": false },
{ "Purpose": "DMARC", "Type": "TXT", "Host": "_dmarc.acme.com",
"Value": "v=DMARC1; p=none;",
"Required": false, "Verified": false }
]
},
"Receiving": {
"Enabled": false,
"ReadyToEnable": false,
"Mode": null,
"Instructions": "To receive mail on acme.com: publish the MX record ...",
"Records": [
{ "Purpose": "Inbound MX", "Type": "MX", "Host": "acme.com",
"Value": "mxin.buzzmarkapp.com", "Priority": 10, "Required": true }
]
},
"Forwarding": {
"Instructions": "Skip DNS entirely: set your mailbox to forward ...",
"Address": "3f9a1c...@in.buzzmarkapp.com"
}
}
Get a domain
/api/domains/{id}
Auth: Hive token
The domain’s status and all its records - the same body as Add a domain. Each record’s Verified shows whether it’s live.
Path parameters
id
Responses
- 200 The domain.
- 401 Invalid or missing token.
-
404
No such domain on your workspace (
ErrorCode 701).
curl https://api.buzzmarkapp.com/api/domains/c6504766-7867-4d5b-8568-0fdabd6e2b3f \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN"
{ "ErrorCode": 701, "Message": "Unknown domain." }
Verify DNS
/api/domains/{id}/verify
Auth: Hive token
Checks the domain’s DNS now and returns it with updated Status and Verified flags.
Optional - Buzzmark checks hourly anyway. A domain can send once Verified is true.
Path parameters
id
Responses
- 200 The domain, freshly checked.
- 401 Invalid or missing token.
- 404 No such domain.
curl https://api.buzzmarkapp.com/api/domains/c6504766-7867-4d5b-8568-0fdabd6e2b3f/verify \
-X POST \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN"
Enable receiving
/api/domains/{id}/inbound
Auth: Hive token
Routes mail sent to the domain to this token’s hive, which posts it to the hive’s inbound webhook. Needs DKIM verified (it proves you own the domain) and the MX record published. Calling it again replaces the settings. See Inbound email for payloads.
Path parameters
id
Body
mode
catch_all (default) accepts every address; addresses accepts only the ones you list - others are refused when the sender tries.addresses
["support"] for support@. Required with addresses mode.forwards
{"sales": "you@example.com"}. Forwarding to the same domain is refused (it would loop). Forwarded addresses are accepted even in addresses mode.Responses
-
200
The domain with
Receiving.Enabled: true. - 401 Invalid or missing token.
- 404 No such domain.
-
422
DKIM isn’t verified yet, or the body is invalid (
ErrorCode 300).
curl https://api.buzzmarkapp.com/api/domains/c6504766-7867-4d5b-8568-0fdabd6e2b3f/inbound \
-X POST \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "addresses",
"addresses": ["support", "billing"],
"forwards": { "sales": "you@example.com" }
}'
{
"ErrorCode": 300,
"Message": "Verify the domain (DKIM) before enabling inbound."
}
Disable receiving
/api/domains/{id}/inbound
Auth: Hive token
Stops receiving mail on the domain. Sending isn’t affected.
Path parameters
id
Responses
-
200
The domain with
Receiving.Enabled: false. - 401 Invalid or missing token.
- 404 No such domain.
curl https://api.buzzmarkapp.com/api/domains/c6504766-7867-4d5b-8568-0fdabd6e2b3f/inbound \
-X DELETE \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN"
Remove a domain
/api/domains/{id}
Auth: Hive token
Removes the domain. Buzzmark stops signing mail for it, so sends from it will be refused until it’s added and verified again.
Path parameters
id
Responses
- 200 Removed.
- 401 Invalid or missing token.
- 404 No such domain.
curl https://api.buzzmarkapp.com/api/domains/c6504766-7867-4d5b-8568-0fdabd6e2b3f \
-X DELETE \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN"
{ "Deleted": true }
Automating with Terraform
There’s no dedicated provider yet, but the generic Mastercard/restapi provider maps onto
these endpoints: it creates with POST /api/domains, reads with GET /api/domains/{ID}
and destroys with DELETE.
- Feed
buzzmark_recordsinto your DNS provider’s record resource, one record per entry. - Leave out the SPF entry if the domain already has an SPF record, and add the include to that instead.
- To bring an existing domain under Terraform, import it with the id from its dashboard URL - adding it again returns
422. - There’s no update endpoint, so changing the domain name means replacing the resource.
terraform {
required_providers {
restapi = { source = "Mastercard/restapi" }
}
}
provider "restapi" {
uri = "https://api.buzzmarkapp.com"
write_returns_object = true
headers = {
"X-Buzzmark-Server-Token" = var.buzzmark_token
"Content-Type" = "application/json"
}
}
resource "restapi_object" "buzzmark_domain" {
path = "/api/domains"
data = jsonencode({ domain = "acme.com" })
id_attribute = "ID"
}
locals {
# One entry per record: Purpose, Type, Host, Value, Required, Verified
buzzmark_records = jsondecode(
restapi_object.buzzmark_domain.api_response
).Sending.Records
}
Events
Report an event
/api/events
Auth: Hive token
For your own sending infrastructure: report what happened to a message Buzzmark accepted. Buzzmark records it on the message, notifies your webhooks, and adds the recipient to the suppression list for hard bounces and spam complaints. Buzzmark’s own mail servers report automatically - you only need this if you deliver mail yourself.
Body
Type
delivery, bounce or spam_complaint.MessageID
Recipient
To address.Details
Type (HardBounce or SoftBounce - soft bounces don’t suppress) and Description. Passed on in the bounce webhook.Responses
- 200 Recorded.
- 401 Invalid or missing token.
-
404
No such message on this hive (
ErrorCode 701). -
422
Invalid fields (
message/errors).
curl https://api.buzzmarkapp.com/api/events \
-X POST \
-H "X-Buzzmark-Server-Token: $BUZZMARK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Type": "bounce",
"MessageID": "f5ab446c-18ad-4db4-b747-8e1ba70ec72d",
"Recipient": "customer@example.com",
"Details": { "Type": "HardBounce", "Description": "550 No such user" }
}'
{ "Status": "recorded" }
{ "ErrorCode": 701, "Message": "Unknown message for this server." }
Next
- Webhooks - get delivery, bounce, open and click events at your own endpoint.
- SMTP - send through a mail relay instead of the API.
- Inbound email - receive mail as JSON.