API Keys
Create, list, and revoke API keys programmatically via GET/POST/DELETE /v1/keys
API Keys
API keys authenticate all programmatic access to the MisarMail API. Keys start with msk_ and are only shown once at creation — store them immediately in a secrets manager or environment variable.
Auth: Session cookie (dashboard login) — these endpoints manage keys, so they require a logged-in session, not a key.
Plan gate: The number of active API keys is limited by plan (Free: 1, Pro: 5, Max: unlimited). Creating a key when at the limit returns 403.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v1/keys | List all API keys (no raw values returned) |
POST | /api/v1/keys | Create a new API key |
DELETE | /api/v1/keys?id=<uuid> | Revoke a key |
GET /api/v1/keys
List all API keys for your account. Raw key values are never returned — only the prefix (msk_ + 8 chars) for identification.
curl https://mail.misar.io/api/v1/keys \
-H "Cookie: <session_cookie>"Response
{
"success": true,
"keys": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "Production sends",
"key_prefix": "msk_a1b2c3d4",
"scopes": "send,analytics",
"allowed_account_id": null,
"is_active": true,
"last_used_at": "2026-04-19T10:22:00Z",
"created_at": "2026-03-01T09:00:00Z",
"expires_at": null
}
]
}POST /api/v1/keys
Create a new API key. The raw key (msk_...) is returned only in this response — it cannot be retrieved later.
curl -X POST https://mail.misar.io/api/v1/keys \
-H "Cookie: <session_cookie>" \
-H "Content-Type: application/json" \
-d '{
"name": "CI pipeline key",
"scopes": ["send", "analytics"]
}'Request Fields
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | ✓ | Human-readable label (max 80 chars) |
scopes | string[] | — | Defaults to ["send"]. See scope table below. |
allowedAccountId | UUID | — | Restrict key to a single email account |
expiresAt | ISO 8601 | — | Optional expiry — must be a future datetime |
Scopes
| Scope | Access granted |
|---|---|
send | POST /v1/send — all email sending |
send:transactional | POST /v1/send — transactional only |
send:marketing | POST /v1/send — marketing only |
contacts | Read/write contacts, segments, scoring |
campaigns | Create, update, send campaigns |
templates | Create and update email templates |
automations | Create and update automation workflows |
analytics | Read analytics, reports, tracking data |
validate | POST /v1/validate — email address validation |
read | Read-only access to all owned resources |
write | Write access to all owned resources |
sandbox | Send in sandbox mode (not delivered) |
Grant only the minimum scopes needed. A key with send cannot read contacts or campaigns — scope separation limits blast radius if a key is compromised.
Response 201
{
"success": true,
"key": "msk_a1b2c3d4e5f6...",
"keyId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"prefix": "msk_a1b2c3d4",
"name": "CI pipeline key",
"scopes": "send,analytics",
"createdAt": "2026-04-20T12:00:00Z",
"expiresAt": null
}Save the key value now. It is shown exactly once. If lost, delete this key and create a new one.
Plan limit error 403
{
"success": false,
"error": "API key limit reached (5 / 5). Upgrade to Max for unlimited keys.",
"feature": "api_keys",
"current": 5,
"limit": 5,
"required_plan": "max"
}DELETE /api/v1/keys?id=<uuid>
Revoke a key immediately. Revoked keys return 401 on all subsequent API calls.
curl -X DELETE \
"https://mail.misar.io/api/v1/keys?id=3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Cookie: <session_cookie>"Response 200
{ "success": true }Error responses
| Code | Reason |
|---|---|
400 | id missing or not a valid UUID |
404 | Key not found or belongs to another user |
Account-Scoped Keys
To restrict a key so it can only send from one specific email account (useful for multi-account setups or client isolation):
{
"name": "Client A sends only",
"scopes": ["send"],
"allowedAccountId": "550e8400-e29b-41d4-a716-446655440000"
}When allowedAccountId is set, any send request where from.email doesn't match that account returns 403.
Key Rotation Pattern
Rotate keys without downtime:
POST /api/v1/keys— create new key with identical scopes- Deploy new key to your service
- Verify traffic is using the new key (
last_used_aton old key stops updating) DELETE /api/v1/keys?id=<old_id>— revoke the old key
Examples
# Create a send-only key expiring in 90 days
curl -X POST https://mail.misar.io/api/v1/keys \
-H "Cookie: <session_cookie>" \
-H "Content-Type: application/json" \
-d '{
"name": "Transactional key (90d)",
"scopes": ["send:transactional"],
"expiresAt": "2026-07-20T00:00:00Z"
}'const res = await fetch("https://mail.misar.io/api/v1/keys", {
method: "POST",
headers: { "Cookie": sessionCookie, "Content-Type": "application/json" },
body: JSON.stringify({
name: "Transactional key (90d)",
scopes: ["send:transactional"],
expiresAt: new Date(Date.now() + 90 * 86400_000).toISOString(),
}),
});
const { key } = await res.json();
// Store `key` in your secrets manager immediately