REST API
Token-authenticated /api/v1/* surface. Personal access tokens carry scopes that
gate per-endpoint access. Mirror of every customer-panel write — full automation surface for
integrations + CI / CD / cron-driven workflows.
Auth
Bearer token in the Authorization header. Create tokens at Customer → API tokens → New token:
curl https://app.example.com/api/v1/me \
-H "Authorization: Bearer kp_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Accept: application/json"
Token scopes
| Scope | What it allows |
|---|---|
* | Full access (wildcard). |
lists:read / lists:write | List CRUD. |
subscribers:read / subscribers:write | Subscriber CRUD. |
campaigns:read / campaigns:write | Campaign management. |
templates:read / templates:write | Template CRUD. |
transactional:send | Fire individual transactional emails. |
Errors
| Code | Meaning | Body |
|---|---|---|
| 401 | Token missing / malformed / revoked | {"message":"Unauthenticated."} |
| 403 | Token valid but scope missing | {"message":"This action is unauthorized."} |
| 404 | Resource not found or not owned by customer | {"message":"Not found."} |
| 422 | Validation failed | {"message":"...","errors":{"field":[...]}} |
| 429 | Rate-limited (60 req/min per token) | Retry-After header |
Endpoints
Identity
GET /api/v1/me
200 OK
{
"id": 12,
"uid": "abc123def456",
"name": "Acme Inc.",
"email": "[email protected]",
"plan": "pro"
}
Lists
GET /api/v1/lists # list all
POST /api/v1/lists # create
GET /api/v1/lists/{uid} # show one
PUT /api/v1/lists/{uid} # update
DELETE /api/v1/lists/{uid} # soft delete
Subscribers
GET /api/v1/lists/{uid}/subscribers # list (paginated)
POST /api/v1/lists/{uid}/subscribers # create + confirm
GET /api/v1/lists/{uid}/subscribers/{email} # show
PUT /api/v1/lists/{uid}/subscribers/{email} # update fields / status
DELETE /api/v1/lists/{uid}/subscribers/{email} # remove
# create example
curl -X POST https://app.example.com/api/v1/lists/abc123/subscribers \
-H "Authorization: Bearer kp_..." \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"fields": { "FIRST_NAME": "Jane", "COUNTRY": "GB" },
"status": "confirmed"
}'
Transactional email
POST /api/v1/transactional/send
{
"to": "[email protected]",
"to_name": "Jane Doe",
"subject": "Your receipt",
"template_uid": "tpl_abc123", // optional, uses html_body if absent
"html_body": "<p>Hello :first_name</p>",
"merge": { "first_name": "Jane" },
"tags": ["receipt", "order-1234"]
}
202 Accepted
{ "message_id": "<[email protected]>", "queued_at": "2026-06-04T..." }
Campaigns
GET /api/v1/campaigns
POST /api/v1/campaigns # create draft
PUT /api/v1/campaigns/{uid} # update
POST /api/v1/campaigns/{uid}/send # schedule / send now
POST /api/v1/campaigns/{uid}/pause
POST /api/v1/campaigns/{uid}/resume
GET /api/v1/campaigns/{uid}/report # engagement metrics
Outbound webhooks
KodMail POSTs JSON to your URL when events happen in the account. Configure at Customer → Webhooks → New webhook.
Event catalogue
subscriber.created/subscriber.confirmed/subscriber.unsubscribed/subscriber.blacklistedcampaign.scheduled/campaign.sending/campaign.sent/campaign.failedemail.delivered/email.opened/email.clicked/email.bounced/email.complainedautomation.enrolled/automation.completed
Signature verification
Every payload arrives with:
X-Webhook-Signature: t=1717512000,v1=abc123def...
X-Webhook-Event: subscriber.created
Verify with HMAC-SHA256 using your webhook signing secret:
// PHP example
$sig = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'];
preg_match('/t=(\d+),v1=(\w+)/', $sig, $m);
[$_, $timestamp, $signature] = $m;
$payload = file_get_contents('php://input');
$expected = hash_hmac('sha256', "{$timestamp}.{$payload}", $secret);
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit;
}
// Timestamp skew check — reject >5 min old
if (abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit;
}
Retry policy
Non-2xx response / timeout / connection error → retry on exponential backoff up to 8 attempts (30s, 1m, 5m, 15m, 30m, 1h, 6h, 24h). After 8 failures, delivery is dropped and the webhook gets last_failure_at stamped.