BitelioBitelio
API Reference

Send transactional email

Send a transactional email via the public API. Automatically creates or updates the recipient contact.

Required content: either a template ID, or both subject and body. Template fields can be overridden by explicit request fields.

Sender: from is required unless using a template that already has a from configured. The sender's domain must be verified.

Multiple recipients: when to is an array, each recipient is processed sequentially with its own contact upsert and rendered email — there is no batch-send semantics. Sending is always immediate; for scheduled sends, use a Campaign.

Attachments: up to 10 attachments per email and 10 MB total by default. The total message size cannot exceed 40 MB.

POST
/v1/send

Authorization

ApiKeyAuth

AuthorizationBearer <token>

API Key authentication. Secret keys (sk_*) are required for all endpoints except /v1/track. Public keys (pk_*) only work with the /v1/track endpoint for client-side event tracking. The project is automatically derived from the key.

In: header

Header Parameters

Idempotency-Key?string

Optional key that guarantees this request runs at most once. If the key was already used by your project, the request is refused with 409 instead of being performed a second time. Keys are scoped to your project, expire after 24 hours (configurable when self-hosting), and must be 1-255 printable ASCII characters.

Lengthlength <= 255

Request Body

application/json

to*string||

Recipient email(s). Can be a string, an object with {name, email}, or an array of either.

subject?string

Email subject. Required if no template is provided. Cannot contain newline characters.

Length1 <= length <= 998
body?string

Email body (HTML). Required if no template is provided.

Length1 <= length
template?string

Template ID to use for this email. When provided, uses the template's subject, body, from, and reply-to settings. You can override these by explicitly providing subject, body, from, or reply fields in the request. Template variables are populated from the data field.

from?string|

Sender email address (requires verified domain). Required unless using a template that has a 'from' address configured. Can be a string (e.g., 'hello@example.com') or an object with {name, email} (e.g., {name: 'My App', email: 'hello@example.com'}).

name?string

Deprecated. Sender display name. Prefer from: { name, email }. Used only as a fallback when from is a string and no name is set there.

subscribed?boolean

Subscription state to apply to the recipient. For new contacts, defaults to false on /v1/send. For existing contacts, omitting this preserves their current state — pass true or false to explicitly change it. A change emits contact.subscribed or contact.unsubscribed.

data?

Variables for template rendering and contact data updates. Each value can be:

  • A primitive (string, number, boolean) — saved on the contact and available as a template variable.
  • null — deletes the field from the contact.
  • An empty string — skipped (does not overwrite existing data).
  • An object { value, persistent: false } — used for this send only, not stored on the contact (good for one-shot password reset codes, magic links).

Reserved keys (id, bitelio_id, bitelio_email, email, unsubscribeUrl, subscribeUrl, manageUrl) are silently filtered out.

headers?

Custom email headers. Header names cannot contain \r\n. Header values are limited to 998 characters and cannot contain \r\n (header injection is rejected).

reply?string

Reply-to address.

Formatemail
attachments?

Email attachments. Default cap: 10 attachments and 10 MB total. The full message size cannot exceed 40 MB.

Itemsitems <= 10

Response Body

application/json

application/json

application/json

application/json

curl -X POST "https://api.bitelio.com/v1/send" \  -H "Content-Type: application/json" \  -d '{    "to": "user@example.com",    "subject": "Password Reset Request",    "body": "<h1>Reset Your Password</h1><p>Click the link to reset: {{resetLink}}</p>",    "data": {      "resetLink": "https://example.com/reset/abc123"    }  }'
{
  "success": true,
  "data": {
    "emails": [
      {
        "contact": {
          "id": "cnt_abc123",
          "email": "user@example.com"
        },
        "email": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf"
      }
    ],
    "timestamp": "2025-01-15T10:30:00.000Z"
  }
}
{
  "code": 0,
  "error": "string",
  "message": "string",
  "time": 0
}
{
  "code": 0,
  "error": "string",
  "message": "string",
  "time": 0
}
{
  "success": false,
  "error": {
    "code": "IDEMPOTENCY_KEY_REUSED",
    "message": "Idempotency-Key \"order-1234-receipt\" has already been used",
    "statusCode": 409,
    "requestId": "8f14e45f-ceea-467a-9575-1f0f38e0b1c2",
    "details": {
      "key": "order-1234-receipt",
      "originalRequest": "POST /v1/send",
      "originalRequestAt": "2025-01-15T10:30:00.000Z",
      "originalStatusCode": 200
    },
    "suggestion": "This Idempotency-Key was already used, so the request was refused rather than performed twice. Generate a new key for a genuinely new request."
  },
  "timestamp": "2025-01-15T10:31:00.000Z"
}