Bizmitra Notify API
One unified HTTP API for transactional messaging across every channel — like Resend, but channel-agnostic. Send through the same endpoint, swap providers without touching your code, and track delivery end to end.
email channel is live via
the Brevo adapter.
SMS, WhatsApp and Push are on the roadmap and will plug into the same
/messages/send endpoint — only the
channel value changes.
Base URL
All endpoints are served under the /api prefix.
https://your-domain.com/api
Authentication
Authenticate with a tenant-scoped API key via the
Authorization header using the Bearer scheme. Generate keys
from the Developer Dashboard → API Keys.
Authorization: Bearer nk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Quickstart
Send your first email in under a minute:
curl -X POST https://your-domain.com/api/messages/send \
-H "Authorization: Bearer nk_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"channel": "email",
"to": "jane@example.com",
"subject": "Welcome to Acme",
"event_key": "user_welcome",
"transaction_id": "ord_8421",
"data": {
"first_name": "Jane",
"activation_url": "https://acme.com/activate/abc123"
}
}'
You'll get back a queued message ID you can correlate with webhook callbacks:
{
"message_id": "01HZX7K...",
"status": "queued"
}
Channels & Providers
A channel is the medium (email, SMS, WhatsApp, push). A provider is the underlying vendor that actually delivers the message. Notify abstracts the two so you can route, fail over and price independently.
"channel": "email"
"channel": "whatsapp"
"channel": "sms"
"channel": "push"
Templates & Events
Every send references a server-side template via
event_key. Templates are created from the
Developer Dashboard and bound to a channel and
tenant. The runtime resolves event_key +
channel + is_active=true and
renders the body with the values in data.
test_email template (when present) so your integration
never crashes silently during onboarding.
Idempotency
Pass a unique transaction_id on every request. Notify
derives a business-event ID from it ({transaction_id}_{action}),
so safe retries from your side will not double-send. Reuse the same
transaction_id when retrying a failed network call.
Rate Limits & Quotas
| Limit | Default | Response |
|---|---|---|
| Per-tenant burst | 100 requests / minute | 429 Rate limit exceeded |
| Sustained tenant rate | 20 requests / minute | 429 Too many requests in the last minute |
| Daily send quota | 100 messages / day / tenant | 429 Daily quota exceeded |
| Event ingestion | 100 requests / minute | 429 |
| Provider webhooks | 300 requests / minute | 429 |
Defaults shown — contact us for higher tiers.
Send a Message
The single endpoint you use for every channel. Today only email is routable.
Headers
| Name | Required | Description |
|---|---|---|
Authorization | Required | Bearer token — your tenant API key |
Content-Type | Required | application/json |
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
channel | string | Required | Currently "email". More channels coming. |
to | string | Required | Recipient address (must be a valid email for the email channel) |
subject | string | Required | Subject line for email channel |
event_key | string | Required | Name of the template to render (must exist for your tenant + channel) |
transaction_id | string | Required | Your unique ID for this send — used for idempotency and reconciliation |
data | object | Optional | Key/value map merged into the template variables |
action | string | Optional | Logical action (default: "send"). Appended to the business-event ID for idempotency scoping. |
Example Request
POST /api/messages/send HTTP/1.1
Host: your-domain.com
Authorization: Bearer nk_live_xxxxxxxx
Content-Type: application/json
{
"channel": "email",
"to": "customer@example.com",
"subject": "Your order has shipped",
"event_key": "order_shipped",
"transaction_id": "ord_8421",
"data": {
"order_id": "8421",
"tracking_url": "https://track.acme.com/8421"
}
}
Success Response 200 OK
{
"message_id": "01HZX7K2A0Q8VVQH...",
"status": "queued"
}
Error Responses
| Status | Code path | Meaning |
|---|---|---|
401 | Missing / Invalid API Key | Authorization header absent or key not found |
403 | Tenant inactive | API key resolved but the tenant is suspended |
422 | Invalid template | No active template matched event_key + channel |
429 | Rate limit / quota | See Rate Limits |
500 | Dispatch failed | Provider error — safe to retry with the same transaction_id |
Ingest a Business Event
Forward business events from your apps (ERP, billing, CRM) so Notify can react with the
correct template + channel. Events are deduplicated by
event_id and processed asynchronously.
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
event_id | string ≤ 64 | Required | Unique ID — repeats return {"status":"duplicate"} |
event_name | string ≤ 100 | Required | e.g. order.shipped |
source | string ≤ 50 | Required | Originating system, e.g. erp |
org_id | integer | Required | Tenant org ID |
actor.type | string ≤ 20 | Required | e.g. user, system |
actor.id | integer | Optional | Actor identifier in your system |
data | object | Required | Free-form event payload |
occurred_at | ISO 8601 date | Required | When the event happened in source-of-truth |
Response
{ "status": "accepted" }
Provider Webhooks (inbound)
Endpoint Notify exposes for providers to call. Configure this URL in your
provider dashboard (e.g. Brevo → Transactional → Webhooks) and Notify will normalize the
payload into a unified delivery-status lifecycle. Signatures are verified per provider —
unsigned or invalid requests get 403.
Supported {provider} values today: brevo.
Health Check
Unauthenticated liveness probe — safe for uptime monitors and load balancers.
{ "status": "ok", "service": "bizmitra-notify" }
Errors
Errors use standard HTTP status codes with a JSON body:
{ "error": "Invalid template" }
For validation failures, Laravel's default 422 shape applies with a message and errors map keyed by field.
Delivery Status Lifecycle
Once dispatched, a message moves through these normalized states (the same vocabulary regardless of provider):
| Status | Meaning |
|---|---|
queued | Accepted by Notify, not yet handed to a provider |
sent | Provider accepted the message for delivery |
delivered | Recipient mailbox / device received it |
opened | Recipient opened the message (channel-dependent) |
clicked | A tracked link was clicked |
bounced | Hard or soft bounce reported by the provider |
failed | Permanent failure — see provider response logged on the message |
Changelog
v1 · Initial release
- Unified
/messages/sendendpoint - Email channel live via Brevo adapter
- Tenant-scoped API keys with prefix lookup and SHA-256 hashing
- Provider webhook normalization (Brevo)
- Async event ingestion with idempotency