Account
The Account endpoints.
Auth: Session token · Public · API key · Base URL:
https://api.mailkite.dev
Endpoints
Each row expands to its request and response schema. The address bar follows along, so any endpoint can be linked to directly.
GET /api/keys Get the account's unrestricted API key (mk_live_…). Read-or-create: the first call mints it.
Get the account's unrestricted API key (mk_live_…). Read-or-create: the first call mints it.
Response body
api-key-response.json| Field | Type | Notes |
|---|---|---|
| key req | string | The account's unrestricted API key (mk_live_…). Read-or-create: the first call mints it. rotate() invalidates the old key and returns a fresh one. |
An actual response, recorded from the conformance suite.
{
"key": "mk_live_9hK2mQx7Tw4bVnR8"
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/api-key-response.json",
"title": "Account API key",
"type": "object",
"required": [
"key"
],
"additionalProperties": false,
"properties": {
"key": {
"type": "string",
"description": "The account's unrestricted API key (mk_live_…). Read-or-create: the first call mints it. rotate() invalidates the old key and returns a fresh one.",
"examples": [
"mk_live_9hK2mQx7Tw4bVnR8"
]
}
}
} POST /api/keys/rotate Rotate the account API key: the old key stops working immediately and a fresh one is returned. The plaintext is only shown here.
Rotate the account API key: the old key stops working immediately and a fresh one is returned. The plaintext is only shown here.
Response body
api-key-response.json| Field | Type | Notes |
|---|---|---|
| key req | string | The account's unrestricted API key (mk_live_…). Read-or-create: the first call mints it. rotate() invalidates the old key and returns a fresh one. |
An actual response, recorded from the conformance suite.
{
"key": "mk_live_3fW7pZr4Xj8kQsN2"
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/api-key-response.json",
"title": "Account API key",
"type": "object",
"required": [
"key"
],
"additionalProperties": false,
"properties": {
"key": {
"type": "string",
"description": "The account's unrestricted API key (mk_live_…). Read-or-create: the first call mints it. rotate() invalidates the old key and returns a fresh one.",
"examples": [
"mk_live_9hK2mQx7Tw4bVnR8"
]
}
}
} GET /api/keys/scoped List the account's domain-scoped API keys. A scoped key can send and manage only its one domain — hand one to each site or CI job so a leak burns only that surface.
List the account's domain-scoped API keys. A scoped key can send and manage only its one domain — hand one to each site or CI job so a leak burns only that surface.
| Field | Type | Notes |
|---|---|---|
| id req | string | Key id (key_…). |
| user_id req | string | Owning account (usr_…). |
| domain_id req | string | The one domain this key is scoped to (dom_…). A scoped key can send and manage only this domain — ideal for a single site or CI job. |
| domain req | string | Denormalized domain name, for display and fast scope checks. |
| name req | string | User-supplied label (e.g. the site or service using it). |
| key req | string | The key material (mk_live_…). Returned in full on list/create — treat like a password. |
| created_at req | number | Creation time, ms epoch. |
| last_used_at req | number · null | Last authenticated use, ms epoch — null if never used. |
An actual array of results, recorded from the conformance suite.
[
{
"id": "key_8Rt2NvQp",
"user_id": "usr_7Fj3MnQw",
"domain_id": "dom_2VbXqTpN8rKw",
"domain": "myapp.ai",
"name": "blog-wordpress",
"key": "mk_live_5sD8wYx2Kq9nBvC4",
"created_at": 1753900000000,
"last_used_at": null
}
] {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/scoped-key.json",
"title": "Domain-scoped API key",
"type": "object",
"required": [
"id",
"user_id",
"domain_id",
"domain",
"name",
"key",
"created_at",
"last_used_at"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "Key id (key_…).",
"examples": [
"key_8Rt2NvQp"
]
},
"user_id": {
"type": "string",
"description": "Owning account (usr_…).",
"examples": [
"usr_7Fj3MnQw"
]
},
"domain_id": {
"type": "string",
"description": "The one domain this key is scoped to (dom_…). A scoped key can send and manage only this domain — ideal for a single site or CI job.",
"examples": [
"dom_2VbXqTpN8rKw"
]
},
"domain": {
"type": "string",
"description": "Denormalized domain name, for display and fast scope checks.",
"examples": [
"myapp.ai"
]
},
"name": {
"type": "string",
"description": "User-supplied label (e.g. the site or service using it).",
"examples": [
"blog-wordpress"
]
},
"key": {
"type": "string",
"description": "The key material (mk_live_…). Returned in full on list/create — treat like a password.",
"examples": [
"mk_live_5sD8wYx2Kq9nBvC4"
]
},
"created_at": {
"type": "number",
"description": "Creation time, ms epoch.",
"examples": [
1753900000000
]
},
"last_used_at": {
"type": [
"number",
"null"
],
"description": "Last authenticated use, ms epoch — null if never used.",
"examples": [
null
]
}
}
} POST /api/keys/scoped Create a key scoped to one domain. Ideal for per-site installs (e.g. a WordPress plugin) — the site never holds the account master key.
Create a key scoped to one domain. Ideal for per-site installs (e.g. a WordPress plugin) — the site never holds the account master key.
Request body
create-scoped-key-request.json| Field | Type | Notes |
|---|---|---|
| domainId req | string | The domain (dom_…) the new key is limited to. Must belong to the calling account. |
| name | string | Optional label shown in the dashboard (e.g. which site/integration holds this key). |
The smallest body that makes this call — every other field is optional.
{
"domainId": "dom_2VbXqTpN8rKw",
"name": "blog-wordpress"
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/create-scoped-key-request.json",
"title": "Create scoped key request body",
"type": "object",
"required": [
"domainId"
],
"additionalProperties": false,
"properties": {
"domainId": {
"type": "string",
"description": "The domain (dom_…) the new key is limited to. Must belong to the calling account.",
"examples": [
"dom_2VbXqTpN8rKw"
]
},
"name": {
"type": "string",
"description": "Optional label shown in the dashboard (e.g. which site/integration holds this key).",
"examples": [
"blog-wordpress"
]
}
}
} Response body
scoped-key.json| Field | Type | Notes |
|---|---|---|
| id req | string | Key id (key_…). |
| user_id req | string | Owning account (usr_…). |
| domain_id req | string | The one domain this key is scoped to (dom_…). A scoped key can send and manage only this domain — ideal for a single site or CI job. |
| domain req | string | Denormalized domain name, for display and fast scope checks. |
| name req | string | User-supplied label (e.g. the site or service using it). |
| key req | string | The key material (mk_live_…). Returned in full on list/create — treat like a password. |
| created_at req | number | Creation time, ms epoch. |
| last_used_at req | number · null | Last authenticated use, ms epoch — null if never used. |
An actual response, recorded from the conformance suite.
{
"id": "key_8Rt2NvQp",
"user_id": "usr_7Fj3MnQw",
"domain_id": "dom_2VbXqTpN8rKw",
"domain": "myapp.ai",
"name": "blog-wordpress",
"key": "mk_live_5sD8wYx2Kq9nBvC4",
"created_at": 1753900000000,
"last_used_at": null
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/scoped-key.json",
"title": "Domain-scoped API key",
"type": "object",
"required": [
"id",
"user_id",
"domain_id",
"domain",
"name",
"key",
"created_at",
"last_used_at"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "Key id (key_…).",
"examples": [
"key_8Rt2NvQp"
]
},
"user_id": {
"type": "string",
"description": "Owning account (usr_…).",
"examples": [
"usr_7Fj3MnQw"
]
},
"domain_id": {
"type": "string",
"description": "The one domain this key is scoped to (dom_…). A scoped key can send and manage only this domain — ideal for a single site or CI job.",
"examples": [
"dom_2VbXqTpN8rKw"
]
},
"domain": {
"type": "string",
"description": "Denormalized domain name, for display and fast scope checks.",
"examples": [
"myapp.ai"
]
},
"name": {
"type": "string",
"description": "User-supplied label (e.g. the site or service using it).",
"examples": [
"blog-wordpress"
]
},
"key": {
"type": "string",
"description": "The key material (mk_live_…). Returned in full on list/create — treat like a password.",
"examples": [
"mk_live_5sD8wYx2Kq9nBvC4"
]
},
"created_at": {
"type": "number",
"description": "Creation time, ms epoch.",
"examples": [
1753900000000
]
},
"last_used_at": {
"type": [
"number",
"null"
],
"description": "Last authenticated use, ms epoch — null if never used.",
"examples": [
null
]
}
}
} DELETE /api/keys/scoped/:id Revoke a domain-scoped key. Takes effect immediately.
Revoke a domain-scoped key. Takes effect immediately.
Response body
ok-response.jsonThe uniform acknowledgement for deletes and other actions with nothing else to report.
| Field | Type | Notes |
|---|---|---|
| ok req | true | Always true — a non-2xx error body is returned otherwise. |
An actual response, recorded from the conformance suite.
{
"ok": true
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/ok-response.json",
"title": "OK",
"description": "The uniform acknowledgement for deletes and other actions with nothing else to report.",
"type": "object",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"const": true,
"description": "Always true — a non-2xx error body is returned otherwise."
}
}
} GET /api/app-passwords List the account's app passwords. Each one opens a mailbox over IMAP and/or the mailbox API, scoped to a domain and an address pattern within it.
List the account's app passwords. Each one opens a mailbox over IMAP and/or the mailbox API, scoped to a domain and an address pattern within it.
A credential that opens a mailbox over IMAP and/or the mailbox API. Scoped to one domain and an address pattern within it, so a single password can serve every address on a domain or exactly one. The secret itself is only ever returned once, at creation.
| Field | Type | Notes |
|---|---|---|
| id req | string | App password id (apw_…). |
| user_id req | string | Owning account (usr_…). |
| label req | string | User-supplied name, e.g. which client or agent holds it. |
| domain_id | string · null | The hosted domain this password covers (dom_…). Null only for legacy account-wide credentials. |
| domain req | string | Domain name this password covers. The literal `*` marks a legacy account-wide IMAP credential, which covers every domain the account owns; new passwords always name one domain. |
| address req | string | Local-part pattern within the domain: `*` (every address), `hello` (exact), `support-*` / `*-agent` (glob). Same wildcard grammar as inbound routes. |
| protocols req | "imap" · "api"[] | What this password may authenticate: `imap` for IMAP LOGIN, `api` for Bearer auth on the mailbox API. At least one. |
| key_prefix req | string | First characters of the secret, for display (the rest is never retrievable). |
| created_at req | number | Creation time, ms epoch. |
| last_used_at req | number · null | Last authenticated use, ms epoch — null if never used. |
| legacy req | boolean | True for pre-existing IMAP-only credentials (mk_imap_…) surfaced in this shape. They keep working indefinitely but cannot grant `api`. |
| secret | string | The password itself (mk_pw_…). Returned ONLY in the create response — store it then, it is never shown again. |
An actual array of results, recorded from the conformance suite.
[
{
"id": "apw_6Kd3PqR7",
"user_id": "usr_7Fj3MnQw",
"label": "support inbox agent",
"domain_id": "dom_2VbXqTpN8rKw",
"domain": "acme.dev",
"address": "support-*",
"protocols": [
"imap",
"api"
],
"key_prefix": "mk_pw_9tQ2xVb",
"created_at": 1753900000000,
"last_used_at": null,
"legacy": false
}
] {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/app-password.json",
"title": "App password",
"description": "A credential that opens a mailbox over IMAP and/or the mailbox API. Scoped to one domain and an address pattern within it, so a single password can serve every address on a domain or exactly one. The secret itself is only ever returned once, at creation.",
"type": "object",
"required": [
"id",
"user_id",
"label",
"domain",
"address",
"protocols",
"key_prefix",
"created_at",
"last_used_at",
"legacy"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "App password id (apw_…).",
"examples": [
"apw_6Kd3PqR7"
]
},
"user_id": {
"type": "string",
"description": "Owning account (usr_…).",
"examples": [
"usr_7Fj3MnQw"
]
},
"label": {
"type": "string",
"description": "User-supplied name, e.g. which client or agent holds it.",
"examples": [
"support inbox agent"
]
},
"domain_id": {
"type": [
"string",
"null"
],
"description": "The hosted domain this password covers (dom_…). Null only for legacy account-wide credentials.",
"examples": [
"dom_2VbXqTpN8rKw"
]
},
"domain": {
"type": "string",
"description": "Domain name this password covers. The literal `*` marks a legacy account-wide IMAP credential, which covers every domain the account owns; new passwords always name one domain.",
"examples": [
"acme.dev"
]
},
"address": {
"type": "string",
"description": "Local-part pattern within the domain: `*` (every address), `hello` (exact), `support-*` / `*-agent` (glob). Same wildcard grammar as inbound routes.",
"examples": [
"support-*"
]
},
"protocols": {
"type": "array",
"description": "What this password may authenticate: `imap` for IMAP LOGIN, `api` for Bearer auth on the mailbox API. At least one.",
"items": {
"type": "string",
"enum": [
"imap",
"api"
]
},
"examples": [
[
"imap",
"api"
]
]
},
"key_prefix": {
"type": "string",
"description": "First characters of the secret, for display (the rest is never retrievable).",
"examples": [
"mk_pw_9tQ2xVb"
]
},
"created_at": {
"type": "number",
"description": "Creation time, ms epoch.",
"examples": [
1753900000000
]
},
"last_used_at": {
"type": [
"number",
"null"
],
"description": "Last authenticated use, ms epoch — null if never used.",
"examples": [
null
]
},
"legacy": {
"type": "boolean",
"description": "True for pre-existing IMAP-only credentials (mk_imap_…) surfaced in this shape. They keep working indefinitely but cannot grant `api`.",
"examples": [
false
]
},
"secret": {
"type": "string",
"description": "The password itself (mk_pw_…). Returned ONLY in the create response — store it then, it is never shown again.",
"examples": [
"mk_pw_9tQ2xVb4Nm8kLc3Wd7Rf1Zy6Hs0Ap5Ej"
]
}
}
} POST /api/app-passwords Create an app password for one domain and address pattern. Hand it to a mail client or an agent — the secret is returned once and never again.
Create an app password for one domain and address pattern. Hand it to a mail client or an agent — the secret is returned once and never again.
Request body
create-app-password-request.json| Field | Type | Notes |
|---|---|---|
| domain req | string | The hosted domain this password covers. Must belong to the calling account. |
| domainId | string | The domain by id (dom_…), as an alternative to `domain`. |
| address | string | Local-part pattern within the domain — `*` (default, every address), `hello`, `support-*`, `*-agent`. No `@`: the domain is already named. |
| protocols | "imap" · "api"[] | What the password may authenticate. Defaults to `["imap"]`. |
| label | string | Optional name shown in the dashboard (e.g. the client or agent holding it). |
The smallest body that makes this call — every other field is optional.
{
"domain": "acme.dev",
"address": "support-*",
"protocols": [
"imap",
"api"
],
"label": "support inbox agent"
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/create-app-password-request.json",
"title": "Create app password request body",
"type": "object",
"required": [
"domain"
],
"additionalProperties": false,
"properties": {
"domain": {
"type": "string",
"description": "The hosted domain this password covers. Must belong to the calling account.",
"examples": [
"acme.dev"
]
},
"domainId": {
"type": "string",
"description": "The domain by id (dom_…), as an alternative to `domain`.",
"examples": [
"dom_2VbXqTpN8rKw"
]
},
"address": {
"type": "string",
"description": "Local-part pattern within the domain — `*` (default, every address), `hello`, `support-*`, `*-agent`. No `@`: the domain is already named.",
"examples": [
"support-*"
]
},
"protocols": {
"type": "array",
"description": "What the password may authenticate. Defaults to `[\"imap\"]`.",
"items": {
"type": "string",
"enum": [
"imap",
"api"
]
},
"examples": [
[
"imap",
"api"
]
]
},
"label": {
"type": "string",
"description": "Optional name shown in the dashboard (e.g. the client or agent holding it).",
"examples": [
"support inbox agent"
]
}
}
} Response body
app-password.jsonA credential that opens a mailbox over IMAP and/or the mailbox API. Scoped to one domain and an address pattern within it, so a single password can serve every address on a domain or exactly one. The secret itself is only ever returned once, at creation.
| Field | Type | Notes |
|---|---|---|
| id req | string | App password id (apw_…). |
| user_id req | string | Owning account (usr_…). |
| label req | string | User-supplied name, e.g. which client or agent holds it. |
| domain_id | string · null | The hosted domain this password covers (dom_…). Null only for legacy account-wide credentials. |
| domain req | string | Domain name this password covers. The literal `*` marks a legacy account-wide IMAP credential, which covers every domain the account owns; new passwords always name one domain. |
| address req | string | Local-part pattern within the domain: `*` (every address), `hello` (exact), `support-*` / `*-agent` (glob). Same wildcard grammar as inbound routes. |
| protocols req | "imap" · "api"[] | What this password may authenticate: `imap` for IMAP LOGIN, `api` for Bearer auth on the mailbox API. At least one. |
| key_prefix req | string | First characters of the secret, for display (the rest is never retrievable). |
| created_at req | number | Creation time, ms epoch. |
| last_used_at req | number · null | Last authenticated use, ms epoch — null if never used. |
| legacy req | boolean | True for pre-existing IMAP-only credentials (mk_imap_…) surfaced in this shape. They keep working indefinitely but cannot grant `api`. |
| secret | string | The password itself (mk_pw_…). Returned ONLY in the create response — store it then, it is never shown again. |
An actual response, recorded from the conformance suite.
{
"id": "apw_6Kd3PqR7",
"user_id": "usr_7Fj3MnQw",
"label": "support inbox agent",
"domain_id": "dom_2VbXqTpN8rKw",
"domain": "acme.dev",
"address": "support-*",
"protocols": [
"imap",
"api"
],
"key_prefix": "mk_pw_9tQ2xVb",
"created_at": 1753900000000,
"last_used_at": null,
"legacy": false,
"secret": "mk_pw_9tQ2xVb4Nm8kLc3Wd7Rf1Zy6Hs0Ap5Ej"
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/app-password.json",
"title": "App password",
"description": "A credential that opens a mailbox over IMAP and/or the mailbox API. Scoped to one domain and an address pattern within it, so a single password can serve every address on a domain or exactly one. The secret itself is only ever returned once, at creation.",
"type": "object",
"required": [
"id",
"user_id",
"label",
"domain",
"address",
"protocols",
"key_prefix",
"created_at",
"last_used_at",
"legacy"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "App password id (apw_…).",
"examples": [
"apw_6Kd3PqR7"
]
},
"user_id": {
"type": "string",
"description": "Owning account (usr_…).",
"examples": [
"usr_7Fj3MnQw"
]
},
"label": {
"type": "string",
"description": "User-supplied name, e.g. which client or agent holds it.",
"examples": [
"support inbox agent"
]
},
"domain_id": {
"type": [
"string",
"null"
],
"description": "The hosted domain this password covers (dom_…). Null only for legacy account-wide credentials.",
"examples": [
"dom_2VbXqTpN8rKw"
]
},
"domain": {
"type": "string",
"description": "Domain name this password covers. The literal `*` marks a legacy account-wide IMAP credential, which covers every domain the account owns; new passwords always name one domain.",
"examples": [
"acme.dev"
]
},
"address": {
"type": "string",
"description": "Local-part pattern within the domain: `*` (every address), `hello` (exact), `support-*` / `*-agent` (glob). Same wildcard grammar as inbound routes.",
"examples": [
"support-*"
]
},
"protocols": {
"type": "array",
"description": "What this password may authenticate: `imap` for IMAP LOGIN, `api` for Bearer auth on the mailbox API. At least one.",
"items": {
"type": "string",
"enum": [
"imap",
"api"
]
},
"examples": [
[
"imap",
"api"
]
]
},
"key_prefix": {
"type": "string",
"description": "First characters of the secret, for display (the rest is never retrievable).",
"examples": [
"mk_pw_9tQ2xVb"
]
},
"created_at": {
"type": "number",
"description": "Creation time, ms epoch.",
"examples": [
1753900000000
]
},
"last_used_at": {
"type": [
"number",
"null"
],
"description": "Last authenticated use, ms epoch — null if never used.",
"examples": [
null
]
},
"legacy": {
"type": "boolean",
"description": "True for pre-existing IMAP-only credentials (mk_imap_…) surfaced in this shape. They keep working indefinitely but cannot grant `api`.",
"examples": [
false
]
},
"secret": {
"type": "string",
"description": "The password itself (mk_pw_…). Returned ONLY in the create response — store it then, it is never shown again.",
"examples": [
"mk_pw_9tQ2xVb4Nm8kLc3Wd7Rf1Zy6Hs0Ap5Ej"
]
}
}
} DELETE /api/app-passwords/:id Revoke an app password. Takes effect immediately — any IMAP session or API call using it stops authenticating.
Revoke an app password. Takes effect immediately — any IMAP session or API call using it stops authenticating.
Response body
ok-response.jsonThe uniform acknowledgement for deletes and other actions with nothing else to report.
| Field | Type | Notes |
|---|---|---|
| ok req | true | Always true — a non-2xx error body is returned otherwise. |
An actual response, recorded from the conformance suite.
{
"ok": true
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/ok-response.json",
"title": "OK",
"description": "The uniform acknowledgement for deletes and other actions with nothing else to report.",
"type": "object",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"const": true,
"description": "Always true — a non-2xx error body is returned otherwise."
}
}
} GET /api/billing/usage Current billing-period usage: emails used vs the plan's included bucket (null = unlimited), AI actions, and the overage state that gates sending. Powers quota meters in dashboards and integrations.
Current billing-period usage: emails used vs the plan's included bucket (null = unlimited), AI actions, and the overage state that gates sending. Powers quota meters in dashboards and integrations.
Response body
usage-response.json| Field | Type | Notes |
|---|---|---|
| plan req | string | The account's plan id. |
| emails req | object | Email volume this billing period (inbound + outbound share one bucket). |
| emails.used req | number | Emails counted so far this period. |
| emails.included req | number · null | The plan's included monthly emails — null means unlimited. |
| aiActions req | object | AI agent actions this period (pay-as-you-go — no included bucket). |
| aiActions.used req | number | AI actions counted so far this period. |
| meteringEnabled req | boolean | True when overage metering is active (billing configured and a subscription exists). |
| meters req | string[] | The usage meters billed on this account. |
| overage req | object | Overage state — drives the dashboard notice and the plugin quota bar. |
| overage.over req | boolean | Past the plan's included email bucket this period. |
| overage.metered req | boolean | Overage is being metered and billed normally. |
| overage.blocked req | boolean | Sending is blocked until the payment card is fixed. |
| overage.status req | string · null | Card-gate status when in overage (ok | failed …) — null outside overage. |
| overage.code req | string · null | Machine-readable reason code when blocked — null otherwise. |
An actual response, recorded from the conformance suite.
{
"plan": "free",
"emails": {
"used": 1240,
"included": 3000
},
"aiActions": {
"used": 12
},
"meteringEnabled": false,
"meters": [
"email",
"ai_actions"
],
"overage": {
"over": false,
"metered": false,
"blocked": false,
"status": null,
"code": null
}
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/usage-response.json",
"title": "Current billing-period usage",
"type": "object",
"required": [
"plan",
"emails",
"aiActions",
"meteringEnabled",
"meters",
"overage"
],
"additionalProperties": false,
"properties": {
"plan": {
"type": "string",
"description": "The account's plan id.",
"examples": [
"free",
"pro"
]
},
"emails": {
"type": "object",
"description": "Email volume this billing period (inbound + outbound share one bucket).",
"required": [
"used",
"included"
],
"additionalProperties": false,
"properties": {
"used": {
"type": "number",
"description": "Emails counted so far this period.",
"examples": [
1240
]
},
"included": {
"type": [
"number",
"null"
],
"description": "The plan's included monthly emails — null means unlimited.",
"examples": [
3000
]
}
}
},
"aiActions": {
"type": "object",
"description": "AI agent actions this period (pay-as-you-go — no included bucket).",
"required": [
"used"
],
"additionalProperties": false,
"properties": {
"used": {
"type": "number",
"description": "AI actions counted so far this period.",
"examples": [
12
]
}
}
},
"meteringEnabled": {
"type": "boolean",
"description": "True when overage metering is active (billing configured and a subscription exists).",
"examples": [
false
]
},
"meters": {
"type": "array",
"description": "The usage meters billed on this account.",
"items": {
"type": "string"
},
"examples": [
[
"email",
"ai_actions"
]
]
},
"overage": {
"type": "object",
"description": "Overage state — drives the dashboard notice and the plugin quota bar.",
"required": [
"over",
"metered",
"blocked",
"status",
"code"
],
"additionalProperties": false,
"properties": {
"over": {
"type": "boolean",
"description": "Past the plan's included email bucket this period.",
"examples": [
false
]
},
"metered": {
"type": "boolean",
"description": "Overage is being metered and billed normally.",
"examples": [
false
]
},
"blocked": {
"type": "boolean",
"description": "Sending is blocked until the payment card is fixed.",
"examples": [
false
]
},
"status": {
"type": [
"string",
"null"
],
"description": "Card-gate status when in overage (ok | failed …) — null outside overage.",
"examples": [
null
]
},
"code": {
"type": [
"string",
"null"
],
"description": "Machine-readable reason code when blocked — null otherwise.",
"examples": [
null
]
}
}
}
}
} POST /api/v1/provision Create a MailKite account from just an email — no password. Returns the new account's API key immediately; a verification link is emailed, and SENDING stays blocked until the address is verified (poll me()). An existing email returns 409 account_exists with no credentials. Powers plugin/CLI onboarding.
Create a MailKite account from just an email — no password. Returns the new account's API key immediately; a verification link is emailed, and SENDING stays blocked until the address is verified (poll me()). An existing email returns 409 account_exists with no credentials. Powers plugin/CLI onboarding.
Request body
register-request.json| Field | Type | Notes |
|---|---|---|
| email req | string | The account email. A verification link is sent to it — the account cannot send email until the address is verified, which is what makes registering safe without a password. |
| channel | string | Distribution-channel slug this registration came through (e.g. wordpress-plugin). Invalid values are dropped, never an error. |
| ref | string | Referral code of the account that referred this signup, when any. |
| referrer | string | First-touch landing referrer URL, when known. Invalid values are dropped, never an error. |
The smallest body that makes this call — every other field is optional.
{
"email": "owner@myapp.ai",
"channel": "wordpress-plugin"
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/register-request.json",
"title": "Register request body",
"type": "object",
"required": [
"email"
],
"additionalProperties": false,
"properties": {
"email": {
"type": "string",
"description": "The account email. A verification link is sent to it — the account cannot send email until the address is verified, which is what makes registering safe without a password.",
"examples": [
"owner@myapp.ai"
]
},
"channel": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9_-]{0,31}$",
"description": "Distribution-channel slug this registration came through (e.g. wordpress-plugin). Invalid values are dropped, never an error.",
"examples": [
"wordpress-plugin"
]
},
"ref": {
"type": "string",
"description": "Referral code of the account that referred this signup, when any.",
"examples": [
"xmbf3bd0"
]
},
"referrer": {
"type": "string",
"description": "First-touch landing referrer URL, when known. Invalid values are dropped, never an error.",
"examples": [
"https://wordpress.org/plugins/mailkite-smtp/"
]
}
}
} Response body
register-response.json| Field | Type | Notes |
|---|---|---|
| api_key req | string | The new account's API key (mk_live_…) — store it now; it is only returned at registration. Sending stays blocked until the email is verified. |
| user_id req | string | The new account id (usr_…). |
| email req | string | The normalized account email the verification link was sent to. |
| email_verified req | boolean | Always false at registration — poll me() until it flips after the user clicks the link. |
| is_new req | boolean | Always true — an existing email returns 409 account_exists (with no credentials) instead. |
An actual response, recorded from the conformance suite.
{
"api_key": "mk_live_9hK2mQx7Tw4bVnR8",
"user_id": "usr_7Fj3MnQw",
"email": "owner@myapp.ai",
"email_verified": false,
"is_new": true
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/register-response.json",
"title": "Register result",
"type": "object",
"required": [
"api_key",
"user_id",
"email",
"email_verified",
"is_new"
],
"additionalProperties": false,
"properties": {
"api_key": {
"type": "string",
"description": "The new account's API key (mk_live_…) — store it now; it is only returned at registration. Sending stays blocked until the email is verified.",
"examples": [
"mk_live_9hK2mQx7Tw4bVnR8"
]
},
"user_id": {
"type": "string",
"description": "The new account id (usr_…).",
"examples": [
"usr_7Fj3MnQw"
]
},
"email": {
"type": "string",
"description": "The normalized account email the verification link was sent to.",
"examples": [
"owner@myapp.ai"
]
},
"email_verified": {
"type": "boolean",
"description": "Always false at registration — poll me() until it flips after the user clicks the link.",
"examples": [
false
]
},
"is_new": {
"type": "boolean",
"description": "Always true — an existing email returns 409 account_exists (with no credentials) instead.",
"examples": [
true
]
}
}
} GET /v1/me The account behind this credential: email, whether it is verified (sending is blocked until it is), and plan. Use to poll verification state after register().
The account behind this credential: email, whether it is verified (sending is blocked until it is), and plan. Use to poll verification state after register().
Response body
me-response.json| Field | Type | Notes |
|---|---|---|
| email req | string | The account email behind this credential. |
| emailVerified req | boolean | Whether the account email is verified. Sending is blocked until true. |
| plan req | string | The account's plan id. |
An actual response, recorded from the conformance suite.
{
"email": "owner@myapp.ai",
"emailVerified": true,
"plan": "free"
} {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://api.mailkite.dev/v1/schemas/me-response.json",
"title": "Current account",
"type": "object",
"required": [
"email",
"emailVerified",
"plan"
],
"additionalProperties": false,
"properties": {
"email": {
"type": "string",
"description": "The account email behind this credential.",
"examples": [
"owner@myapp.ai"
]
},
"emailVerified": {
"type": "boolean",
"description": "Whether the account email is verified. Sending is blocked until true.",
"examples": [
true
]
},
"plan": {
"type": "string",
"description": "The account's plan id.",
"examples": [
"free"
]
}
}
}