MisarMisar Docs
MisarMailMisarBlogMisarReachMisarPostMisarDevMisarCoderMisarSEOMisar PlatformMisar SSO
API Reference

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

MethodPathDescription
GET/api/v1/keysList all API keys (no raw values returned)
POST/api/v1/keysCreate a new API key
DELETE/api/v1/keys?id=<uuid>Revoke a key

List API keys

GET/mail/v1/keys

List all API keys for your account. Raw key values are never returned — only the prefix (msk_ + 8 chars) for identification.

Response fields

successboolean

true 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.

Request
curl https://api.misar.io/mail/v1/keys \
  -H "Cookie: <session_cookie>"
200 — OK
{
  "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

POST/mail/v1/keys

Create a new API key. The raw key (msk_...) is returned only in this response — it cannot be retrieved later.

Request body

namestringbodyrequired

Human-readable label (max 80 chars).

scopesstring[]bodydefault: ["send"]

Scopes granted to the key. See the scope table below.

allowedAccountIdUUIDbody

Restrict key to a single email account.

expiresAtISO 8601body

Optional expiry — must be a future datetime.

Response fields

successboolean

true when the key was created.

keystring

The raw API key — shown exactly once.

keyIdUUID

Identifier for the created key.

prefixstring

Key prefix (msk_ + 8 chars) for identification.

namestring

The label supplied at creation.

scopesstring

Comma-separated granted scopes.

createdAtISO 8601

Creation timestamp.

expiresAtISO 8601 | null

Expiry timestamp, or null if non-expiring.

Request
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"]
  }'
201 — Created
{
  "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
}
403 — Plan limit reached
{
  "success":           false,
  "error":             "API key limit reached (5/5). Upgrade your plan or enable overage billing in Settings > Billing.",
  "limit":             5,
  "current":           5,
  "feature":           "api_keys",
  "overage_available": false,
  "upgrade_url":       "/settings?tab=billing",
  "billing_url":       "/settings?tab=billing"
}

Save the key value now. It is shown exactly once. If lost, delete this key and create a new one.

Scopes

ScopeAccess granted
sendPOST /v1/send — all email sending
send:transactionalPOST /v1/send — transactional only
send:marketingPOST /v1/send — marketing only
readRead-only access to all owned resources
writeWrite access to all owned resources
contactsRead/write contacts, segments, scoring
analyticsRead analytics, reports, tracking data
sandboxSend in sandbox mode (not delivered)
validatePOST /v1/validate — email address validation
ipsManage dedicated IPs
act-as-userAct on behalf of a user (delegated server-to-server access)

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

DELETE/mail/v1/keys

Revoke a key immediately. Revoked keys return 401 on all subsequent API calls.

Query parameters

idUUIDqueryrequired

ID of the key to revoke.

Response fields

successboolean

true when the key was revoked.

Request
curl -X DELETE \
  "https://api.misar.io/mail/v1/keys?id=3fa85f64-5717-4562-b3fc-2c963f66afa6" \
  -H "Cookie: <session_cookie>"
200 — OK
{ "success": true }

Error responses

CodeReason
400id missing or not a valid UUID
404Key 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:

  1. POST /api/v1/keys — create new key with identical scopes
  2. Deploy new key to your service
  3. Verify traffic is using the new key (last_used_at on old key stops updating)
  4. 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