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://api.misar.io/mail/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://api.misar.io/mail/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://api.misar.io/mail/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://api.misar.io/mail/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://api.misar.io/mail/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