Reference · /api/v2
API reference
Every public Koltrix endpoint. Add a kx_ key below and each endpoint's Try it panel sends the request to api.koltrix.com from your browser and shows the response. Nothing is proxied through this docs site.
Kept in this browser tab only (sessionStorage) and sent only to api.koltrix.com. Create one at app.koltrix.com/api-keys.
Try it sends real requests. Koltrix has no test mode: a send really sends. Use a dedicated key with the fewest permissions that work, and revoke it afterwards. The key stays in this tab (sessionStorage), goes only to api.koltrix.com, and never passes through this docs site.
Transactional email
Send one email to one or more recipients. Requires the send scope. Pass an Idempotency-Key header so a retry can never send twice.
Send an email
/api/v2/emailsQueues the message and returns 202 with the id of the first recipient's message record (one record is kept per recipient). The From address must be an active address on a verified domain in your workspace. Fields not listed here, such as reply_to or attachments, are ignored. Every recipient, including cc and bcc, is listed in the sent message's To header. A retry with the same Idempotency-Key within 24 hours returns the original response with status 200 and the header Idempotent-Replayed: true.
Parameters
fromstringbodyrequired[email protected] or Acme <[email protected]>. Must be an active address on a verified domain in this workspace.toarraybodyrequiredccarraybodybccarraybodysubjectstringbodyrequiredbody_htmlstringbodybody_text, or both.body_textstringbodybody_html when omitted.Idempotency-Keystringheadercurl -X POST "https://api.koltrix.com/api/v2/emails" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"from": "[email protected]",
"to": [
"[email protected]"
],
"subject": "Your receipt for order 1042",
"body_html": "<p>Thanks for your order.</p>",
"body_text": "Thanks for your order."
}'import { randomUUID } from "node:crypto";
const res = 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": "Your receipt for order 1042",
"body_html": "<p>Thanks for your order.</p>",
"body_text": "Thanks for your order."
}),
});
console.log(res.status, await res.json());import os, uuid, requests
res = 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": "Your receipt for order 1042",
"body_html": "<p>Thanks for your order.</p>",
"body_text": "Thanks for your order."
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
"github.com/google/uuid"
)
func main() {
body := strings.NewReader(`{
"from": "[email protected]",
"to": [
"[email protected]"
],
"subject": "Your receipt for order 1042",
"body_html": "<p>Thanks for your order.</p>",
"body_text": "Thanks for your order."
}`)
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/emails", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
req.Header.Set("Idempotency-Key", uuid.NewString())
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/emails");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
"Idempotency-Key: " . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode([
"from" => "[email protected]",
"to" => [
"[email protected]",
],
"subject" => "Your receipt for order 1042",
"body_html" => "<p>Thanks for your order.</p>",
"body_text" => "Thanks for your order.",
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20",
"status": "queued",
"queued": true,
"tracking_url": "/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20",
"recipient_count": 1
}Messages
Your outbound message log: every message sent through the API, the SMTP relay and broadcasts, one record per recipient. Requires the read or send scope.
List messages
/api/v2/messagesNewest first. status is one of queued, sent or bounced.
Parameters
statusstringqueryqueued, sent or bounced.recipientstringquerylimitnumberqueryoffsetnumberquerycurl -X GET "https://api.koltrix.com/api/v2/messages?status=sent&recipient=example.com&limit=50&offset=0" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/messages?status=sent&recipient=example.com&limit=50&offset=0", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/messages?status=sent&recipient=example.com&limit=50&offset=0",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/messages?status=sent&recipient=example.com&limit=50&offset=0", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/messages?status=sent&recipient=example.com&limit=50&offset=0");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"data": [
{
"id": "6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20",
"tenant_id": "5c4b3a29-1807-4f6e-9d5c-4b3a29180716",
"message_id": "[email protected]",
"from": "[email protected]",
"to": "[email protected]",
"subject": "Your receipt for order 1042",
"status": "sent",
"open_count": 2,
"click_count": 0,
"created_at": "2026-10-02T14:00:00Z",
"sent_at": "2026-10-02T14:00:01Z",
"opened_at": "2026-10-02T14:07:04Z"
}
],
"total": 1
}Get a message
/api/v2/messages/:idOne message with its status, engagement counters and timestamps. engagement_score adds 1 per open, 5 per click and 10 per reply. Currently answers 404 while the message is still queued, usually for under a second.
Parameters
idstringpathrequiredPOST /api/v2/emails.curl -X GET "https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20",
"from": "[email protected]",
"to": "[email protected]",
"subject": "Your receipt for order 1042",
"status": "sent",
"open_count": 2,
"click_count": 0,
"reply_count": 1,
"engagement_score": 12,
"first_open_after_seconds": 423,
"message_id": "[email protected]",
"error": null,
"created_at": "2026-10-02T14:00:00Z",
"sent_at": "2026-10-02T14:00:01Z",
"opened_at": "2026-10-02T14:07:04Z",
"clicked_at": null,
"replied_at": "2026-10-02T15:12:40Z",
"bounced_at": null
}List a message's events
/api/v2/messages/:id/eventsEvery open, click and reply, oldest first. kind is open, click or reply; a click also has the url. prefetch: true marks an open that looks like automatic image fetching by a mail provider; those are not counted in open_count.
Parameters
idstringpathrequiredcurl -X GET "https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20/events" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20/events", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20/events",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20/events", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/messages/6f1c2a9e-3b7d-4e41-9a52-0c8d1f3e7b20/events");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"data": [
{
"kind": "open",
"prefetch": true,
"ip": "17.58.0.12",
"user_agent": "Mozilla/5.0",
"created_at": "2026-10-02T14:00:03Z"
},
{
"kind": "open",
"prefetch": false,
"ip": "203.0.113.7",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_5)",
"created_at": "2026-10-02T14:07:04Z"
},
{
"kind": "reply",
"prefetch": false,
"user_agent": "inbound-mail",
"created_at": "2026-10-02T15:12:40Z"
}
],
"total": 3
}Your key
Check which workspace an API key belongs to and which scopes it has.
Get the current key
/api/v2/meReturns the workspace short name (tenant) and the key's scopes. Works with any valid key, so it makes a good health check.
curl -X GET "https://api.koltrix.com/api/v2/me" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/me", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/me",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/me", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/me");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"tenant": "acme",
"permissions": [
"read",
"send"
]
}Lists
Newsletter audiences, available over the API only for now. Reading needs read or newsletter; changes need newsletter.
curl -X GET "https://api.koltrix.com/api/v2/lists" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/lists", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/lists",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/lists", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"data": [
{
"id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"name": "Product updates",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]",
"reply_to": "",
"subscriber_count": 1284,
"created_at": "2026-09-12T09:14:22Z",
"updated_at": "2026-10-01T08:00:00Z"
}
]
}Create a list
/api/v2/listsReturns 201 with the new list. from_address is the default sender for broadcasts and sequences on the list; use an active address on a verified domain.
Parameters
namestringbodyrequireddescriptionstringbodyfrom_namestringbodyfrom_addressstringbodyreply_tostringbodycurl -X POST "https://api.koltrix.com/api/v2/lists" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Product updates",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]"
}'const res = await fetch("https://api.koltrix.com/api/v2/lists", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Product updates",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]"
}),
});
console.log(res.status, await res.json());import os, requests
res = requests.post(
"https://api.koltrix.com/api/v2/lists",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
json={
"name": "Product updates",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]"
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"name": "Product updates",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]"
}`)
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/lists", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Product updates",
"description" => "What shipped this month",
"from_name" => "Acme",
"from_address" => "[email protected]",
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"name": "Product updates",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]",
"reply_to": "",
"subscriber_count": 0,
"created_at": "2026-10-02T10:11:12Z",
"updated_at": "2026-10-02T10:11:12Z"
}Get a list
/api/v2/lists/:idOne list. 404 if there is no such list.
Parameters
idstringpathrequiredcurl -X GET "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"name": "Product updates",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]",
"reply_to": "",
"subscriber_count": 1284,
"created_at": "2026-09-12T09:14:22Z",
"updated_at": "2026-10-01T08:00:00Z"
}Update a list
/api/v2/lists/:idChanges only the fields you send and returns the updated list.
Parameters
idstringpathrequirednamestringbodydescriptionstringbodyfrom_namestringbodyfrom_addressstringbodyreply_tostringbodycurl -X PATCH "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Changelog"
}'const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Changelog"
}),
});
console.log(res.status, await res.json());import os, requests
res = requests.patch(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
json={
"name": "Changelog"
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"name": "Changelog"
}`)
req, _ := http.NewRequest("PATCH", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Changelog",
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"name": "Changelog",
"description": "What shipped this month",
"from_name": "Acme",
"from_address": "[email protected]",
"reply_to": "",
"subscriber_count": 1284,
"created_at": "2026-09-12T09:14:22Z",
"updated_at": "2026-10-02T10:12:00Z"
}Delete a list
/api/v2/lists/:idDeletes the list and its subscribers. Cannot be undone. Returns 204 with an empty body.
Parameters
idstringpathrequiredcurl -X DELETE "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.delete(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;Empty body. A 204 means it worked.
Subscribers
The people on a list. Reading needs read or newsletter; changes need newsletter.
List subscribers
/api/v2/lists/:id/subscribersstatus is one of pending (waiting for double opt-in), active or unsubscribed.
Parameters
idstringpathrequiredstatusstringquerylimitnumberqueryoffsetnumberquerycurl -X GET "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers?status=active&limit=100&offset=0" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers?status=active&limit=100&offset=0", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers?status=active&limit=100&offset=0",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers?status=active&limit=100&offset=0", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers?status=active&limit=100&offset=0");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"data": [
{
"id": "9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"email": "[email protected]",
"name": "Ada Lovelace",
"status": "active",
"unsubscribe_token": "4f9a0c1d2e3b4a5f6c7d8e9f0a1b2c3d",
"tags": [
"beta"
],
"created_at": "2026-09-20T14:01:00Z",
"updated_at": "2026-09-20T14:01:00Z"
}
],
"total": 1
}Add a subscriber
/api/v2/lists/:id/subscribersReturns 201 with the subscriber. Adding an email that is already on the list updates its name instead of failing; someone who unsubscribed stays unsubscribed. On a list that requires double opt-in the subscriber starts as pending.
Parameters
idstringpathrequiredemailstringbodyrequirednamestringbodytagsarraybodycurl -X POST "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"name": "Ada Lovelace",
"tags": [
"beta"
]
}'const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"email": "[email protected]",
"name": "Ada Lovelace",
"tags": [
"beta"
]
}),
});
console.log(res.status, await res.json());import os, requests
res = requests.post(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
json={
"email": "[email protected]",
"name": "Ada Lovelace",
"tags": [
"beta"
]
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"email": "[email protected]",
"name": "Ada Lovelace",
"tags": [
"beta"
]
}`)
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"email" => "[email protected]",
"name" => "Ada Lovelace",
"tags" => [
"beta",
],
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"email": "[email protected]",
"name": "Ada Lovelace",
"status": "active",
"unsubscribe_token": "4f9a0c1d2e3b4a5f6c7d8e9f0a1b2c3d",
"tags": [
"beta"
],
"created_at": "2026-10-02T10:14:33Z",
"updated_at": "2026-10-02T10:14:33Z"
}Import subscribers from CSV
/api/v2/lists/:id/subscribers/importSend the CSV as a string. A header row with email, name (or first_name or full_name) and tags columns is recognised; without one, the first column is the email. Separate tags with semicolons. Every imported row becomes active, even on a list that requires double opt-in, so import only people who agreed to hear from you. Addresses on your suppression list are still never mailed. Rows with an invalid email are skipped.
Parameters
idstringpathrequiredcsvstringbodyrequiredcurl -X POST "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/import" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"csv": "email,name,tags\[email protected],Ada Lovelace,beta;vip\[email protected],Linus,beta"
}'const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/import", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"csv": "email,name,tags\n[email protected],Ada Lovelace,beta;vip\n[email protected],Linus,beta"
}),
});
console.log(res.status, await res.json());import os, requests
res = requests.post(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/import",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
json={
"csv": "email,name,tags\n[email protected],Ada Lovelace,beta;vip\n[email protected],Linus,beta"
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"csv": "email,name,tags\[email protected],Ada Lovelace,beta;vip\[email protected],Linus,beta"
}`)
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/import", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/import");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"csv" => "email,name,tags
[email protected],Ada Lovelace,beta;vip
[email protected],Linus,beta",
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"imported": 2,
"skipped": 0,
"errors": null
}Delete a subscriber
/api/v2/lists/:id/subscribers/:sub_idRemoves the subscriber from the list. To stop mailing someone but keep the record, unsubscribe them instead. Returns 204 with an empty body.
Parameters
idstringpathrequiredsub_idstringpathrequiredcurl -X DELETE "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.delete(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;Empty body. A 204 means it worked.
Unsubscribe a subscriber
/api/v2/lists/:id/subscribers/:sub_id/unsubscribeMarks the subscriber unsubscribed, stops any sequence they are in and adds the address to your suppression list, exactly as if they had clicked the unsubscribe link. No body.
Parameters
idstringpathrequiredsub_idstringpathrequiredcurl -X POST "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a/unsubscribe" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a/unsubscribe", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.post(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a/unsubscribe",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a/unsubscribe", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/subscribers/9d8c7b6a-5f4e-4d3c-b2a1-0f9e8d7c6b5a/unsubscribe");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"status": "unsubscribed"
}Broadcasts
Send one message to every active subscriber on a list. Requires the newsletter scope.
Send a broadcast
/api/v2/lists/:id/broadcastCreates a campaign and queues it straight away; returns 202. Each subscriber gets their own copy with an unsubscribe link and header, open tracking and tracked links. In the body, the merge variables {{name}}, {{first_name}}, {{first_name|fallback}}, {{email}} and {{unsubscribe_url}} are filled in per recipient (the subject is sent as written). from_address and from_name default to the list's.
Parameters
idstringpathrequiredsubjectstringbodyrequiredbody_htmlstringbodyrequiredbody_textstringbodypreheaderstringbodyfrom_namestringbodyfrom_addressstringbodycurl -X POST "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/broadcast" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"subject": "What shipped in October",
"body_html": "<p>Hi {{first_name|there}},</p><p>This month we shipped…</p>",
"body_text": "Hi {{first_name|there}}, this month we shipped…",
"preheader": "Faster search and a new inbox"
}'const res = await fetch("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/broadcast", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"subject": "What shipped in October",
"body_html": "<p>Hi {{first_name|there}},</p><p>This month we shipped…</p>",
"body_text": "Hi {{first_name|there}}, this month we shipped…",
"preheader": "Faster search and a new inbox"
}),
});
console.log(res.status, await res.json());import os, requests
res = requests.post(
"https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/broadcast",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
json={
"subject": "What shipped in October",
"body_html": "<p>Hi {{first_name|there}},</p><p>This month we shipped…</p>",
"body_text": "Hi {{first_name|there}}, this month we shipped…",
"preheader": "Faster search and a new inbox"
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"subject": "What shipped in October",
"body_html": "<p>Hi {{first_name|there}},</p><p>This month we shipped…</p>",
"body_text": "Hi {{first_name|there}}, this month we shipped…",
"preheader": "Faster search and a new inbox"
}`)
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/broadcast", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/lists/0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d/broadcast");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"subject" => "What shipped in October",
"body_html" => "<p>Hi {{first_name|there}},</p><p>This month we shipped…</p>",
"body_text" => "Hi {{first_name|there}}, this month we shipped…",
"preheader" => "Faster search and a new inbox",
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"campaign_id": "1d2c3b4a-5f6e-4d7c-8b9a-0f1e2d3c4b5a",
"status": "queued",
"message": "Broadcast enqueued — recipients will be fanned out by the worker."
}Sequences
Timed series of emails sent to people who join a list. Reading needs read or newsletter; changes need newsletter.
curl -X GET "https://api.koltrix.com/api/v2/sequences" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/sequences", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/sequences",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/sequences", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/sequences");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"data": [
{
"id": "3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"name": "Welcome series",
"trigger": "subscribe",
"active": true,
"step_count": 3,
"created_at": "2026-09-21T12:00:00Z",
"updated_at": "2026-09-21T12:00:00Z"
}
]
}Create a sequence
/api/v2/sequencesReturns 201. trigger defaults to subscribe, the only trigger: people are enrolled when they become active on the list.
Parameters
namestringbodyrequiredlist_idstringbodyrequiredtriggerstringbodysubscribe (the default and only value).curl -X POST "https://api.koltrix.com/api/v2/sequences" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome series",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"trigger": "subscribe"
}'const res = await fetch("https://api.koltrix.com/api/v2/sequences", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"name": "Welcome series",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"trigger": "subscribe"
}),
});
console.log(res.status, await res.json());import os, requests
res = requests.post(
"https://api.koltrix.com/api/v2/sequences",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
json={
"name": "Welcome series",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"trigger": "subscribe"
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"name": "Welcome series",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"trigger": "subscribe"
}`)
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/sequences", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/sequences");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Welcome series",
"list_id" => "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"trigger" => "subscribe",
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9",
"list_id": "0b3c6d2e-91f4-4a7b-8c5d-2e3f4a5b6c7d",
"name": "Welcome series",
"trigger": "subscribe",
"active": true,
"step_count": 0,
"created_at": "2026-10-02T12:00:00Z",
"updated_at": "2026-10-02T12:00:00Z"
}Delete a sequence
/api/v2/sequences/:idDeletes the sequence and its steps. Returns 204 with an empty body.
Parameters
idstringpathrequiredcurl -X DELETE "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.delete(
"https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;Empty body. A 204 means it worked.
List a sequence's steps
/api/v2/sequences/:id/stepsThe steps in order of position.
Parameters
idstringpathrequiredcurl -X GET "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.get(
"https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("GET", "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"data": [
{
"id": "7e6d5c4b-3a29-4f18-8e7d-6c5b4a392817",
"sequence_id": "3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9",
"position": 0,
"delay_hours": 0,
"subject": "Welcome to Acme",
"body_html": "<p>Hi {{first_name|there}}, glad you're here.</p>",
"body_text": "Hi {{first_name|there}}, glad you're here.",
"created_at": "2026-10-02T12:01:00Z"
}
]
}Add a step
/api/v2/sequences/:id/stepsReturns 201. delay_hours is counted from the previous step (from enrolment for the first step). In step bodies {{email}} and {{unsubscribe_url}} are filled in; the name variables are always empty, so give {{first_name}} a fallback.
Parameters
idstringpathrequiredpositionnumberbodyrequireddelay_hoursnumberbodyrequiredsubjectstringbodyrequiredbody_htmlstringbodybody_textstringbodycurl -X POST "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps" \
-H "Authorization: Bearer $KOLTRIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"position": 0,
"delay_hours": 0,
"subject": "Welcome to Acme",
"body_html": "<p>Hi {{first_name|there}}, glad you'\''re here.</p>",
"body_text": "Hi {{first_name|there}}, glad you'\''re here."
}'const res = await fetch("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"position": 0,
"delay_hours": 0,
"subject": "Welcome to Acme",
"body_html": "<p>Hi {{first_name|there}}, glad you're here.</p>",
"body_text": "Hi {{first_name|there}}, glad you're here."
}),
});
console.log(res.status, await res.json());import os, requests
res = requests.post(
"https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
json={
"position": 0,
"delay_hours": 0,
"subject": "Welcome to Acme",
"body_html": "<p>Hi {{first_name|there}}, glad you're here.</p>",
"body_text": "Hi {{first_name|there}}, glad you're here."
},
)
print(res.status_code, res.json())package main
import (
"strings"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := strings.NewReader(`{
"position": 0,
"delay_hours": 0,
"subject": "Welcome to Acme",
"body_html": "<p>Hi {{first_name|there}}, glad you're here.</p>",
"body_text": "Hi {{first_name|there}}, glad you're here."
}`)
req, _ := http.NewRequest("POST", "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps", body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"position" => 0,
"delay_hours" => 0,
"subject" => "Welcome to Acme",
"body_html" => "<p>Hi {{first_name|there}}, glad you're here.</p>",
"body_text" => "Hi {{first_name|there}}, glad you're here.",
]),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;{
"id": "7e6d5c4b-3a29-4f18-8e7d-6c5b4a392817",
"sequence_id": "3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9",
"position": 0,
"delay_hours": 0,
"subject": "Welcome to Acme",
"body_html": "<p>Hi {{first_name|there}}, glad you're here.</p>",
"body_text": "Hi {{first_name|there}}, glad you're here.",
"created_at": "2026-10-02T12:01:00Z"
}Delete a step
/api/v2/sequences/:id/steps/:step_idReturns 204 with an empty body.
Parameters
idstringpathrequiredstep_idstringpathrequiredcurl -X DELETE "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps/7e6d5c4b-3a29-4f18-8e7d-6c5b4a392817" \
-H "Authorization: Bearer $KOLTRIX_KEY"const res = await fetch("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps/7e6d5c4b-3a29-4f18-8e7d-6c5b4a392817", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.KOLTRIX_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
res = requests.delete(
"https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps/7e6d5c4b-3a29-4f18-8e7d-6c5b4a392817",
headers={
"Authorization": f"Bearer {os.environ['KOLTRIX_KEY']}",
},
)
print(res.status_code, res.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, _ := http.NewRequest("DELETE", "https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps/7e6d5c4b-3a29-4f18-8e7d-6c5b4a392817", nil)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KOLTRIX_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(out))
}<?php
$ch = curl_init("https://api.koltrix.com/api/v2/sequences/3a2b1c0d-9e8f-4a7b-b6c5-d4e3f2a1b0c9/steps/7e6d5c4b-3a29-4f18-8e7d-6c5b4a392817");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("KOLTRIX_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response;Empty body. A 204 means it worked.