Transactional Email API — for developers
Everything you need to integrate the Brixus365 transactional email API into your application — base URL, authentication, errors, rate limits, and idempotency.
For developers. The endpoints documented in this section power the transactional surface of Brixus365 — receipts, password resets, magic links, OTPs, and any other 1:1 email triggered by your app. Marketing campaigns are managed under Spark in the dashboard, not here. In the dashboard, transactional sends and per-message status are surfaced in your dashboard.
Base URL
All API endpoints are relative to:
https://app.brixus365.com/api/v1
All requests and responses use application/json.
Authentication
Pass your API key in the X-API-Key request header on every request.
X-API-Key: bx_live_your_key_here
Missing or invalid keys return 401. See Creating API keys to generate one, or run through the Quickstart for a guided 5-minute setup.
Don’t use Authorization: Bearer for API keys — that header is reserved for dashboard JWTs. Sending an API key in Authorization returns 401 missing_api_key.
Key types
Brixus365 issues two key types depending on how you signed up. Both authenticate the same way (X-API-Key); they differ in capability.
| Key type | Prefix | What it can do |
|---|---|---|
| Preview key | bx_preview_… | Send via starter templates only. Sender is fixed to |
| Live key | bx_live_… | Send via starter templates, your saved templates, or raw HTML. Send from any verified domain. Batch send + SMTP relay supported. Issued by full signup. 9,000/month free, more on paid plans. |
If you try a paid-only feature with a free-tier key, the response is 403 upgrade_required with an upgrade_url field pointing to the pricing page.
Error format
All errors use the same envelope:
{
"error": {
"code": "stable_machine_readable_code",
"message": "Human-readable description.",
"type": "invalid_request_error"
}
}
Some errors include extra fields (upgrade_url, details). Always key on code — the human message may change.
Error types
| Type | Meaning |
|---|---|
| invalid_request_error | Bad input — fix the request and retry |
| authentication_error | Missing or invalid API key |
| authorization_error | Key valid but lacks permission for this action |
| not_found | Resource doesn’t exist or isn’t accessible |
| idempotency_error | Idempotency-Key conflict |
| rate_limit_error | Too many requests — back off and retry |
| api_error | Unexpected server-side error |
| service_unavailable | Email delivery temporarily unreachable |
Common codes
| Status | Code | Description |
|---|---|---|
| 401 | missing_api_key | X-API-Key header not present |
| 401 | invalid_api_key | Key invalid, revoked, or wrong header used |
| 403 | scope_required | Key lacks the permission this endpoint requires — see details.required_permission for the missing scope |
| 403 | no_verified_sender | No sender address marked as Default. Open Settings → Email Setup and set one. |
| 403 | upgrade_required | Feature requires a paid plan — response includes upgrade_url |
| 404 | template_not_found | Unknown starter-template slug, or template ID doesn’t exist on your account |
| 404 | message_not_found | Message ID doesn’t exist or belongs to a different account |
| 422 | validation_failed | Body shape invalid — see error.details.errors[] for the field paths |
| 429 | rate_limit_exceeded | Per-minute send rate exceeded — see error.details.limit_per_minute |
| 429 | daily_limit_exceeded | Daily send quota exhausted |
| 429 | monthly_limit_exceeded | Monthly send quota exhausted |
Rate limits
Limits keep delivery healthy and reputation high.
| Key type | Per minute | Per day | Per month |
|---|---|---|---|
Preview key (bx_preview_…) | 10 | 50 | 1,500 |
Live key — Free plan (bx_live_…) | Plan-based | Plan-based | 9,000 |
| Live key — Standard / Enterprise | Plan-based | Plan-based | 15K – 250K (Standard) · Custom (Enterprise) |
When you exceed the per-minute rate, the response is 429 with code rate_limit_exceeded:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Retry in a few seconds.",
"type": "rate_limit_error",
"details": { "limit_per_minute": 10 }
}
}
Daily and monthly exhaustion return 429 with codes daily_limit_exceeded and monthly_limit_exceeded respectively.
Backoff guidance: for per-minute limits, wait 60 seconds before the next request; for daily limits, wait until UTC midnight. The response does not currently include a Retry-After header — use a fixed-delay or exponential backoff instead.
Idempotency
For POST /v1/emails, pass a unique Idempotency-Key header (≤ 256 bytes) so retries on network errors don’t double-send:
Idempotency-Key: a-unique-string-per-request
Retrying with the same key within 24 hours returns the original response verbatim with Idempotent-Replay: true in the response headers. Reusing the same key with a different request body returns 409 with code idempotency_key_mismatch.
Use a UUID or hash of the payload as your idempotency key — something stable on retry but unique across distinct requests.