Integrations
Koltrix is a normal HTTP + SMTP product, so it plugs into anything that talks HTTP, SMTP, or webhooks. This page collects the patterns that come up most often — Stripe receipts, GitHub notifications, Slack alerts, Zapier / Make connectors, and the embed-form pattern for forms on your own site.
Sending from your own backend
Most integrations boil down to "when X happens in my system, send an email
through Koltrix." The pattern is always the same: receive a webhook from
the source system, then POST /api/v2/emails.
Stripe receipts
Stripe fires checkout.session.completed when a payment succeeds. Your
handler:
// /api/webhooks/stripe (Next.js route handler)
import Stripe from "stripe";
export async function POST(req: Request) {
const sig = req.headers.get("stripe-signature")!;
const raw = await req.text();
const event = Stripe.webhooks.constructEvent(raw, sig, process.env.STRIPE_WEBHOOK_SECRET!);
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
await fetch("https://api.koltrix.com/api/v2/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KOLTRIX_KEY!}`,
"Content-Type": "application/json",
"Idempotency-Key": session.id, // safe replay key
},
body: JSON.stringify({
from: "[email protected]",
to: [session.customer_details!.email!],
subject: `Receipt for order ${session.id.slice(-6)}`,
body_html: `<p>Thanks for your order — $${(session.amount_total ?? 0) / 100}.</p>`,
body_text: `Thanks for your order — $${(session.amount_total ?? 0) / 100}.`,
}),
});
}
return new Response("ok");
}Using session.id as the Idempotency-Key means Stripe's automatic
retries don't double-send the receipt.
GitHub PR notifications
GitHub fires pull_request events. A handler that emails a code owner
when a PR is opened against main:
export async function POST(req: Request) {
const { action, pull_request, repository } = await req.json();
if (action !== "opened" || pull_request.base.ref !== "main") {
return new Response("ignored");
}
await fetch("https://api.koltrix.com/api/v2/emails", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KOLTRIX_KEY!}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "[email protected]",
to: ["[email protected]"],
subject: `[${repository.full_name}] ${pull_request.title}`,
body_html: `<p><a href="${pull_request.html_url}">Review #${pull_request.number}</a></p>`,
}),
});
return new Response("ok");
}Slack alerting on hard bounces
Subscribe to message.bounced and forward to a Slack incoming webhook:
// /api/webhooks/koltrix
import crypto from "node:crypto";
export async function POST(req: Request) {
const raw = await req.text();
const sig = req.headers.get("x-koltrix-signature")!;
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.KOLTRIX_WEBHOOK_SECRET!)
.update(raw)
.digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
return new Response("bad signature", { status: 401 });
}
const event = JSON.parse(raw);
if (event.event === "message.bounced") {
await fetch(process.env.SLACK_WEBHOOK_URL!, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `:warning: Hard bounce to *${event.to}* — ${event.bounce_reason ?? "no reason"}`,
}),
});
}
return new Response("ok");
}A single endpoint can fan out to multiple downstreams — Slack on bounces,
PagerDuty on message.complained, your CRM on message.opened. Just
branch on event.event.
Zapier / Make / n8n
Koltrix doesn't ship a Zapier app yet, but the REST API works as a generic "POST a webhook" target on the producer side, and the outbound webhook system works as a "trigger Zap on event" consumer:
- Send email from a Zap: use the Zapier Webhooks by Zapier → POST
step pointing at
https://api.koltrix.com/api/v2/emails. Set theAuthorizationheader toBearer kx_.... - Trigger a Zap from a Koltrix event: add a Koltrix webhook endpoint pointing at the Zapier webhook URL. Pick the events you care about; Zapier hands you the JSON to parse.
Make and n8n work the same way — both have generic HTTP request + webhook nodes that fit Koltrix without a custom connector.
Embed signup forms
Put a form on your marketing site that drops subscribers into a Koltrix list with no API key in the browser. See the full snippet (with honeypot) under Newsletters → Embed signup form; the short version:
<form action="https://api.koltrix.com/api/v1/public/lists/PUBLIC_TOKEN/subscribe"
method="post">
<input name="email" type="email" required />
<input name="_hp" type="text" style="display:none" tabindex="-1" autocomplete="off" />
<button>Subscribe</button>
</form>PUBLIC_TOKEN comes from Newsletters → list → Embed. The endpoint
accepts both form-encoded and JSON payloads — same URL — so a vanilla
HTML form works and a fetch from your SPA works.
If you'd rather keep all traffic server-side, post from your own backend with an API key:
// /api/subscribe handler
await fetch(`https://api.koltrix.com/api/v2/lists/${LIST_ID}/subscribers`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.KOLTRIX_KEY}` },
body: JSON.stringify({ email: req.body.email }),
});Inbound email parsing
Receive emails to addresses on your verified domain at Settings → Domains → <domain> → Inbound and they'll appear in the dashboard inbox automatically. If you'd rather handle them programmatically:
- Add a webhook subscribed to
email.inbound. - Your handler receives the parsed email shape:
{
"event": "email.inbound",
"timestamp": 1716220800,
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Re: Demo follow-up",
"body_html": "<p>Yes, that works…</p>",
"body_text": "Yes, that works…",
"in_reply_to": "<[email protected]>",
"thread_id": "01HX9...",
"attachments": [
{ "filename": "calendar.ics", "size": 1842, "content_type": "text/calendar" }
]
}Attachment bytes aren't in the webhook (we'd blow your endpoint's body
limit) — fetch them with GET /api/v2/mail/{id}/attachments/{att_id}/download.
CRM sync
Koltrix ships a lightweight CRM (contacts + tags) so inbox threads show useful context next to the message. Sync from your source of truth (HubSpot, Salesforce, your own DB) via the contacts API:
curl https://api.koltrix.com/api/v2/contacts \
-H "Authorization: Bearer kx_..." \
-d '{
"email": "[email protected]",
"name": "Ada Lovelace",
"company": "Acme Co",
"tags": ["customer", "annual"]
}'Requires the contacts scope. A contact appearing here makes inbox
threads from that address show their CRM card next to the message in
the dashboard.
Language-by-language ergonomics
| Language | Recommendation |
|---|---|
| Node | fetch (Node 18+) for REST; nodemailer for SMTP |
| Python | requests for REST; smtplib for SMTP |
| Go | net/http for REST; net/smtp for SMTP |
| PHP | Guzzle for REST; PHPMailer for SMTP |
| Ruby | Net::HTTP for REST; Mail gem for SMTP |
| Rust | reqwest for REST; lettre for SMTP |
We don't ship official SDKs today — the REST surface is small enough that your own thin wrapper does the job, and we'd rather not maintain six SDKs that lag behind the API. If that changes, it'll be announced in the docs and on the changelog.
Custom integrations
Anything you can't model with the patterns above usually fits in one of two shapes:
- Push from your system → Koltrix: your code posts to
/api/v2/*with an API key. Use idempotency keys to handle retries. - Push from Koltrix → your system: subscribe a webhook with the events you care about, verify the signature, do your thing.
If you've built something cool that other Koltrix users would want, send a writeup to [email protected] — we'll feature it here.