Skip to the documentation
API

API keys

Every operation in this group: what it accepts, what it returns and the errors it can answer with.

Operations

The keys of the workspace, their request log and their activity: the Settings, API keys page of the app. Reading needs keys:read; creating, changing, rotating, switching, revoking and deleting need keys:manage, a scope no key holds unless somebody gave it one. Step-up verification, which the app asks for before it mints or rotates a key, cannot apply to a call made with a key, so treat keys:manage as a credential that can make credentials: give it only to automation that provisions keys, narrow that key to the send scope and role it needs, and give it an expiry. A key never makes or reaches a key wider than itself.

GET/keys

List API keys

Scopeskeys:readReads

Every key in the workspace, newest first, a page at a time, with its status, scopes, role, send scope, when it was last used and who made and last changed it. Revoked and expired keys stay listed until somebody deletes them. No secret is ever returned here.

A key never makes or reaches a key wider than itself. What it creates, changes, rotates, switches, revokes or deletes has to sit inside it on every axis: scopes the caller holds (after its own role has narrowed them), the same role when the caller has one, an expiry no later than the caller's when the caller expires, the caller's mode, and a send scope inside the caller's own, where holding one address never covers its whole domain. A key narrowed to some domains or addresses also only SEES the keys whose send scope sits inside its own; any other is a 404. Over OAuth only the reads work, because no app is ever given keys:manage. A member's token reads keys when the member's role holds api-keys:read and reaches every address, and then sees every key unless the app was limited to particular addresses. Any other member's token is refused, with insufficient_authority when the role lacks api-keys:read and owner_only when it does not reach every address.

Requires the keys:read scope.

Query parameters

limitinteger

Rows per page, 1 to 100.

At least 1At most 100Default25
cursorstring

The previous page's nextCursor, passed back as it came. It is opaque: it holds where the last row sat in this list's order, so a row deleted or edited between pages never breaks the walk, and the next page starts at the first row that sorts after it. A value this list did not hand out is a 400 invalid_cursor.

Returns

A page of keys.

Errors

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
nextmail.keys.list()nextmail.keys.listAll()nextmail.keys.iterate()
Python
keys.list()keys.list_all()keys.iterate()
Ruby
keys.listkeys.list_allkeys.iterate
PHP
keys->listkeys->listAllkeys->iterate
Go
Keys.ListKeys.ListAllKeys.Iterate
Java
keys().listkeys().listAllkeys().iterate
C#
Keys.ListAsyncKeys.ListAllAsyncKeys.IterateAsync
CLI
openemail keys list
MCP
listApiKeys

POST/keys

Create an API key

Scopeskeys:manageChanges data

Mints a live key and returns its secret in token, once. Nothing recovers it afterwards, so store it before doing anything else.

Left out, scopes is emails:send, the role is the caller's own (or none), the send scope is the caller's own (or none, which means every address the workspace owns), and the expiry is the caller's (or none). The workspace cap on live keys applies, as a 422 workspace_limit_reached. The new key is attributed to the key that made it in the activity log, and it can make keys of its own only if it was given keys:manage.

A key never makes or reaches a key wider than itself. What it creates, changes, rotates, switches, revokes or deletes has to sit inside it on every axis: scopes the caller holds (after its own role has narrowed them), the same role when the caller has one, an expiry no later than the caller's when the caller expires, the caller's mode, and a send scope inside the caller's own, where holding one address never covers its whole domain. A key narrowed to some domains or addresses also only SEES the keys whose send scope sits inside its own; any other is a 404. Over OAuth only the reads work, because no app is ever given keys:manage. A member's token reads keys when the member's role holds api-keys:read and reaches every address, and then sees every key unless the app was limited to particular addresses. Any other member's token is refused, with insufficient_authority when the role lacks api-keys:read and owner_only when it does not reach every address.

Requires the keys:manage scope.

Request body

namestringRequired

A name, 1 to 60 characters.

1 to 60 characters
scopesstring[]

The scopes the key holds. Left out, emails:send. Each one has to be held by the caller.

At least one itemOne of"emails:send""emails:read""drafts:read""drafts:write""threads:read""threads:write""files:read""files:write""labels:read""labels:write""contacts:read""contacts:write""audiences:read""audiences:write""calendar:read""calendar:write""templates:read""templates:write""domains:read""domains:write""webhooks:read""webhooks:write""rules:read""rules:write""connections:read""members:read""members:write""roles:read""roles:write""settings:read""settings:write""keys:write""keys:read""keys:manage""forms:read""forms:write""knowledge:read""knowledge:write""automations:read""automations:write""tasks:read""tasks:write""connectors:read""connectors:use""connectors:write""billing:read""billing:write""account:read""account:write"Default["emails:send"]
roleIdstring

A role to cap the key. Left out, the caller's own role, or none. A caller with a role can only give its own.

Can be null1 to 64 characters
addressAllowliststring[]

Single addresses the key may send as. Left out together with domainAllowlist, the caller's own send scope.

Up to 50 items
domainAllowliststring[]

Whole domains the key may send as, including addresses added to them later.

Up to 25 items
expiresInMinutesinteger

Minutes until the key expires, 5 to 5,256,000 (ten years). Left out, the caller's own expiry, or none.

At least 5At most 5256000
ipAllowliststring[]

Up to 50 IP addresses and ranges the key works from, such as 203.0.113.7, 198.51.100.0/24 or 2001:db8::/32. A call or an SMTP sign-in from anywhere else is refused with 403 ip_not_allowed. Left out, the caller's own list, or none.

Up to 50 items

Returns

201object

The key, with its secret.

objectstring
One of"api_key"
idstring

24 hex characters, the part of the token after oe_live_.

namestring
modestring
One of"live""test"
maskedKeystring

Enough of the token to tell two keys apart, such as oe_live_4c1b…kX7a.

keyLast4string
statusstring
One of"revoked""expired""inactive""active"
scopesstring[]
One of"emails:send""emails:read""drafts:read""drafts:write""threads:read""threads:write""files:read""files:write""labels:read""labels:write""contacts:read""contacts:write""audiences:read""audiences:write""calendar:read""calendar:write""templates:read""templates:write""domains:read""domains:write""webhooks:read""webhooks:write""rules:read""rules:write""connections:read""members:read""members:write""roles:read""roles:write""settings:read""settings:write""keys:write""keys:read""keys:manage""forms:read""forms:write""knowledge:read""knowledge:write""automations:read""automations:write""tasks:read""tasks:write""connectors:read""connectors:use""connectors:write""billing:read""billing:write""account:read""account:write"
roleIdstring

The role that caps the key, if any.

Can be null
roleNamestring
Can be null
addressAllowliststring[]
Can be null
domainAllowliststring[]
Can be null
ipAllowliststring[]

The IP addresses and ranges the key works from, such as 203.0.113.7 or 2001:db8::/32. A call from anywhere else is refused with 403 ip_not_allowed. Null when the key works from anywhere.

Can be null
expiresAtstring
Can be nullFormatdate-time
lastUsedAtstring
Can be nullFormatdate-time
totalUsesinteger

Calls the key has made, leaving out the ones refused before it authenticated.

rotatedAtstring
Can be nullFormatdate-time
rotationCountinteger
deactivatedAtstring
Can be nullFormatdate-time
revokedAtstring
Can be nullFormatdate-time
revokedReasonstring
Can be null
createdAtstring
Formatdate-time
createdByAuditActor
updatedAtstring
Formatdate-time
updatedByAuditActor
lastChangeAtstring
Can be nullFormatdate-time
lastChangeTypestring
Can be null
tokenstring

The whole secret, oe_live_…, shown this once.

Errors

403

insufficient_scope: the caller lacks keys:manage. beyond_caller_authority: the key asked for would be wider than the caller, and param names the axis (scopes, roleId, mode, expiresInMinutes, addressAllowlist or ipAllowlist). owner_only: a member's OAuth token.

422

invalid_parameter or unknown_parameter on a field, role_not_found on roleId, a domain or address the workspace does not own, an entry of ipAllowlist that is not an IP address or a range, or workspace_limit_reached.

The errors every operation can return400401404429500Error catalog

Also available in

TypeScript
nextmail.keys.create()
Python
keys.create()
Ruby
keys.create
PHP
keys->create
Go
Keys.Create
Java
keys().create
C#
Keys.CreateAsync
CLI
openemail keys create

GET/keys/requests

List the request log

Scopeskeys:readReads

Every authenticated call the workspace's keys made, newest first, the Requests tab of the app: method, path, status, error code, duration, IP and user agent, never a body or a query string. Calls refused before a key could be identified are not in it, and neither are calls made with an OAuth access token. Nothing is pruned. keyIds, failedOnly, status, path, since and until narrow it, and they combine.

A narrowed key reads only the log of the keys it can see. A key reads its own log with no scope at all at GET /keys/self/requests.

Requires the keys:read scope.

Query parameters

keyIdsstring

Comma-separated key ids, at most 50. Left out, every key in the workspace.

failedOnlyboolean

Only calls answered with a status of 400 or more.

Defaultfalse
statusstring

Only calls answered with one of these HTTP status codes, comma-separated, such as 401,403. At most 20 codes, each from 100 to 599. Anything else is a 422 invalid_parameter.

pathstring

Only calls to this route, whatever the method, such as /emails or /emails/:id. The log matches the route, not the exact path: an id in the path counts as :id and an email address as :address, so /emails/4c1b257a66287fd113bd89d0 reads every call to /emails/:id. End it with * to read every route that starts with it, such as /emails/*. It takes no query string, and a value that is not a path is a 422 invalid_parameter.

sincestring

Only rows at or after this instant, ISO 8601. One that does not parse is a 400 invalid_parameter.

Formatdate-time
untilstring

Only rows before this instant. It has to be later than since.

Formatdate-time
limitinteger

Rows per page, 1 to 100.

At least 1At most 100Default25
cursorstring

The previous page's nextCursor, passed back as it came. It is opaque: it holds where the last row sat in this list's order, so a row deleted or edited between pages never breaks the walk, and the next page starts at the first row that sorts after it. A value this list did not hand out is a 400 invalid_cursor.

Returns

A page of calls, newest first.

Errors

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
nextmail.keys.listWorkspaceRequests()nextmail.keys.listAllWorkspaceRequests()nextmail.keys.iterateWorkspaceRequests()
Python
keys.list_workspace_requests()keys.list_all_workspace_requests()keys.iterate_workspace_requests()
Ruby
keys.list_workspace_requestskeys.list_all_workspace_requestskeys.iterate_workspace_requests
PHP
keys->listWorkspaceRequestskeys->listAllWorkspaceRequestskeys->iterateWorkspaceRequests
Go
Keys.ListWorkspaceRequestsKeys.ListAllWorkspaceRequestsKeys.IterateWorkspaceRequests
Java
keys().listWorkspaceRequestskeys().listAllWorkspaceRequestskeys().iterateWorkspaceRequests
C#
Keys.ListWorkspaceRequestsAsyncKeys.ListAllWorkspaceRequestsAsyncKeys.IterateWorkspaceRequestsAsync
CLI
openemail keys list-workspace-requests
MCP
listApiKeyRequests

GET/keys/activity

List API key activity

Scopeskeys:readReads

What happened to the workspace's keys, newest first, the Activity tab of the app: created, updated, rotated, deactivated, reactivated, revoked, deleted, and every attempt to authenticate with a key that was refused. actor says who, a person as @username or a key as API key <name>, and detail.source where from. A deleted key keeps its history. keyIds, since and until narrow it.

A narrowed key reads only the activity of the keys it can see.

Requires the keys:read scope.

Query parameters

keyIdsstring

Comma-separated key ids, at most 50. Left out, every key in the workspace.

sincestring

Only rows at or after this instant, ISO 8601. One that does not parse is a 400 invalid_parameter.

Formatdate-time
untilstring

Only rows before this instant. It has to be later than since.

Formatdate-time
limitinteger

Rows per page, 1 to 100.

At least 1At most 100Default25
cursorstring

The previous page's nextCursor, passed back as it came. It is opaque: it holds where the last row sat in this list's order, so a row deleted or edited between pages never breaks the walk, and the next page starts at the first row that sorts after it. A value this list did not hand out is a 400 invalid_cursor.

Returns

A page of changes, newest first.

Errors

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
nextmail.keys.listWorkspaceActivity()nextmail.keys.listAllWorkspaceActivity()nextmail.keys.iterateWorkspaceActivity()
Python
keys.list_workspace_activity()keys.list_all_workspace_activity()keys.iterate_workspace_activity()
Ruby
keys.list_workspace_activitykeys.list_all_workspace_activitykeys.iterate_workspace_activity
PHP
keys->listWorkspaceActivitykeys->listAllWorkspaceActivitykeys->iterateWorkspaceActivity
Go
Keys.ListWorkspaceActivityKeys.ListAllWorkspaceActivityKeys.IterateWorkspaceActivity
Java
keys().listWorkspaceActivitykeys().listAllWorkspaceActivitykeys().iterateWorkspaceActivity
C#
Keys.ListWorkspaceActivityAsyncKeys.ListAllWorkspaceActivityAsyncKeys.IterateWorkspaceActivityAsync
CLI
openemail keys list-workspace-activity
MCP
listApiKeyActivity

PATCH/keys/{id}

Update an API key

Scopeskeys:manageChanges data

Renames a key, replaces its scopes or its send scope, or switches it off and on with enabled. A switched-off key is refused on every call with inactive_api_key and keeps everything it had, so switching it back on restores it exactly; that is the reversible alternative to revoking. scopes, addressAllowlist and domainAllowlist REPLACE what the key had, and a list left out stays as it was. A revoked key cannot be changed.

A key never makes or reaches a key wider than itself. What it creates, changes, rotates, switches, revokes or deletes has to sit inside it on every axis: scopes the caller holds (after its own role has narrowed them), the same role when the caller has one, an expiry no later than the caller's when the caller expires, the caller's mode, and a send scope inside the caller's own, where holding one address never covers its whole domain. A key narrowed to some domains or addresses also only SEES the keys whose send scope sits inside its own; any other is a 404. Over OAuth only the reads work, because no app is ever given keys:manage. A member's token reads keys when the member's role holds api-keys:read and reaches every address, and then sees every key unless the app was limited to particular addresses. Any other member's token is refused, with insufficient_authority when the role lacks api-keys:read and owner_only when it does not reach every address. A key changing itself may only narrow itself.

Requires the keys:manage scope.

Path parameters

idstringRequired

The key id, 24 hex characters.

Request body

namestring

A new name, 1 to 60 characters.

1 to 60 characters
scopesstring[]

The whole new list of scopes, at least one.

At least one itemOne of"emails:send""emails:read""drafts:read""drafts:write""threads:read""threads:write""files:read""files:write""labels:read""labels:write""contacts:read""contacts:write""audiences:read""audiences:write""calendar:read""calendar:write""templates:read""templates:write""domains:read""domains:write""webhooks:read""webhooks:write""rules:read""rules:write""connections:read""members:read""members:write""roles:read""roles:write""settings:read""settings:write""keys:write""keys:read""keys:manage""forms:read""forms:write""knowledge:read""knowledge:write""automations:read""automations:write""tasks:read""tasks:write""connectors:read""connectors:use""connectors:write""billing:read""billing:write""account:read""account:write"
addressAllowliststring[]

The whole new list of single addresses.

Up to 50 items
domainAllowliststring[]

The whole new list of whole domains.

Up to 25 items
enabledboolean

False switches the key off, true switches it back on. Reversible, unlike revoking.

ipAllowliststring[]

Up to 50 IP addresses and ranges the key works from, such as 203.0.113.7, 198.51.100.0/24 or 2001:db8::/32. A call or an SMTP sign-in from anywhere else is refused with 403 ip_not_allowed. Left out, the caller's own list, or none. An empty list lets the key work from anywhere again. A key cannot change its own list.

Up to 50 items

Returns

The key as it now stands.

Errors

409

revoked: a revoked key cannot be changed.

The errors every operation can return400401404422429500Error catalog

Also available in

TypeScript
nextmail.keys.update()
Python
keys.update()
Ruby
keys.update
PHP
keys->update
Go
Keys.Update
Java
keys().update
C#
Keys.UpdateAsync
CLI
openemail keys update

DELETE/keys/{id}

Delete an API key

Scopeskeys:manageDeletes

Removes a revoked key from the list. Its request log and its activity stay, under Deleted key. A key that has not been revoked is refused, so nothing still calling with it can lose its credential without somebody deciding to first.

Requires the keys:manage scope.

Path parameters

idstringRequired

The key id, 24 hex characters.

Returns

Deleted.

Errors

409

not_revoked: revoke the key first.

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
nextmail.keys.delete()
Python
keys.delete()
Ruby
keys.delete
PHP
keys->delete
Go
Keys.Delete
Java
keys().delete
C#
Keys.DeleteAsync
CLI
openemail keys delete

POST/keys/{id}/rotate

Rotate an API key

Scopeskeys:manageDeletes

Gives a key a new secret and returns it in token, once. The id, name, scopes, role, send scope, expiry and request history all carry on, and the old secret stops working the instant this returns, with no overlap window. Rotating the calling key itself follows POST /keys/self/rotate and is allowed with keys:write as well as keys:manage. A revoked or expired key cannot be rotated.

A key never makes or reaches a key wider than itself. What it creates, changes, rotates, switches, revokes or deletes has to sit inside it on every axis: scopes the caller holds (after its own role has narrowed them), the same role when the caller has one, an expiry no later than the caller's when the caller expires, the caller's mode, and a send scope inside the caller's own, where holding one address never covers its whole domain. A key narrowed to some domains or addresses also only SEES the keys whose send scope sits inside its own; any other is a 404. Over OAuth only the reads work, because no app is ever given keys:manage. A member's token reads keys when the member's role holds api-keys:read and reaches every address, and then sees every key unless the app was limited to particular addresses. Any other member's token is refused, with insufficient_authority when the role lacks api-keys:read and owner_only when it does not reach every address. Rotation hands the caller a working secret for the key, which is why a key that is wider than the caller in any way is refused.

Requires the keys:manage scope.

Path parameters

idstringRequired

The key id, 24 hex characters.

Returns

200object

The key, with its new secret.

objectstring
One of"api_key"
idstring

24 hex characters, the part of the token after oe_live_.

namestring
modestring
One of"live""test"
maskedKeystring

Enough of the token to tell two keys apart, such as oe_live_4c1b…kX7a.

keyLast4string
statusstring
One of"revoked""expired""inactive""active"
scopesstring[]
One of"emails:send""emails:read""drafts:read""drafts:write""threads:read""threads:write""files:read""files:write""labels:read""labels:write""contacts:read""contacts:write""audiences:read""audiences:write""calendar:read""calendar:write""templates:read""templates:write""domains:read""domains:write""webhooks:read""webhooks:write""rules:read""rules:write""connections:read""members:read""members:write""roles:read""roles:write""settings:read""settings:write""keys:write""keys:read""keys:manage""forms:read""forms:write""knowledge:read""knowledge:write""automations:read""automations:write""tasks:read""tasks:write""connectors:read""connectors:use""connectors:write""billing:read""billing:write""account:read""account:write"
roleIdstring

The role that caps the key, if any.

Can be null
roleNamestring
Can be null
addressAllowliststring[]
Can be null
domainAllowliststring[]
Can be null
ipAllowliststring[]

The IP addresses and ranges the key works from, such as 203.0.113.7 or 2001:db8::/32. A call from anywhere else is refused with 403 ip_not_allowed. Null when the key works from anywhere.

Can be null
expiresAtstring
Can be nullFormatdate-time
lastUsedAtstring
Can be nullFormatdate-time
totalUsesinteger

Calls the key has made, leaving out the ones refused before it authenticated.

rotatedAtstring
Formatdate-time
rotationCountinteger
deactivatedAtstring
Can be nullFormatdate-time
revokedAtstring
Can be nullFormatdate-time
revokedReasonstring
Can be null
createdAtstring
Formatdate-time
createdByAuditActor
updatedAtstring
Formatdate-time
updatedByAuditActor
lastChangeAtstring
Can be nullFormatdate-time
lastChangeTypestring
Can be null
tokenstring

The new secret, shown this once.

Errors

409

revoked or expired: create a new key instead.

The errors every operation can return400401404422429500Error catalog

Also available in

TypeScript
nextmail.keys.rotate()
Python
keys.rotate()
Ruby
keys.rotate
PHP
keys->rotate
Go
Keys.Rotate
Java
keys().rotate
C#
Keys.RotateAsync
CLI
openemail keys rotate

POST/keys/{id}/revoke

Revoke an API key

Scopeskeys:manageDeletes

Revokes a key for good: every later call with it is refused with revoked_api_key, and it can never be switched back on, rotated or changed. The body is optional and may carry a reason, kept on the key. Revoking a key that is already revoked changes nothing and answers the same. A key may revoke itself, which is how an integration that suspects its secret has leaked retires it at once.

A key never makes or reaches a key wider than itself. What it creates, changes, rotates, switches, revokes or deletes has to sit inside it on every axis: scopes the caller holds (after its own role has narrowed them), the same role when the caller has one, an expiry no later than the caller's when the caller expires, the caller's mode, and a send scope inside the caller's own, where holding one address never covers its whole domain. A key narrowed to some domains or addresses also only SEES the keys whose send scope sits inside its own; any other is a 404. Over OAuth only the reads work, because no app is ever given keys:manage. A member's token reads keys when the member's role holds api-keys:read and reaches every address, and then sees every key unless the app was limited to particular addresses. Any other member's token is refused, with insufficient_authority when the role lacks api-keys:read and owner_only when it does not reach every address.

Requires the keys:manage scope.

Path parameters

idstringRequired

The key id, 24 hex characters.

Request body

reasonstring

Why, at most 200 characters.

Up to 200 characters

Returns

The key, now revoked.

Errors

The errors every operation can return400401404422429500Error catalog

Also available in

TypeScript
nextmail.keys.revoke()
Python
keys.revoke()
Ruby
keys.revoke
PHP
keys->revoke
Go
Keys.Revoke
Java
keys().revoke
C#
Keys.RevokeAsync
CLI
openemail keys revoke

GET/keys/{id}/requests

List one key's requests

Scopeskeys:readReads

The request log of one key, newest first. failedOnly, status, path, since and until narrow it, and they combine. A deleted key's log is still readable by a key that is not narrowed.

Requires the keys:read scope.

Path parameters

idstringRequired

The key id, 24 hex characters.

Query parameters

failedOnlyboolean

Only calls answered with a status of 400 or more.

Defaultfalse
statusstring

Only calls answered with one of these HTTP status codes, comma-separated, such as 401,403. At most 20 codes, each from 100 to 599. Anything else is a 422 invalid_parameter.

pathstring

Only calls to this route, whatever the method, such as /emails or /emails/:id. The log matches the route, not the exact path: an id in the path counts as :id and an email address as :address, so /emails/4c1b257a66287fd113bd89d0 reads every call to /emails/:id. End it with * to read every route that starts with it, such as /emails/*. It takes no query string, and a value that is not a path is a 422 invalid_parameter.

sincestring

Only rows at or after this instant, ISO 8601. One that does not parse is a 400 invalid_parameter.

Formatdate-time
untilstring

Only rows before this instant. It has to be later than since.

Formatdate-time
limitinteger

Rows per page, 1 to 100.

At least 1At most 100Default25
cursorstring

The previous page's nextCursor, passed back as it came. It is opaque: it holds where the last row sat in this list's order, so a row deleted or edited between pages never breaks the walk, and the next page starts at the first row that sorts after it. A value this list did not hand out is a 400 invalid_cursor.

Returns

A page of calls, newest first.

Errors

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
nextmail.keys.listRequests()nextmail.keys.listAllRequests()nextmail.keys.iterateRequests()
Python
keys.list_requests()keys.list_all_requests()keys.iterate_requests()
Ruby
keys.list_requestskeys.list_all_requestskeys.iterate_requests
PHP
keys->listRequestskeys->listAllRequestskeys->iterateRequests
Go
Keys.ListRequestsKeys.ListAllRequestsKeys.IterateRequests
Java
keys().listRequestskeys().listAllRequestskeys().iterateRequests
C#
Keys.ListRequestsAsyncKeys.ListAllRequestsAsyncKeys.IterateRequestsAsync
CLI
openemail keys list-requests
MCP
listApiKeyRequests

GET/keys/{id}/activity

List one key's activity

Scopeskeys:readReads

What happened to one key, newest first. A deleted key's history is still readable by a key that is not narrowed.

Requires the keys:read scope.

Path parameters

idstringRequired

The key id, 24 hex characters.

Query parameters

sincestring

Only rows at or after this instant, ISO 8601. One that does not parse is a 400 invalid_parameter.

Formatdate-time
untilstring

Only rows before this instant. It has to be later than since.

Formatdate-time
limitinteger

Rows per page, 1 to 100.

At least 1At most 100Default25
cursorstring

The previous page's nextCursor, passed back as it came. It is opaque: it holds where the last row sat in this list's order, so a row deleted or edited between pages never breaks the walk, and the next page starts at the first row that sorts after it. A value this list did not hand out is a 400 invalid_cursor.

Returns

A page of changes, newest first.

Errors

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
nextmail.keys.listActivity()nextmail.keys.listAllActivity()nextmail.keys.iterateActivity()
Python
keys.list_activity()keys.list_all_activity()keys.iterate_activity()
Ruby
keys.list_activitykeys.list_all_activitykeys.iterate_activity
PHP
keys->listActivitykeys->listAllActivitykeys->iterateActivity
Go
Keys.ListActivityKeys.ListAllActivityKeys.IterateActivity
Java
keys().listActivitykeys().listAllActivitykeys().iterateActivity
C#
Keys.ListActivityAsyncKeys.ListAllActivityAsyncKeys.IterateActivityAsync
CLI
openemail keys list-activity
MCP
listApiKeyActivity

GET/keys/stats

Read API key stats

Scopeskeys:reademails:readReads

What the keys of the workspace did inside a window, the Analytics tab of the API keys page: the mail they sent and what became of it, the calls refused for a bad or revoked secret, the requests they made and how many failed, the 8 routes they called most with the median time each took, and the status codes they got back. Every key you can see, or the ones keyIds names. A key limited to some addresses sees only the keys whose send scope sits inside its own, and over OAuth only the owner of the workspace reaches it.

Requires the keys:read and emails:read scopes.

Query parameters

keyIdsstring

Comma-separated key ids, at most 50. Left out, every key you can see.

sincestring

The start of the window, an ISO 8601 instant. Left out, 30 days before until.

Formatdate-time
untilstring

The end of the window, an ISO 8601 instant, not included. Left out, now.

Formatdate-time
grainstring

How wide one bucket of the series is. The bucket keys change shape with it: YYYY-MM-DD for a day, YYYY-MM-DDTHH for an hour, YYYY-MM-DDTHH:MM for a minute.

One of"minute""hour""day"Default"day"
offsetMinutesinteger

Minutes to add to UTC before cutting the buckets, so a day starts at midnight where the reader is. 120 for UTC+2.

At least -840At most 840Default0

Returns

The figures for the window.

Errors

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
nextmail.keys.stats()
Python
keys.stats()
Ruby
keys.stats
PHP
keys->stats
Go
Keys.Stats
Java
keys().stats
C#
Keys.StatsAsync
CLI
openemail keys stats
MCP
getApiKeyStats

Objects

ApiKeyobject

An API key without its secret. The secret is returned once, by the call that made or rotated it, and never again.

objectstring
One of"api_key"
idstring

24 hex characters, the part of the token after oe_live_.

namestring
modestring
One of"live""test"
maskedKeystring

Enough of the token to tell two keys apart, such as oe_live_4c1b…kX7a.

keyLast4string
statusstring
One of"revoked""expired""inactive""active"
scopesstring[]
One of"emails:send""emails:read""drafts:read""drafts:write""threads:read""threads:write""files:read""files:write""labels:read""labels:write""contacts:read""contacts:write""audiences:read""audiences:write""calendar:read""calendar:write""templates:read""templates:write""domains:read""domains:write""webhooks:read""webhooks:write""rules:read""rules:write""connections:read""members:read""members:write""roles:read""roles:write""settings:read""settings:write""keys:write""keys:read""keys:manage""forms:read""forms:write""knowledge:read""knowledge:write""automations:read""automations:write""tasks:read""tasks:write""connectors:read""connectors:use""connectors:write""billing:read""billing:write""account:read""account:write"
roleIdstring

The role that caps the key, if any.

Can be null
roleNamestring
Can be null
addressAllowliststring[]
Can be null
domainAllowliststring[]
Can be null
ipAllowliststring[]

The IP addresses and ranges the key works from, such as 203.0.113.7 or 2001:db8::/32. A call from anywhere else is refused with 403 ip_not_allowed. Null when the key works from anywhere.

Can be null
expiresAtstring
Can be nullFormatdate-time
lastUsedAtstring
Can be nullFormatdate-time
totalUsesinteger

Calls the key has made, leaving out the ones refused before it authenticated.

rotatedAtstring
Can be nullFormatdate-time
rotationCountinteger
deactivatedAtstring
Can be nullFormatdate-time
revokedAtstring
Can be nullFormatdate-time
revokedReasonstring
Can be null
createdAtstring
Formatdate-time
createdByAuditActor
updatedAtstring
Formatdate-time
updatedByAuditActor
lastChangeAtstring
Can be nullFormatdate-time
lastChangeTypestring
Can be null

ApiKeyActivityEntryobject

objectstring
One of"api_key_event"
idstring
keyIdstring
keyNamestring

Deleted key once the key itself is gone.

typestring
One of"created""updated""rotated""revoked""deactivated""reactivated""auth_failed""deleted"
createdAtstring
Formatdate-time
detailobject

What changed and where it was changed from (source: console, api, mcp or documentation). A refused authentication carries reason, and via: smtp when it was an SMTP sign-in.

ApiKeyActivityListobject

objectstring
One of"list"
hasMoreboolean

True when another page follows. Pass nextCursor back as cursor to read it.

nextCursorstring

An opaque cursor for the next page, or null on the last page. Pass it back unchanged.

Can be null

ApiKeyListobject

objectstring
One of"list"
hasMoreboolean

True when another page follows. Pass nextCursor back as cursor to read it.

nextCursorstring

An opaque cursor for the next page, or null on the last page. Pass it back unchanged.

Can be null

ApiKeyRequestobject

One authenticated call a key made, or one message it handed over SMTP. Never a body or a query string.

objectstring
One of"api_key_request"
idstring
keyIdstring
keyNamestring

Deleted key once the key itself is gone.

requestIdstring

The X-Request-Id the call was answered with.

Can be null
methodstring

The HTTP method, or SMTP for a message the key handed over SMTP.

pathstring

The path that was called. A message handed over SMTP is logged as /emails.

statusinteger

The HTTP status. A message handed over SMTP carries the status the same send would have had over REST.

errorCodestring
Can be null
durationMsinteger
ipstring
Can be null
userAgentstring

smtp for a message handed over SMTP.

Can be null
createdAtstring
Formatdate-time

ApiKeyRequestListobject

objectstring
One of"list"
hasMoreboolean

True when another page follows. Pass nextCursor back as cursor to read it.

nextCursorstring

An opaque cursor for the next page, or null on the last page. Pass it back unchanged.

Can be null

ApiKeyStatsobject

objectstringRequired
One of"api_key_stats"
sincestringRequired
Formatdate-time
untilstringRequired
Formatdate-time
grainstringRequired
One of"minute""hour""day"
keyIdsstring[]Required

The keys asked for. Empty means every key you can see.

sendsobjectRequired

The mail the keys sent, with what became of it.

daysobject[]
daystring

The bucket, shaped by grain.

sentinteger
deliveredinteger
failedinteger
bouncedinteger
complainedinteger
totalsobject
sendsinteger
testSendsinteger
recipientsinteger
deliveredinteger
failedinteger
bouncedinteger
complainedinteger
uncertaininteger
pendinginteger
statusesobject[]

Sends by status.

labelstring
countinteger
sourcesobject[]

Sends by where they came from.

labelstring
countinteger
sendersobject[]

Sends by the address they went out as.

labelstring
countinteger
suppressedobject[]

Recipients skipped because they were suppressed, by reason.

labelstring
countinteger
rejectedobject[]Required

Calls refused because the secret was wrong, revoked or expired, per bucket.

bucketstring
countinteger
requestsobject[]Required

Requests made and how many of them failed with a 4xx or 5xx, per bucket.

bucketstring
countinteger
failedinteger
routesobject[]Required

The routes called most, with how many failed and the median time in milliseconds.

labelstring
countinteger
failedinteger
medianMsinteger
codesobject[]Required

Requests by the HTTP status they got back.

labelstring
countinteger

AuditActorobject

Who made the change: a person, or a key acting over the API. Null when Nextmail made it on its own, such as switching a webhook off after 100 failed events in a row, and when the person or key has since been deleted.

Can be null
kindstring
One of"user""apiKey"
idstring

The account id of the person, or the id of the key.

namestring

The name of the person, or API key <name> for a key.

usernamestring
Can be null
labelstring

What the app shows: @username for a person who has a username, otherwise name.

DeletedApiKeyobject

objectstringRequired
One of"api_key"
idstringRequired
deletedbooleanRequired
One oftrue