Send an email
Send a single email, transactional or one-off, through the API. The message is accepted and queued for delivery.
POST https://app.blacklistguard.com/api/v1/emails/send
Authenticate with your X-API-Key header and send a JSON body. The key needs the emails:send permission.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
from_email | string | Yes | Sender address. Must be on a verified sending domain, or be a verified sender. |
to | string | Yes | Recipient email address. One plain address, with no display name and no list of addresses. One message per request. |
subject | string | Yes | Subject line. |
body | string | No | Message content. A message without one is sent empty. It is sent as HTML when it contains both a < and a >, and as plain text otherwise. |
from_name | string | No | Display name shown next to the sender address. |
bounce_email | string | No | Bounce address. Leave it out and BlacklistGuard handles bounces for you. An address on one of your incoming domains works the same way: bounces still come back to us and are processed. An address on any other domain is used exactly as you give it, so bounces go straight to that address. |
virtual_mta_ip | string | No | Which IP the message is sent from. Leave it out to use your default (your dedicated IP if you have one, otherwise the shared pool). See Choosing the sending IP. |
attachments | array | No | File attachments. See below. |
A request without from_email, to or subject is rejected with a 400 and nothing is queued. Fields the endpoint does not know are ignored.
Choosing the sending IP
Leave virtual_mta_ip out and the message uses your default: your own dedicated IP if you have one, otherwise BlacklistGuard's shared pool. Set it to choose where the message is sent from instead. It takes the same values as the campaign field of the same name:
- An empty string, or no field at all, for your default.
"shared_pool"to always use the shared pool, even if you have a dedicated IP.- An IP address, such as
"203.0.113.10". It must be one of your own verified dedicated IPs, including any you've bought from us. You'll find them under Virtual MTAs in the console. "routing_rule:<id>"to send through one of your active routing rules.
Your choice is kept with the message, so it still applies if the message has to wait in the queue. If the IP stops being yours before the message goes out, for example because you released a dedicated IP, it uses your default instead.
Attachment fields
| Field | Required | Description |
|---|---|---|
filename | Yes | File name shown to the recipient. |
content | Yes | File contents, Base64-encoded. |
content_type | No | MIME type, e.g. application/pdf. Defaults to application/octet-stream. |
disposition | No | attachment (default) or inline for embedded images. |
content_id | No | For inline images. Reference it in your HTML as cid:<content_id>. |
filename, no content, or content that is not valid Base64 is left out. The request still succeeds and the message goes out without it, so check your encoding.cc, bcc, reply-to, custom headers, scheduling, multiple recipients in one call, separate HTML/text parts, or templates. For audience sends, use campaigns.Example request
curl -X POST https://app.blacklistguard.com/api/v1/emails/send \
-H "X-API-Key: efk_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from_email": "you@yourdomain.com",
"from_name": "Acme",
"to": "customer@example.com",
"subject": "Your receipt",
"body": "<h1>Thanks!</h1><p>Your order is confirmed.</p>",
"attachments": [
{ "filename": "receipt.pdf", "content_type": "application/pdf", "content": "JVBERi0xLjQK..." }
]
}'
Response
A successful request returns 201 with the queued message's ID:
{
"success": true,
"data": { "status": "queued", "id": "1a3a56c6-4393-465e-86aa-d2c0d1200958" },
"message": "Resource created successfully"
}
Use data.id to find the message in the console under Messages, or through the reporting API.
A message to an address your workspace has suppressed is accepted like any other, but it isn't delivered. Its status becomes skipped, with the reason address is suppressed.
A 201 means the message is queued, not delivered. The platform makes one delivery attempt. A message that fails is not sent again automatically, so check its status and send it again from your side if you need to.
Errors
| Status | Code | When |
|---|---|---|
400 | BAD_REQUEST | from_email is missing or blank. The message is from_email is required. |
400 | BAD_REQUEST | from_email is not a valid address. The message is Invalid from_email. |
400 | BAD_REQUEST | from_email is not on a verified sending domain and is not a verified sender. The message tells you to verify the domain or the address. |
400 | BAD_REQUEST | to is missing or blank. The message is to is required. |
400 | BAD_REQUEST | to is not one plain email address. The message is Invalid to: send one email address. |
400 | BAD_REQUEST | subject is missing or blank. The message is subject is required. |
400 | BAD_REQUEST | virtual_mta_ip is an IP that isn't one of your own verified dedicated IPs. The message is Sending IP must be one of your own verified dedicated IPs; shared-pool IPs cannot be selected directly (use "shared_pool" for the shared pool). |
400 | BAD_REQUEST | virtual_mta_ip names a routing rule with a malformed ID. The message is Invalid routing rule ID. |
400 | BAD_REQUEST | virtual_mta_ip names a routing rule that doesn't exist or isn't yours. The message is Routing rule not found or not owned by this tenant. |
400 | BAD_REQUEST | The body is not valid JSON, or a field has the wrong type. The message starts with Invalid JSON:. |
402 | INSUFFICIENT_BALANCE | Your account balance has run out. Nothing is queued, and the message explains why. |
402 | PLAN_LIMIT_REACHED | Your plan's sending allowance is used up. Nothing is queued. |
402 | SERVICE_SUSPENDED | Sending is suspended because a payment is overdue. Nothing is queued. |
429 | RATE_LIMIT_EXCEEDED | Your workspace has reached its hourly, daily or monthly sending limit. Nothing is queued, and the message says which. The daily limit resets at 00:00 UTC. |
403 | SENDING_PAUSED | Sending is paused for your account. Nothing is queued. The message is Sending is paused for this account. Contact support to have it reviewed. |
403 | INSUFFICIENT_SCOPE | The API key does not have the emails:send permission. |
403 | FEATURE_NOT_AVAILABLE | Your plan does not include API access. |
See the error codes for the full list, including plan-limit and balance errors.