Misar IO Docs

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:

  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