API Keys
Create, list, and revoke API keys programmatically via GET/POST/DELETE /v1/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.
Authentication
These endpoints manage keys, so they require a logged-in session cookie (dashboard login), not an API 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 |
List API keys
/mail/v1/keysList all API keys for your account. Raw key values are never returned — only the prefix (msk_ + 8 chars) for identification.
Response fields
successbooleantrue when the request succeeded.
keysArray<ApiKey>All keys for the account. Each key includes id, name, key_prefix, scopes, allowed_account_id, is_active, last_used_at, created_at, and expires_at.
curl https://api.misar.io/mail/v1/keys \
-H "Cookie: <session_cookie>"{
"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
}
]
}Create an API key
/mail/v1/keysCreate a new API key. The raw key (msk_...) is returned only in this response — it cannot be retrieved later.
Request body
namestringbodyrequiredHuman-readable label (max 80 chars).
scopesstring[]bodydefault: ["send"]Scopes granted to the key. See the scope table below.
allowedAccountIdUUIDbodyRestrict key to a single email account.
expiresAtISO 8601bodyOptional expiry — must be a future datetime.
Response fields
successbooleantrue when the key was created.
keystringThe raw API key — shown exactly once.
keyIdUUIDIdentifier for the created key.
prefixstringKey prefix (msk_ + 8 chars) for identification.
namestringThe label supplied at creation.
scopesstringComma-separated granted scopes.
createdAtISO 8601Creation timestamp.
expiresAtISO 8601 | nullExpiry timestamp, or null if non-expiring.
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"]
}'{
"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
}{
"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"
}Save the key value now. It is shown exactly once. If lost, delete this key and create a new one.
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.
Revoke an API key
/mail/v1/keysRevoke a key immediately. Revoked keys return 401 on all subsequent API calls.
Query parameters
idUUIDqueryrequiredID of the key to revoke.
Response fields
successbooleantrue when the key was revoked.
curl -X DELETE \
"https://api.misar.io/mail/v1/keys?id=3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Cookie: <session_cookie>"{ "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