KoltrixKoltrix docs

Authentication

Koltrix has two authentication surfaces and the right one depends on what you're doing.

SurfaceAuthUsed by
app.koltrix.com (dashboard)Email + password / SSO via your account loginHumans in a browser
/api/v2/* (HTTP API)Bearer API key — Authorization: Bearer kx_...Your server-side code
/api/v1/public/*Per-list public token in the URL (no Authorization header)Embed signup forms on customer sites
SMTP relay (port 2525)AUTH PLAIN with your API key as the passwordMail libraries (nodemailer, swaks)

The split exists so we can keep two very different threat models clean. The dashboard is talking to a logged-in human; the public HTTP API is talking to your server and trusts nothing except a hashed, scope-limited API key. The public-token surface is no-auth on purpose — it's the URL that ships inside an HTML <form action="..."> on your landing page, protected with a honeypot field instead of a header.

API keys (/api/v2/*)

Create one at app.koltrix.com/api-keys (or Settings → API Keys → New key). Each key is:

  • Scoped to one organisation. The tenant is bound at creation and cannot be changed.
  • Prefixed kx_ so it's obvious in logs and easy to grep for.
  • SHA-256 hashed at rest. We show the full secret exactly once when you create it — copy it somewhere safe, we cannot recover it.

Scopes

Tick one or more when creating the key — least privilege wins:

ScopeWhat it lets the key do
readList and read messages, lists, sequences, events
sendSend transactional email (REST + SMTP)
newsletterManage lists, subscribers, segments, broadcasts, sequences
contactsManage CRM contacts
*Full access — every endpoint, every method. Use sparingly.

A key may carry several scopes; the endpoint checks whichever it needs. A request that hits a route the key isn't scoped for returns 403.

Make an authenticated request

curl https://api.koltrix.com/api/v2/me \
  -H "Authorization: Bearer kx_..."
{
  "tenant": "acme",
  "permissions": ["read", "send", "newsletter"]
}

Permission errors

If the key is valid but missing the scope you need, the API returns 403:

{ "error": "missing permission: newsletter" }

If the key was revoked, deleted, or never existed, the API returns 401:

{ "error": "invalid or revoked api key" }

Rotation

There's no clever "rotate in place" — mint a new key, deploy it everywhere the old one was used, then revoke the old key in Settings → API Keys. Revocation takes effect immediately; the next request with the old key returns 401. Anything already in flight finishes without interruption because we only check revocation at request entry.

Rotate quarterly as routine hygiene, or immediately if a key has been committed to git, posted in chat, or printed in a log.

Public list tokens (embed forms)

Some routes — embed signup, unsubscribe confirmation — must be callable from a browser with no auth. We support that without exposing your API key by issuing a per-list public token that's baked into the form action URL:

<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>

The _hp field is a honeypot — bots fill every input on the page including hidden ones, so a non-empty _hp value makes us silently 200 without saving. This keeps the form working without a CAPTCHA and without coupling to a third-party anti-bot vendor.

If you suspect a token has leaked (somebody screenshotted your landing page into a guide?), rotate it under Newsletters → list → Embed → Rotate. The old token starts returning errors immediately; the new one slots into the same form HTML.

SMTP authentication

The SMTP relay on smtp.koltrix.com:2525 accepts AUTH PLAIN and AUTH LOGIN. Use any value for the username (apikey is conventional); the password must be your kx_ API key with the send scope.

import smtplib, os
with smtplib.SMTP("smtp.koltrix.com", 2525) as s:
    s.login("apikey", os.environ["KOLTRIX_KEY"])
    # ... send message

If the password doesn't parse as a kx_ key, the relay rejects with 535 5.7.8 Authentication failed. If the key parses but lacks the send scope, you'll see the same 535 after RCPT TO.

See SMTP relay for full client examples.

Rate limits

LimitDefaultReturned when exceeded
Per-IP (all surfaces)300 / minute429
Per-key (/api/v2)60 / minute429
Per-account sendswarmup-driven429

Idempotency-Key replays don't count toward your quota. If you're consistently hitting the limit on a legitimate workload, contact [email protected] — we'll raise it on the account.