Transactional Email
The transactional send endpoint is the core of Forge Email. Your app calls it whenever it needs to send an email — signup confirmations, password resets, receipts, alerts, or any one-off notification.
Endpoint
Section titled “Endpoint”POST /api/v1/email/sendAuthorization: Bearer ef_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxContent-Type: application/jsonRequest Body
Section titled “Request Body”{ "from": "noreply@yourdomain.com", "to": ["user@example.com"], "subject": "Welcome to My App!", "text_body": "Hello! Thanks for signing up.\n\n— The Team", "html_body": "<h1>Welcome!</h1><p>Thanks for signing up.</p><p>— The Team</p>"}| Field | Required | Description |
|---|---|---|
from | No | Sender address. Required for project-scoped keys when the project has multiple verified senders. Must match a verified sender in the key’s project. |
to | Yes | Array of recipient email addresses. Max 50 per request. |
subject | Yes | Email subject line. Max 998 characters (RFC 2822). |
text_body | Yes | Plain text version of the email. Always included even if HTML is present. |
html_body | No | HTML version of the email. When provided, Forge sends a multipart/alternative email. |
Responses
Section titled “Responses”Success (201)
Section titled “Success (201)”{ "id": "send_a1b2c3d4e5f6", "status": "sent", "smtp_message_id": "<abc123@mail.yourdomain.com>", "duration_ms": 342}Validation Error (400)
Section titled “Validation Error (400)”{ "error": "to must contain at least one valid email address"}Unauthorized (401)
Section titled “Unauthorized (401)”{ "error": "Invalid or revoked API key"}Quota Exceeded (429)
Section titled “Quota Exceeded (429)”{ "error": "Daily quota exceeded (1000/1000). Resets at midnight UTC.", "retry_after_seconds": 3600}Example: Node.js
Section titled “Example: Node.js”const response = await fetch("https://forge.yourdomain.com/api/v1/email/send", { method: "POST", headers: { "Authorization": `Bearer ${process.env.FORGE_EMAIL_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ from: "noreply@yourdomain.com", to: ["user@example.com"], subject: "Welcome!", text_body: "Thanks for signing up.", html_body: "<h1>Welcome!</h1><p>Thanks for signing up.</p>" })});
const result = await response.json();console.log(result.status); // "sent"Example: Python
Section titled “Example: Python”import httpx
async def send_welcome_email(to_email: str, name: str): async with httpx.AsyncClient() as client: response = await client.post( "https://forge.yourdomain.com/api/v1/email/send", headers={ "Authorization": f"Bearer {FORGE_EMAIL_API_KEY}", "Content-Type": "application/json" }, json={ "from": "noreply@yourdomain.com", "to": [to_email], "subject": f"Welcome to My App, {name}!", "text_body": f"Hey {name},\n\nThanks for signing up!", "html_body": f"<h1>Welcome {name}!</h1><p>Thanks for signing up.</p>" } ) return response.json()Example: curl
Section titled “Example: curl”curl -X POST https://forge.yourdomain.com/api/v1/email/send \ -H "Authorization: Bearer ef_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "from": "billing@yourdomain.com", "to": ["alice@example.com"], "subject": "Your receipt", "text_body": "Payment received: $12.34\nThank you!", "html_body": "<h1>Receipt</h1><p>Payment received: <strong>$12.34</strong></p>" }'From Address
Section titled “From Address”Sender-scoped keys always send from their selected identity. If you include from, it must match that sender.
Project-scoped keys can send from any verified sender identity in the project. Include from when the project has multiple verified senders:
My App <noreply@mail.yourdomain.com>If a project-scoped key is used without from, Forge only chooses a sender automatically when the project has exactly one verified sender. Custom from addresses within a verified domain are supported by creating additional sender identities.
Delivery & Logging
Section titled “Delivery & Logging”Every send is logged to the Send History tab in the dashboard:
- Recipient, subject, timestamp
- Status (sent / failed)
- SMTP message ID
- Duration in milliseconds
- Error details if failed
Logs are retained for the configured FORGE_EMAIL_LOG_RETENTION_DAYS (default 90 days). Failed sends include the SMTP error for debugging:
"Authentication failed"— check SMTP username/password"Connection refused"— SMTP host or port is wrong"Connection timed out"— firewall blocking the port"Message rejected as spam"— improve SPF/DKIM/DMARC setup
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Max recipients per request | 50 |
| Max email body size | 256 KB (configurable via FORGE_EMAIL_MAX_BODY_BYTES) |
| Daily send quota | 1,000 per identity (configurable) |
| Rate limit | Enforced by daily quota |
For sending to more than 50 recipients at once, use Broadcasting.