Everything you need to send transactional emails via the BejaFly API.
All API requests require an API key sent via the X-API-Key header:
curl -H "X-API-Key: bf_live_your_key_here" https://bejafly.com/api/v1/account
You can also use the Authorization: Bearer header:
curl -H "Authorization: Bearer bf_live_your_key_here" https://bejafly.com/api/v1/account
Generate API keys from your dashboard. Keys are shown once on creation.
POST /api/v1/send
curl -X POST https://bejafly.com/api/v1/send \
-H "X-API-Key: bf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"from": {"email": "hello@yourapp.com", "name": "YourApp"},
"to": {"email": "user@example.com", "name": "Jane"},
"template": "welcome-email",
"variables": {
"name": "Jane",
"login_url": "https://yourapp.com/login"
},
"track_opens": true,
"track_clicks": true
}'
curl -X POST https://bejafly.com/api/v1/send \
-H "X-API-Key: bf_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"from": {"email": "hello@yourapp.com", "name": "YourApp"},
"to": {"email": "user@example.com"},
"subject": "Your receipt",
"html": "<h1>Thanks for your purchase</h1><p>Amount: $49</p>"
}'
{
"success": true,
"message_id": "msg_a1b2c3d4e5f6",
"status": "queued"
}
| Field | Type | Required | Description |
|---|---|---|---|
from.email | string | Yes | Sender email (domain must be verified) |
from.name | string | No | Sender display name |
to.email | string | Yes | Recipient email |
to.name | string | No | Recipient display name |
subject | string | * | Email subject (required if no template) |
template | string | * | Template slug or ID |
variables | object | No | Template variable values |
html | string | * | Raw HTML body (required if no template) |
text | string | No | Plain text fallback |
headers | object | No | Custom X-headers |
track_opens | boolean | No | Enable open tracking (default: true) |
track_clicks | boolean | No | Enable click tracking (default: true) |
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/templates | List all templates |
POST | /api/v1/templates | Create template |
GET | /api/v1/templates/{id} | Get template |
PUT | /api/v1/templates/{id} | Update template |
DELETE | /api/v1/templates/{id} | Delete template |
Templates use {{variable_name}} placeholders that get replaced with values from the variables object in your send request.
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/emails | List emails (filterable) |
GET | /api/v1/emails/{message_id} | Get email + event timeline |
status — Filter by status (queued, delivered, bounced, failed)
to — Filter by recipient email
page, per_page — Pagination (max 100 per page)
Configure webhook endpoints in your dashboard to receive real-time event notifications.
delivered, bounced, opened, clicked, unsubscribed, failed
{
"event": "delivered",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"message_id": "msg_a1b2c3d4e5f6",
"to": "user@example.com",
"subject": "Welcome to YourApp",
"template": "welcome-email"
}
}
Every webhook includes an X-BejaFly-Signature header:
X-BejaFly-Signature: t=1705312200,v1=5257a869...
# Verify in PHP:
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_BEJAFLY_SIGNATURE'];
preg_match('/t=(\d+),v1=(.+)/', $header, $m);
$expected = hash_hmac('sha256', $m[1] . '.' . $payload, $your_webhook_secret);
$valid = hash_equals($expected, $m[2]);
| Code | HTTP | Description |
|---|---|---|
UNAUTHORIZED | 401 | Invalid or missing API key |
RATE_LIMITED | 429 | Too many requests |
VALIDATION_ERROR | 400 | Invalid request parameters |
DOMAIN_NOT_VERIFIED | 400 | Sending domain not verified |
QUOTA_EXCEEDED | 400 | Monthly email quota exceeded |
RECIPIENT_SUPPRESSED | 400 | Recipient is on suppression list |
TEMPLATE_NOT_FOUND | 400 | Template slug/ID not found |
NOT_FOUND | 404 | Resource not found |
$ch = curl_init('https://bejafly.com/api/v1/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: bf_live_your_key',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'from' => ['email' => 'hello@yourapp.com'],
'to' => ['email' => 'user@example.com'],
'template' => 'welcome-email',
'variables' => ['name' => 'Jane'],
]),
CURLOPT_RETURNTRANSFER => true,
]);
$response = json_decode(curl_exec($ch), true);
import requests
r = requests.post('https://bejafly.com/api/v1/send',
headers={'X-API-Key': 'bf_live_your_key'},
json={
'from': {'email': 'hello@yourapp.com'},
'to': {'email': 'user@example.com'},
'template': 'welcome-email',
'variables': {'name': 'Jane'},
}
)
print(r.json())
const res = await fetch('https://bejafly.com/api/v1/send', {
method: 'POST',
headers: {
'X-API-Key': 'bf_live_your_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: { email: 'hello@yourapp.com' },
to: { email: 'user@example.com' },
template: 'welcome-email',
variables: { name: 'Jane' },
}),
});
const data = await res.json();
Rate limits are based on your plan and applied per API key using a token bucket algorithm:
| Plan | Requests/sec | Monthly Emails |
|---|---|---|
| Free | 1 | 100/day |
| Starter | 5 | 2,000 |
| Growth | 20 | 10,000 |
| Scaling | 50 | 50,000 |
| Pro | 100 | 100,000 |
When rate limited, you'll receive a 429 response with a Retry-After header.