Bolrach Send API

Transactional and campaign email from your own domains: one key, one POST, delivered from Bolrach's own IPs with your DKIM.

Quick start

Base URL: https://api.bolrach.com/send/v1

Mint a key on the API keys page, add and verify a domain under Domains, then:

CURL
curl -X POST https://api.bolrach.com/send/v1/messages \
  -H "Authorization: Bearer $BOLRACH_SEND_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stream":"transactional","from":{"email":"[email protected]","name":"Example"},"to":"[email protected]","subject":"Your receipt","text":"Thanks for your order."}'
NODE
const r = await fetch("https://api.bolrach.com/send/v1/messages", {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.BOLRACH_SEND_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({
    stream: "transactional",
    from: { email: "[email protected]", name: "Example" },
    to: "[email protected]",
    subject: "Your receipt",
    html: "<p>Thanks for your order.</p>",
    idempotency_key: "order-8123-receipt",
  }),
});
const message = await r.json(); // 202 { accepted, id, status, message_id }

Inside the estate, apps use @empire/send-client: sendMail({ stream, from, to, subject, html }) with SEND_API_URL and SEND_API_KEY in the service environment.

Authentication

Every request carries Authorization: Bearer snd_live_… (or snd_test_… for a sandbox key that records but does not deliver). Keys are scoped per workspace, can be locked to named From domains, and are shown once at creation. Older mtz_live_ keys keep working.

Streams

A message names its stream and the engine routes it accordingly. transactional and security go out from the transactional IP and always flow. subscribed is marketing: only confirmed contacts receive it, List-Unsubscribe headers are added, it leaves from the campaign IP, and it pauses automatically when the workspace's bounce rate passes 15% or complaint rate passes 0.5%. repermission is the one message you may send an unconfirmed contact, asking them to confirm.

Errors and retries

A send answers 202 when accepted. 400 names the field. 401 is a missing or revoked key. 403 is a From domain the key is not allowed to use, or a domain that is not verified. 429 carries Retry-After. Retry 5xx with the same idempotency_key; the original result comes back and nothing sends twice.

Endpoints

POST /send/v1/messages Send a message

Queue one transactional or campaign message. stream is required: transactional, security, subscribed (marketing, confirmed contacts only) or repermission. The From domain must be verified in your workspace and allowed on the key.

Idempotent. Send idempotency_key (or an Idempotency-Key header) and a retry returns the original message instead of sending twice.

curl -X POST https://api.bolrach.com/send/v1/messages \
  -H "Authorization: Bearer snd_live_..." -H "Content-Type: application/json" \
  -d '{
    "stream": "transactional",
    "from": { "email": "[email protected]", "name": "Example" },
    "to": "[email protected]",
    "subject": "Your receipt",
    "html": "<p>Thanks for your order.</p>",
    "idempotency_key": "order-8123-receipt"
  }'

# 202 { "accepted": true, "stream": "transactional", "id": "...", "status": "sent", "message_id": "<[email protected]>" }
GET /send/v1/messages/{id} Message status

Status, timestamps and the delivery route of one message.

POST /send/v1/domains Add a sending domain

Register a domain you control. The answer lists the DNS records to publish: the DKIM selector, the SPF include spf.bolrach.email, and a DMARC suggestion.

{ "domain": "example.com" }
GET /send/v1/domains List domains

Every sending domain in the workspace with its verification state.

GET /send/v1/domains/{domain} One domain

The domain's records and whether SPF and DKIM currently verify.

POST /send/v1/domains/{domain}/verify Verify now

Check the published records straight away instead of waiting for the hourly pass.

POST /send/v1/contacts Add a contact

A contact added through the API or a form is consented at source; an import is unconfirmed until the person confirms.

{ "email": "[email protected]", "consent_source": "api" }
POST /send/v1/contacts/import Import contacts

Bulk import. Imported contacts stay unconfirmed and cannot receive the subscribed stream until they confirm.

{ "contacts": [ { "email": "[email protected]" }, { "email": "[email protected]" } ] }
POST /send/v1/contacts/reconfirm Ask contacts to reconfirm

Send the reconfirmation message to every unconfirmed contact.

GET /send/v1/contacts List contacts

Filter with ?status=confirmed|unconfirmed|unsubscribed.

GET /send/v1/contacts/{email} One contact

Consent state and history for one address.

POST /send/v1/contacts/{email}/unsubscribe Unsubscribe

Records the unsubscribe; the address never receives the subscribed stream again.

POST /send/v1/campaigns Create a campaign

A campaign goes to confirmed contacts only, on the subscribed stream, with List-Unsubscribe headers added for you.

{ "name": "October news", "subject": "What is new", "html": "<p>...</p>", "from_email": "[email protected]" }
GET /send/v1/campaigns List campaigns

GET /send/v1/campaigns/{id} One campaign

Counts of sent and blocked recipients.

POST /send/v1/campaigns/{id}/send Send a campaign

Refused while the workspace's marketing is paused by the reputation gate.

POST /send/v1/templates Create a template

Variables render server-side when a message names template_id and variables.

{ "name": "receipt", "subject": "Receipt {{order}}", "html": "<p>Order {{order}}</p>" }
GET /send/v1/templates List templates

GET /send/v1/templates/{id} One template

PATCH /send/v1/templates/{id} Update a template

DELETE /send/v1/templates/{id} Delete a template

POST /send/v1/webhooks Create a webhook

Events: accepted, sent, delivered, bounced, failed, suppressed. Every delivery is signed with the whsec_ secret returned once at creation.

{ "url": "https://example.com/hooks/send", "events": ["delivered", "bounced"] }
GET /send/v1/webhooks List webhooks

POST /send/v1/webhooks/{id}/test Test a webhook

Delivers a signed test event to the endpoint.

DELETE /send/v1/webhooks/{id} Delete a webhook

POST /send/v1/verify Verify an address

Syntax, MX and free-provider checks for one address before you send to it.

{ "email": "[email protected]" }
POST /send/v1/verify/bulk Verify many

{ "emails": ["[email protected]", "[email protected]"] }
GET /send/v1/analytics/overview Analytics

Sent, delivered, bounced and failed for the workspace, by day.

GET /send/v1/usage Usage

Messages this month against the plan cap.

GET /send/v1/status Sending status

Whether marketing is paused by the reputation gate, and the recent bounce and complaint rates.