The Brixus365 Email API: A Developer's Quick Start (Authentication, Idempotency, Webhooks)
A first-build walkthrough of the Brixus365 transactional email API -- authentication, sending your first email, idempotency keys, error handling, webhooks, and what to watch for in production.
On this page · 11
Transactional email is one of those primitives where the difference between a good API and a bad one shows up in 3am incidents. The bad ones fail silently, swallow retries, and leak deliverability problems. The good ones are boring — they accept your request, give you a clean response, and emit events you can audit.
This guide is a developer’s first-build walkthrough of the Brixus365 transactional email API: authentication, sending your first email, idempotency, error handling, webhooks, templates, and what to watch for moving to production. For backend engineers and SaaS founders evaluating or onboarding the API.
Why a Developer-Friendly Transactional Email API Matters
Transactional email is different from marketing email. A marketing email is a campaign — you batch it, schedule it, measure it across a list. A transactional email is a one-off response to a user action — order confirmation, password reset, security alert — and it must be reliable per-message, low-latency, and traceable to the originating event.
A developer-friendly API for this job has a few traits:
- Predictable authentication. Same header on every request, no token-rotation dance, scoped keys for sandbox and production.
- One endpoint that does the obvious thing.
POST /v1/emailsto send. Not five endpoints, not a “campaign builder” mental model imposed on transactional sends. - First-class idempotency. A retry must not duplicate a send.
- Structured errors. A 4xx tells you what to fix; a 5xx tells you whether to retry.
- Webhooks for everything that matters downstream. Delivered, bounced, complained, opened, clicked.
The rest of this guide walks through each of these on the Brixus365 API specifically.
The Brixus365 API in 60 Seconds
Base URL: https://api.brixus365.com/v1
Authentication: X-API-Key: bx_live_... header on every request
Send an email:
curl https://api.brixus365.com/v1/emails \
-X POST \
-H "X-API-Key: bx_live_xxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7c1a-orders-12345" \
-d '{
"from": "orders@yourdomain.com",
"to": "customer@example.com",
"subject": "Your order is confirmed",
"html": "<p>Hi {{first_name}}, thanks for your order.</p>"
}'
Response:
{
"id": "msg_2cP3kQ...",
"status": "queued",
"accepted_at": "2026-06-11T09:14:22Z"
}
That’s the whole story. The rest of the API is variations on this: templates, attachments, scheduled sends, batch operations.
Authentication: X-API-Key for Developer API, JWT for Dashboard
Brixus365 has two authenticated contexts — the developer API and the dashboard — and they use different headers on purpose.
X-API-Key: bx_live_...— the developer API path. Used for transactional sends, webhooks, template management, contact operations.Authorization: Bearer <JWT>— the dashboard path. Used by the web app and admin tools.
Each header has one job. The dispatch is by header presence, not by sniffing a shared header value. If you accidentally put an API key in Authorization: Bearer, you will get a 401 — the system treats it as a malformed JWT. Likewise for a JWT in X-API-Key. This is intentional: it keeps logs readable and prevents subtle dispatch bugs.
Creating an API key in the dashboard:
- Settings -> Developer -> API Keys -> New Key.
- Choose scope:
sandbox(for development) orlive(for production). - Choose permissions: typically
email:sendis enough for transactional flows. Addtemplate:readif you’ll reference templates. - Copy the key once — it is shown only at creation.
Key prefixes make environment confusion obvious in logs: bx_test_... for sandbox, bx_live_... for production. Reading bx_test_ in production logs is a clear flag.
Rotation. Rotate keys quarterly or whenever a team member leaves. The dashboard supports creating a new key and revoking the old one without downtime: deploy with the new key first, verify, revoke the old.
Sending Your First Email: POST /v1/emails
The minimal request body:
{
"from": "orders@yourdomain.com",
"to": "customer@example.com",
"subject": "Your order is confirmed",
"html": "<p>Hi {{first_name}}, thanks for your order.</p>",
"variables": { "first_name": "Aarav" }
}
Required fields: from, to, subject, and either html or text (or both — for proper plain-text fallback).
A more realistic request:
{
"from": "orders@yourdomain.com",
"from_name": "YourBrand Orders",
"to": "customer@example.com",
"reply_to": "support@yourdomain.com",
"subject": "Your order #12345 is confirmed",
"html": "<p>Hi {{first_name}}...</p>",
"text": "Hi {{first_name}}...",
"variables": { "first_name": "Aarav", "order_number": "12345" },
"tags": ["transactional", "orders", "order_confirmation"],
"headers": { "X-Order-Id": "12345" }
}
The first response:
{
"id": "msg_2cP3kQ9X7tYz",
"status": "queued",
"accepted_at": "2026-06-11T09:14:22Z"
}
status: queued means the message has been accepted into the send queue. status: sent would mean handed off to the upstream relay. The webhook events tell you what happens next.
Required vs Optional Fields
The fields that earn their place:
tags. Arbitrary strings for analytics and segmentation. Tag every transactional send with at least its purpose (order_confirmation,password_reset,security_alert). It costs nothing and the per-tag analytics in the dashboard are useful.headers. Custom RFC-822 headers. Useful for threading (References,In-Reply-To), internal IDs (X-Order-Id), or compliance signals. Note that some headers are reserved (To,From,Subject,List-Unsubscribe) — set those via top-level fields, not via the headers object.reply_to. When the sending address is no-reply and you want replies to go elsewhere.scheduled_at. ISO-8601 datetime. Schedules the send for the future. Useful for time-zoned transactional flows or batched (not one-at-a-time) sends.
The difference between From and Reply-To: From is the sender identity (must be on a verified domain). Reply-To is where replies go. Many transactional flows want from: orders@yourdomain.com (recognisable sender) with reply_to: support@yourdomain.com (where humans actually answer).
Idempotency Keys: Why Every Transactional Send Needs One
The classic bug at scale: your order service POSTs to /v1/emails, the request times out at the network layer, the service retries, and the customer gets two order confirmations. Worse, your accounting system thinks one order generated two receipts.
The fix is an idempotency key: a unique identifier per logical operation. Brixus365 reads it from the Idempotency-Key header.
-H "Idempotency-Key: orders-12345-confirmation"
If you POST the same idempotency key twice within the TTL window, the second call returns the same response as the first (same message ID, same accepted_at) and does not re-send the email.
Constructing the key. A natural pattern is {flow}-{entity_id}-{event}:
orders-12345-confirmationaccounts-67890-welcomepasswords-abc-reset-2026-06-11T09:14:22Z
The point is that the key is deterministic from the originating event. If your retry logic constructs the same key, the duplicate is caught.
TTL. The idempotency cache lives for 24 hours by default. After that, the same key with the same payload becomes a new send. This is rarely an issue for transactional flows where retries happen within minutes.
Handling Errors: The Response Envelope and Retry Policy
All Brixus365 API errors return a unified envelope:
{
"error": {
"code": "invalid_recipient",
"message": "The 'to' field is not a valid email address",
"type": "validation_error",
"field": "to"
}
}
type is the error category — useful for routing in your client code. Common types:
validation_error(4xx): a field in your request is wrong. Fix and retry.authentication_error(401): your API key is invalid, expired, or missing. Investigate.permission_error(403): your API key is valid but lacks the required permission scope.rate_limit_error(429): you have exceeded your rate limit. Back off and retry.not_found_error(404): the resource referenced does not exist (e.g., template ID, contact ID).internal_error(5xx): a transient or permanent problem on our side. Retry with backoff.
Retry policy for transactional sends:
- 4xx errors (except 429): do not retry. The request is wrong; retrying will not fix it.
- 429: retry with exponential backoff. The
Retry-Afterheader tells you the minimum wait. - 5xx: retry with exponential backoff up to about 5 attempts. Past that, alert and stop.
A reasonable backoff schedule for transactional retries: 1s, 4s, 16s, 60s, 240s. Combined with the idempotency key, retries are safe.
Webhooks: Subscribing to Delivery, Bounce, Complaint, Open, Click
The send response (status: queued) tells you the message was accepted. Webhooks tell you what happened next.
Setting up a webhook destination:
- Settings -> Developer -> Webhooks -> New Endpoint.
- Provide the URL (must be HTTPS).
- Select the events to subscribe to.
- Copy the signing secret.
Event types:
email.delivered— accepted by the recipient’s mail server.email.bounced— rejected (with hard/soft sub-classification).email.complained— recipient marked as spam.email.opened— recipient opened (subject to Apple MPP caveats).email.clicked— recipient clicked a link.
The payload:
{
"event": "email.delivered",
"message_id": "msg_2cP3kQ9X7tYz",
"occurred_at": "2026-06-11T09:14:48Z",
"to": "customer@example.com",
"tags": ["transactional", "orders", "order_confirmation"],
"metadata": { "X-Order-Id": "12345" }
}
Signature verification. Every webhook request includes an X-Brixus365-Signature header containing an HMAC-SHA256 of the payload, signed with your endpoint’s signing secret. Verify on every request — accepting unsigned or wrongly-signed payloads is how attackers exfiltrate data.
Idempotency on receipt. Webhooks can be delivered more than once (at-least-once semantics). Your handler must be idempotent on (message_id, event). The simplest pattern: store processed events in a small table keyed by that tuple.
Templates and Dynamic Fields: API vs Dashboard
For transactional flows with stable content, store the template in the dashboard once and reference it by ID:
{
"from": "orders@yourdomain.com",
"to": "customer@example.com",
"template_id": "tpl_order_confirmation_v3",
"variables": {
"first_name": "Aarav",
"order_number": "12345",
"order_total": "1180.00"
}
}
Why this beats inline HTML:
- Designers can edit templates without redeploying code.
- Template versioning is built in (each save is a version; you can roll back).
- Per-template analytics — see CTR and delivery rate by template.
- Compliance: a single approved template prevents drift across services.
Inline HTML is still right for one-off or dynamic content (transactional content that varies wildly per send). The two patterns coexist.
Merge tag syntax: {{ variable_name }}. Brixus365 uses a safe subset of Liquid for templates — fully sandboxed, no arbitrary code execution.
From First API Call to Production
A short pre-launch checklist before you flip a transactional flow to production:
- Verify your sending domain. Add the verification, DKIM, and SPF records. Set DMARC at
p=noneinitially. - Set up the suppression list. Brixus365 manages this automatically — every bounce and complaint goes to suppression. Verify suppressed addresses are excluded from future sends.
- Subscribe to webhooks for delivered, bounced, complained at minimum. Add open and click if you care about engagement signals.
- Implement idempotency keys on every transactional send.
- Set up monitoring. Send-success rate, webhook-receive rate, and 429 frequency are the three to alert on.
- Verify rate limits for your plan. The default for new accounts is 60 requests per second; higher tiers raise this.
- Move to a
liveAPI key for production. Sandbox keys (bx_test_) accept all requests but do not actually send mail — they are for development.
Then ship. Transactional email is one of those rare integration surfaces where you can move fast if the primitives are right.
Key takeaways
- Always send an Idempotency-Key on every transactional send. It is the cheapest defence against duplicate emails in a retry storm — and retry storms happen.
- Subscribe to webhook events from day one (delivered, bounced, complained). They are the source of truth for downstream automation, not the synchronous response of POST
/v1/emails. - Separate sandbox and production API keys, and rotate keys quarterly. A leaked key is a deliverability incident — design key handling like you would database credentials.
FAQ
How do I authenticate requests to the Brixus365 email API?
X-API-Key header (keys look like bx_live_…). Keep it server-side and load it from an environment variable — never expose it in client-side code.Is the Brixus365 email API free?
Is there an SDK, or do I call the REST API directly?
How do I make retries safe?
How do I track delivery, opens, and bounces?
If you want to start sending, create a free account — 9,000 emails free per month, no credit card. The full API reference is at docs.brixus365.com.
Send smarter emails with Brixus365
Campaigns and transactional API on one engine. 9,000 emails/month free, no credit card.