API reference

Domains and suppressions

Sending domains and the suppression list

Domains

A space sends only from domains it has verified through DNS. Set up your domain explains each record.

The domain object

{
  "id": "6e7f8a9b-0c1d-4e2f-3a4b-5c6d7e8f9a0b",
  "domain": "acme.com",
  "status": "VERIFIED",
  "checks": { "dkim": "OK", "mailFrom": "OK", "dmarc": "OK", "tracking": "OK" },
  "records": [
    { "purpose": "DKIM", "type": "CNAME", "name": "abc123._domainkey.acme.com", "value": "abc123.dkim.amazonses.com", "priority": null, "status": "OK", "optional": false },
    { "purpose": "DKIM", "type": "CNAME", "name": "def456._domainkey.acme.com", "value": "def456.dkim.amazonses.com", "priority": null, "status": "OK", "optional": false },
    { "purpose": "DKIM", "type": "CNAME", "name": "ghi789._domainkey.acme.com", "value": "ghi789.dkim.amazonses.com", "priority": null, "status": "OK", "optional": false },
    { "purpose": "MAIL_FROM", "type": "MX", "name": "bounce.acme.com", "value": "feedback-smtp.eu-west-3.amazonses.com", "priority": 10, "status": "OK", "optional": false },
    { "purpose": "MAIL_FROM", "type": "TXT", "name": "bounce.acme.com", "value": "v=spf1 include:amazonses.com ~all", "priority": null, "status": "OK", "optional": false },
    { "purpose": "DMARC", "type": "TXT", "name": "_dmarc.acme.com", "value": "v=DMARC1; p=none;", "priority": null, "status": "OK", "optional": false },
    { "purpose": "TRACKING", "type": "CNAME", "name": "link.acme.com", "value": "track.kulamail.com", "priority": null, "status": "OK", "optional": false },
    { "purpose": "GOOGLE_POSTMASTER", "type": "TXT", "name": "acme.com", "value": "…", "priority": null, "status": "PENDING", "optional": true }
  ],
  "trackingDomain": "link.acme.com",
  "mailFromDomain": "bounce.acme.com",
  "verifiedAt": "2026-10-17T09:42:00Z",
  "lastCheckedAt": "2026-10-17T09:42:00Z",
  "nextCheckAt": "2026-10-18T09:42:00Z",
  "lastError": null,
  "ramp": { "stage": 0, "dailyCap": 1050, "sentToday": 0, "uncapped": false },
  "postmasterStatus": "PENDING",
  "postmasterError": null,
  "createdAt": "2026-10-17T09:30:00Z"
}
FieldMeaning
statusPENDING (checks in progress), VERIFIED (all four checks pass: the domain can send), FAILED (checks gave up after 7 days; verify again once fixed)
checksEach check is PENDING, OK or FAILED
recordsWhat to publish at your DNS provider. name is fully qualified. The optional GOOGLE_POSTMASTER record lets Kula read Gmail's spam rate and never blocks sending
rampThe warm-up: today's marketing cap for this domain and how much of it is used

List domains

GET /v1/domains

Every domain of the space, oldest first.

Add a domain

POST /v1/domains

Adds a domain and returns the records to publish.

domain
string required
The domain you send from, such as acme.com or mail.acme.com. Public mailbox domains (gmail.com, outlook.com…) are refused.
curl https://api.kulamail.com/v1/domains \
  -H "Authorization: Bearer ml_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "acme.com" }'

Returns 201 with the domain object, status: "PENDING".

StatusCodeWhen
400invalid_domainNot a valid domain name
400public_mailbox_domainA public mailbox provider such as gmail.com
409domain_takenThe domain is already added to this space or used by another one

Kula checks the records automatically: every 15 minutes for the first 3 hours, then hourly for a day, then every 6 hours up to 7 days. A verified domain is checked again every day; if a record disappears, sending from it stops until it is back.

Get a domain

GET /v1/domains/{id}

Verify a domain

POST /v1/domains/{id}/verify

Checks the records now, for example right after publishing them. Returns the domain with the result.

curl -X POST https://api.kulamail.com/v1/domains/6e7f8a9b-0c1d-4e2f-3a4b-5c6d7e8f9a0b/verify \
  -H "Authorization: Bearer ml_your_api_key"

Delete a domain

DELETE /v1/domains/{id}

Answers 204. Emails can no longer be sent from this domain.

Suppressions

Addresses Kula won't email. Bounces and suppressions explained.

ReasonAdded whenBlocks
HARD_BOUNCEThe address does not existEvery email, from every space (global: true). Cannot be removed
SOFT_BOUNCE3 failed deliveries in a rowMarketing from this space
COMPLAINTThe person marked an email as spamMarketing from this space
MANUALYou added itMarketing from this space

Sending a transactional email to a suppressed address answers status: "SUPPRESSED" and sends nothing.

The suppression object

{
  "id": "8a9b0c1d-2e3f-4a5b-6c7d-8e9f0a1b2c3d",
  "email": "old-address@example.com",
  "reason": "HARD_BOUNCE",
  "detail": "550 5.1.1 The email account that you tried to reach does not exist",
  "global": true,
  "sourceMessageId": "8f6c2a1e-3b4d-4e5f-9a0b-1c2d3e4f5a6b",
  "createdAt": "2026-10-12T16:20:00Z"
}

List suppressions

GET /v1/suppressions

Newest first.

search
string
Part of an address. With a complete address, its global hard bounce comes first.
page
integer
Page number, from 0.
size
integer
50 by default, 100 at most.
200 OK
{
  "items": [ { "id": "8a9b0c1d-…", "email": "old-address@example.com", "reason": "HARD_BOUNCE", "…": "…" } ],
  "page": 0,
  "size": 50,
  "total": 37
}

Suppress an address

POST /v1/suppressions

Adds a MANUAL suppression: the address receives no more marketing from this space, for example after an opt-out received outside Kula.

email
string required
The address to suppress.
curl https://api.kulamail.com/v1/suppressions \
  -H "Authorization: Bearer ml_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@example.com" }'

Returns 201 with the suppression object.

Remove a suppression

DELETE /v1/suppressions/{id}

Answers 204. A global hard bounce cannot be removed. Remove a complaint only if the person explicitly asked to receive your emails again.