Authentication

sevk uses bearer API keys scoped to a single project. Each key carries a fixed set of capability scopes and can optionally be pinned to one verified domain.

API keys

An API key belongs to exactly one project. Every resource read or written through the API (emails, contacts, audiences, broadcasts, domains) is implicitly filtered by that project id. There is no way to reach another project's data with a given key, even at the 404 level.

Create and revoke keys from the sevk dashboard under Settings → API Keys. Revocation takes effect on the next request.

Sending the key

Pass the key as a bearer token on every request:

Authorization: Bearer sevk_xxxxxxxxx
curl https://api.sevk.io/emails \
-H "Authorization: Bearer sevk_xxxxxxxxx" \
-H "Content-Type: application/json"

Through the SDK

The clients take the key in their constructor and attach the header themselves:

Node.js
import { Sevk } from 'sevk'
const sevk = new Sevk(process.env.SEVK_API_KEY!)
Python
import os
from sevk import Sevk
sevk = Sevk(os.environ['SEVK_API_KEY'])

Domain-pinned keys

An API key can optionally be bound to a single verified domain. When a key has a domainId, every POST /emails, POST /emails/bulk, and POST /broadcasts/:id/send request is inspected: if the from address (or the broadcast's domainId) is not on that domain, the request is rejected with 403 before any queue work happens. Pinning is the right choice for per-tenant sending where one key must never send as another tenant.

Sending from multiple regions

When the same hostname is registered in several regions, bind each sending key to the domain record for the region you want to use. A key bound to example.com in eu-central-1 sends from that region; a key bound to its eu-west-1 record sends from Ireland. Both can use [email protected] as the sender. This applies to single sends, bulk sends, and SMTP authentication. Broadcasts use the region of their selected domain record.

Keys scoped to all domains do not select a specific region when several verified records match the sender. Use a domain-pinned key when region selection matters. sevk does not automatically switch regions when a send fails.

Capability scopes

Every endpoint checks one or more capabilities on the presenting key. A key can be issued with full access, or with an arbitrary subset of scopes. Revoking a scope takes effect on the next request. No token re-issue required.

Emails

NameTypeDescription
email:sendcapabilityPOST /emails and POST /emails/bulk.
email:readcapabilityGET /emails/:id: read a specific email record.

Contacts

NameTypeDescription
contact:readcapabilityList and read contacts within an audience.
contact:writecapabilityCreate and update contacts; updates resubscriptionLocked state when contacts resubscribe.
contact:deletecapabilityDelete contacts. Removes the contact from every audience and topic it belonged to.

Audiences

NameTypeDescription
audience:readcapabilityList and read audiences (includes contactCount).
audience:writecapabilityCreate and update audiences.
audience:deletecapabilityDelete audiences and their contacts.

Templates

NameTypeDescription
template:readcapabilityList and read templates.
template:writecapabilityCreate and update templates.
template:deletecapabilityDelete templates.

Broadcasts

NameTypeDescription
broadcast:readcapabilityList and read broadcasts, analytics, and broadcast-specific cost/delivery detail.
broadcast:writecapabilityCreate and update broadcasts (draft state).
broadcast:deletecapabilityDelete broadcasts.
broadcast:sendcapabilityTrigger a send. Deducts per-recipient cost from the project balance.

Domains

NameTypeDescription
domain:readcapabilityList and read domain records and verification status.
domain:writecapabilityAdd, update, and trigger verification on domains.
domain:deletecapabilityDelete domains. Keys pinned to the domain are orphaned.
domain:dns-readcapabilityRead the DKIM/SPF/return-path records sevk expects.

Topics

NameTypeDescription
topic:readcapabilityList and read topics (includes contactCount).
topic:writecapabilityCreate and update topics.
topic:deletecapabilityDelete topics.

Segments

NameTypeDescription
segment:readcapabilityList and read segments and their resolved contacts.
segment:writecapabilityCreate and update segment filter rules.
segment:deletecapabilityDelete segments.

Subscriptions

NameTypeDescription
subscription:subscribecapabilitySubscribe a contact to a topic. Returns 403 if resubscriptionLocked is true on an unsubscribed contact.
subscription:unsubscribecapabilityUnsubscribe a contact globally. Marks the contact unsubscribed; it does not set resubscriptionLocked.

Webhooks

NameTypeDescription
webhook:readcapabilityList and read webhook endpoints.
webhook:writecapabilityCreate and update webhook endpoints and event filters.
webhook:deletecapabilityDelete webhook endpoints.

Activity

NameTypeDescription
activity:readcapabilityRead the activity log (opens, clicks, bounces, complaints, deliveries, and other email events).

Usage

NameTypeDescription
limits:readcapabilityRead GET /limits: project balance, plan limits, and current resource counts.

Outbound

NameTypeDescription
outbound:readcapabilityRead the outbound monthly quota, usage, reset time, and pause state.
outbound:writecapabilityUpdate the outbound quota limit and pause or resume outbound sending.

Inbound

NameTypeDescription
inbound:readcapabilityList and read inbound emails, routes, and blocklist entries.
inbound:writecapabilityCreate and update inbound routing and settings.
inbound:deletecapabilityDelete inbound emails, routes, and blocklist entries.

AI

NameTypeDescription
ai:generatecapabilityGenerate email markup with AI.

Key hygiene

  • Server-side only. Keys carry full send authority for the project. Never embed one in a mobile app, desktop app, or browser bundle.
  • One key per integration. If a key leaks, you want to revoke it without taking down the rest of your stack.
  • Scope minimally. A webhook worker that only looks up emails should have email:read, not email:send.
  • Pin to a domain when you can. A pinned key can only send as that domain even if it is exfiltrated.
  • Rotate on suspicion. Keys are cheap to issue. Rotate before you investigate, not after.

Loading from env

.env
SEVK_API_KEY=sevk_xxxxxxxxx
index.ts
import { Sevk } from 'sevk'
if (!process.env.SEVK_API_KEY) {
throw new Error('SEVK_API_KEY is not set')
}
const sevk = new Sevk(process.env.SEVK_API_KEY)