Skip to content

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.

POST /api/v1/email/send
Authorization: Bearer ef_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"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>"
}
FieldRequiredDescription
fromNoSender address. Required for project-scoped keys when the project has multiple verified senders. Must match a verified sender in the key’s project.
toYesArray of recipient email addresses. Max 50 per request.
subjectYesEmail subject line. Max 998 characters (RFC 2822).
text_bodyYesPlain text version of the email. Always included even if HTML is present.
html_bodyNoHTML version of the email. When provided, Forge sends a multipart/alternative email.
{
"id": "send_a1b2c3d4e5f6",
"status": "sent",
"smtp_message_id": "<abc123@mail.yourdomain.com>",
"duration_ms": 342
}
{
"error": "to must contain at least one valid email address"
}
{
"error": "Invalid or revoked API key"
}
{
"error": "Daily quota exceeded (1000/1000). Resets at midnight UTC.",
"retry_after_seconds": 3600
}
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"
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()
Terminal window
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>"
}'

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.

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
LimitValue
Max recipients per request50
Max email body size256 KB (configurable via FORGE_EMAIL_MAX_BODY_BYTES)
Daily send quota1,000 per identity (configurable)
Rate limitEnforced by daily quota

For sending to more than 50 recipients at once, use Broadcasting.