Emaratel TE API
Send transactional email over a REST API or standard SMTP, track every delivery event, and receive signed webhooks. Base URL: https://te.emaratel.com
Getting started
- Verify a sending domain. In the customer console open Domains, add your domain, create the DNS records shown at your DNS host, then press Verify DNS. Sending only works from verified domains.
- Create a credential. Under Credentials create a REST API key (for HTTPS) or an SMTP user (for classic apps and devices). The secret is shown once - store it safely.
- Send your first email with the example below, then watch it move through accepted → delivered → opened in the Activity page.
Authentication
Every API request carries your key as a bearer token:
Authorization: Bearer emt_live_xxxxxxxxxxxxxxxx
Keys can be renamed, blocked, rate-limited and pinned to allowed IPs/CIDR ranges from the console. A key used from outside its allowed IPs gets 403 ip_not_allowed.
Send an email
POST/v1/email/send
curl https://te.emaratel.com/v1/email/send \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "orders@yourdomain.com",
"to": ["customer@example.com"],
"subject": "Your order is confirmed",
"text": "Thanks for your order!",
"html": "<p>Thanks for your <b>order</b>!</p>"
}'
Response 202: {"id":"emt_msg_...","status":"queued"}. Keep the id - it identifies the message in every later lookup and webhook.
| Field | Type | Notes |
|---|---|---|
from | string, required | Address on one of your verified domains. |
to | string[], required | 1-100 recipients (account limit may differ). |
subject | string, required | May come from a template instead. |
text / html | string | At least one required (or supplied by a template). |
headers | object | Custom headers. Reserved headers (From, To, Bcc, ...) and CRLF values are rejected. |
metadata | object | Your own key/values, echoed back in lookups. |
template_id | string | Fills any EMPTY subject/text/html from the template. |
substitutions | object | Values for {{placeholders}}. HTML-escaped in the HTML body. |
attachments | array | See below. |
Idempotency-Key header to make retries safe - a repeated key returns the original message id instead of sending twice.Attachments
"attachments": [
{ "filename": "invoice.pdf",
"content_type": "application/pdf",
"content": "JVBERi0xLjQK..." }
]
Up to 10 files per message, base64-encoded, total decoded size capped by your account's message-size limit (default 10 MB). SMTP submissions keep their attachments too - nested multipart is fully supported.
Templates
Create reusable content under Templates in the console or via the API, using {{name}} placeholders:
POST /v1/templates {"name":"welcome","subject":"Hi {{name}}!","html":"<p>Welcome {{name}}</p>"}
GET /v1/templates
PATCH /v1/templates/{id}
DELETE /v1/templates/{id}
Then send with {"template_id":"emt_tpl_...","substitutions":{"name":"Sara"}}. Anything you set explicitly on the send wins over the template.
Messages & events
GET /v1/email?q=&status=&from=YYYY-MM-DD&to=YYYY-MM-DD&limit=50
GET /v1/email/{id} # full message + stored content + event timeline
GET /v1/usage # quota + rate limit + billing state
GET /v1/metrics/summary?days=30
GET /v1/metrics/breakdown # per-sender totals
POST /v1/exports # async CSV export (activity or suppressions)
| Status / event | Meaning |
|---|---|
queued / processing | Accepted by us, on its way out. |
accepted | Handed to the delivery network. |
delivered | The receiving server accepted it. |
opened / clicked | Recipient engagement (when tracking is on). |
bounced | Permanent failure - the address is auto-suppressed. |
complained | Marked as spam - auto-suppressed. |
deferred | Temporary delay; retried automatically for ~17 hours. |
failed | Gave up after all retries; see last_error. |
SMTP relay
Host: smtp.te.emaratel.com
Port: 587 (STARTTLS) or 465 (TLS)
Username: your SMTP user (emt_...)
Password: the secret shown once at creation
Anything that speaks SMTP - frameworks, CRMs, printers, legacy apps - can submit through the relay. The same quotas, suppression checks and event tracking apply as on the API.
Suppressions
GET /v1/suppressions?reason=&q=
POST /v1/suppressions {"email":"user@example.com","reason":"manual"}
DELETE /v1/suppressions/{id}
Hard bounces, complaints and unsubscribes are added automatically; sends to a suppressed address are rejected with 422 recipient_suppressed before anything leaves your quota.
Webhooks
Register an endpoint under Webhooks in the console and pick the event types you want. Each delivery is a POST:
{
"id": "emt_evt_...",
"type": "email.delivered",
"message_id": "emt_msg_...",
"data": { ... },
"occurred_at": "2026-08-30T12:00:00Z"
}
Every request is signed. X-Emaratel-Signature is an HMAC-SHA256 (hex) of timestamp + "." + rawBody with your endpoint secret; X-Emaratel-Timestamp carries the unix timestamp. Verify like this and reject anything older than 5 minutes:
const crypto = require("crypto");
function verify(req, rawBody, secret) {
const ts = req.headers["x-emaratel-timestamp"];
if (Math.abs(Date.now() / 1000 - ts) > 300) return false; // replay guard
const mac = crypto.createHmac("sha256", secret)
.update(ts + "." + rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(mac),
Buffer.from(req.headers["x-emaratel-signature"]));
}
Respond with any 2xx quickly. Failed deliveries retry with backoff for several hours.
Errors & limits
| Code | Meaning |
|---|---|
401 unauthorized | Missing, wrong or revoked key. |
402 payment_required | No active plan - choose one in the console's Billing tab. |
403 ip_not_allowed | Key used outside its allowed IPs. |
422 invalid_message | Missing/invalid from, to, subject or body. |
422 sender_domain_not_verified | The from-domain has not passed DNS verification. |
422 recipient_suppressed | Recipient is on your suppression list. |
429 rate_limit_exceeded | Per-minute limit hit - retry after a short wait. |
429 monthly_quota_exceeded | Plan quota used up for this month. |
Errors are JSON: {"error":"code_here"}. Rate and quota limits are shown live under Usage and on your Overview page.
OpenAPI spec
The full machine-readable API description is at /openapi.yaml - import it into Postman, Insomnia or a code generator.