Sending email
There are three ways to send mail through Koltrix:
- REST API —
POST /api/v2/emails, JSON, idempotent. - SMTP relay — port 2525, AUTH PLAIN with your API key as password.
- Campaigns — broadcast to a list from the dashboard or via the API.
All three go through the same pipeline, so bounces, suppression, opens, clicks, and webhooks behave identically.
REST
curl https://api.koltrix.com/api/v2/emails \
-H "Authorization: Bearer kx_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"from": "[email protected]",
"to": ["[email protected]"],
"cc": ["[email protected]"],
"subject": "Hello",
"body_html": "<p>Hi</p>",
"body_text": "Hi"
}'| Header | Notes |
|---|---|
Authorization | Required. Bearer kx_... |
Idempotency-Key | Recommended. Any unique string per logical send. Cached 24h per key. |
Content-Type | application/json |
Node (fetch)
import { randomUUID } from "node:crypto";
await fetch("https://api.koltrix.com/api/v2/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KOLTRIX_KEY!}`,
"Content-Type": "application/json",
"Idempotency-Key": randomUUID(),
},
body: JSON.stringify({
from: "[email protected]",
to: ["[email protected]"],
subject: "Hello",
body_html: "<p>Hi</p>",
}),
});Python
import os, uuid, requests
requests.post(
"https://api.koltrix.com/api/v2/emails",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Hello",
"body_html": "<p>Hi</p>",
},
)Go
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/emails",
strings.NewReader(`{"from":"[email protected]","to":["[email protected]"],"subject":"Hi","body_html":"<p>Hi</p>"}`))
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Idempotency-Key", uuid.New().String())
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()Status flow
Every send writes a row in outbound_messages that walks through states:
queued ─► sent ─► delivered ─► opened ─► clicked
└────► bounced (auto-suppressed on 5xx)
└────► failedYou can subscribe to any of these via webhooks and view a live feed in Settings → Email logs.
Idempotency
When the network blinks and your client retries, you don't want two copies of
the welcome email. Include Idempotency-Key: <unique-string> and we cache the
first response for 24 hours per (tenant, key). Subsequent calls with the same
key return the original response with header Idempotent-Replayed: true.