Skip to the documentation
API

Security

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

Operations

Verification codes for OAuth access tokens. A few operations change who can reach the workspace or where its mail goes, and an app acting for a person has to confirm who they are before it does them, exactly as the app does: ask for a code, send the code the person typed, and repeat the call. API keys are never asked for a code.

GET/security/step-up

Whether this access token is verified

No scopeReads

Needs no scope, and is for OAuth access tokens only: an API key is refused with 400 step_up_not_applicable, because a key is never asked for a code.

Some operations change who can reach the workspace or where its mail goes, and the app asks the person to confirm who they are before it does them. An app acting for that person is held to the same rule: called with an OAuth access token, each of those operations answers 403 step_up_required until the token is verified, and one verification lasts 60 minutes. method says which kind of code to ask the person for.

Returns

Whether the token is verified, until when, and which kind of code it takes.

Errors

400

step_up_not_applicable: the call was made with an API key, which is never asked for a code.

The errors every operation can return401403404422429500Error catalog

Also available in

TypeScript
security.stepUpStatus()
Python
security.step_up_status()
Ruby
security.step_up_status
PHP
security->stepUpStatus
Go
Security.StepUpStatus
Java
security().stepUpStatus
C#
Security.StepUpStatusAsync

POST/security/step-up

Ask for a verification code

No scopeChanges data

Needs no scope, and is for OAuth access tokens only. Starts a verification for the person the token acts for.

With method email, a six-digit code goes to the address they sign in with and works for 10 minutes. The email names the app and, when a call from it was refused for want of a code in the last 15 minutes, the last thing it asked to do. It also tells them that the code is not tied to that one call: for 60 minutes it lets the app make every change that needs a code, within the access they gave it. With totp, nothing is sent: they read the code off their authenticator app, or use one of their backup codes. Either way, send what they type to POST /security/step-up/verify.

A code already waiting is reused rather than sent again, which reused reports; pass resend: true to send a fresh one. A code that took 5 wrong tries is never reused, so a call without resend then starts a fresh one.

Each app has its own allowance for each person: 5 codes an hour and 20 in 24 hours. It is separate from every other app's and from the 10 an hour the person has on the website. After 10 wrong codes for one app in 24 hours, verification for that app is paused, and after 20 wrong codes across all of the person's apps it is paused for every app. Either way this answers 429 step_up_locked until the time its message gives.

Request body

resendboolean

Send a new code even though one is still waiting, and stop the waiting one from working. Left out, a waiting code that still has tries left is reused and nothing is sent.

methodstring

email sends a code to the address the person signs in with even when they have an authenticator app, for when they would rather use their inbox. totp asks for the authenticator code and only applies when two-factor sign-in is on. Left out, the authenticator is asked for when two-factor sign-in is on and an email code otherwise. A waiting code of the other kind is replaced.

One of"totp""email"

Returns

How the person gets their code, and where it went.

Errors

400

step_up_not_applicable: the call was made with an API key, which is never asked for a code.

429

step_up_throttled: this app has asked for too many codes for this person. Each app has its own allowance of 5 codes an hour and 20 in 24 hours, and the message says when the next one can be asked for. It is separate from every other app's and from the 10 an hour the person has on the website, so one app never uses those up.

step_up_locked: 10 wrong codes were sent for this app in the last 24 hours, so verification is paused for it. No code can be asked for or checked until the time the message gives after paused until, which is when the oldest of those wrong codes is 24 hours old. The same answer comes once the person's apps together have sent 20 wrong codes in 24 hours: verification is then paused for every app they connected, and removing an app does not lift that pause. Codes entered on the website are not affected. The person is told by email.

rate_limited: the key or app made more than 600 requests in 60 seconds. Wait for the seconds in Retry-After.

503

step_up_undeliverable: the email with the code could not be sent just now. Try again in a moment.

The errors every operation can return401403404422500Error catalog

Also available in

TypeScript
security.beginStepUp()
Python
security.begin_step_up()
Ruby
security.begin_step_up
PHP
security->beginStepUp
Go
Security.BeginStepUp
Java
security().beginStepUp
C#
Security.BeginStepUpAsync

POST/security/step-up/verify

Send the verification code

No scopeChanges data

Needs no scope, and is for OAuth access tokens only. Checks the code the person typed against the one POST /security/step-up started, and on a match verifies this token for 60 minutes. Every operation that answered step_up_required works until elevatedUntil, and so do the app's MCP tools that need a code.

A code takes at most 5 tries, and an authenticator code is accepted only once. Wrong codes also add up for the app: after 10 in 24 hours, verification for that app is paused (after 20 across all of the person's apps, for every app), even a correct code is refused until the time the 429 message gives, and the person is told by email. The verification belongs to the app the token was issued to, by its client ID, so a second app verifies separately.

Request body

codestringRequired

The six-digit code from the email or the authenticator app, or one of the backup codes, such as abcde-12345. Spaces are ignored. A backup code works once.

6 to 12 characters

Returns

Verified, and until when.

Errors

400

step_up_code_invalid: the code did not match, and the message says how many tries are left, counting down to whichever runs out first: this code's 5 tries, the 10 wrong codes the app may send in 24 hours, or the 20 wrong codes all of the person's apps together may send in 24 hours. An authenticator code is accepted once, so sending it again counts as a wrong code. step_up_code_expired: there is no code waiting, or it ran out after 10 minutes. Ask for a new one with POST /security/step-up. step_up_not_applicable: the call was made with an API key.

429

step_up_locked: 5 wrong codes were sent for this code, so it no longer works. Ask for a new one with POST /security/step-up. A code that ran out of tries is never reused, so that call starts a fresh one even without resend.

step_up_locked: 10 wrong codes were sent for this app in the last 24 hours, so verification is paused for it. No code can be asked for or checked until the time the message gives after paused until, which is when the oldest of those wrong codes is 24 hours old. The same answer comes once the person's apps together have sent 20 wrong codes in 24 hours: verification is then paused for every app they connected, and removing an app does not lift that pause. Codes entered on the website are not affected. The person is told by email.

rate_limited: the key or app made more than 600 requests in 60 seconds. Wait for the seconds in Retry-After.

The errors every operation can return401403404422500Error catalog

Also available in

TypeScript
security.verifyStepUp()
Python
security.verify_step_up()
Ruby
security.verify_step_up
PHP
security->verifyStepUp
Go
Security.VerifyStepUp
Java
security().verifyStepUp
C#
Security.VerifyStepUpAsync

GET/security/events

Read the workspace security log

Scopesmembers:readReads

The changes people made to who can reach the workspace, newest first: invitations sent and withdrawn, members added and removed, roles created, changed and deleted, the two-factor requirement, passwords set for an address and directory sync. Each one names who did it. Nothing is ever removed from it. Paging is keyset: pass nextCursor back as cursor while hasMore is true.

Requires the members: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 the log.

Errors

The errors every operation can return400401403404422429500Error catalog

Also available in

TypeScript
security.listEvents()security.listAllEvents()security.iterateEvents()
Python
security.list_events()security.list_all_events()security.iterate_events()
Ruby
security.list_eventssecurity.list_all_eventssecurity.iterate_events
PHP
security->listEventssecurity->listAllEventssecurity->iterateEvents
Go
Security.ListEventsSecurity.ListAllEventsSecurity.IterateEvents
Java
security().listEventssecurity().listAllEventssecurity().iterateEvents
C#
Security.ListEventsAsyncSecurity.ListAllEventsAsyncSecurity.IterateEventsAsync
CLI
openemail security list-events
MCP
listWorkspaceSecurityEvents

Objects

SecurityEventobject

objectstringRequired
One of"security_event"
idstringRequired
scopestringRequired

account for the person's own log, workspace for the workspace's.

One of"account""workspace"
kindstringRequired
One of"sign-in""session-revoked""sessions-revoked""password-changed""password-reset""two-factor-enabled""two-factor-disabled""passkey-added""passkey-removed""member-added""member-removed""member-role-changed""invitation-sent""invitation-revoked""role-created""role-changed""role-deleted""two-factor-requirement-changed""address-password-set""address-password-removed""directory-sync-connected""directory-sync-disconnected""directory-sync-token-rotated""directory-sync-changed""connector-connected""connector-disconnected""connector-changed""ownership-offered""ownership-transferred"
atstringRequired
Formatdate-time
actorobjectRequired

Who made a workspace change, or null for the person's own log.

Can be null
namestringRequired
Can be null
emailstringRequired
Can be null
memberobjectRequired

The person a workspace change was about.

Can be null
namestringRequired
Can be null
emailstringRequired
Can be null
deviceobjectRequired

The device it happened on, when one was known.

Can be null
kindstringRequired

browser, app for the Nextmail phone app, or unknown.

One of"browser""app""unknown"
browserstringRequired

Such as Chrome.

Can be null
osstringRequired

Such as macOS or iOS.

Can be null
namestringRequired

The name of the phone, when the phone app reported one.

Can be null
appVersionstringRequired
Can be null
authMethodstringRequired

How a sign-in was made.

Can be nullOne of"password""totp""backup-code""passkey""email-link""handoff"
countrystringRequired
Can be null
citystringRequired
Can be null
detailSecurityEventDetailRequired

SecurityEventDetailobject

What the event was about. Only the fields that apply to its kind are present.

byKeyboolean

True when an API key made the change.

byDirectoryboolean

True when directory sync made the change.

rolestring
Can be null
previousRolestring
Can be null
passkeystring

The name of the passkey.

Can be null
devicestring

The device that was signed out, as an English label.

Can be null
deviceInfoobject

The same device in parts, so a client can name it in its own language. Absent on entries recorded before it was added.

Can be null
kindstringRequired

browser, app for the Nextmail phone app, or unknown.

One of"browser""app""unknown"
browserstringRequired

Such as Chrome.

Can be null
osstringRequired

Such as macOS or iOS.

Can be null
namestringRequired

The name of the phone, when the phone app reported one.

Can be null
appVersionstringRequired
Can be null
sessionsinteger

How many devices were signed out.

appsinteger

How many connected apps were removed.

newDeviceboolean

True for a sign-in from a device not seen before.

addressstring
Can be null
requiredboolean
enabledboolean
connectorstring

The name of the app a connector entry is about.

Can be null
accessstring

What the connector lets the assistant do, read or full.

Can be null
pausedboolean

Whether the connector is paused.

SecurityEventListobject

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

StepUpChallengeobject

objectstring
One of"step_up_challenge"
methodstring

totp when the person has two-factor authentication turned on: the code comes from their authenticator app, or is one of their backup codes. email otherwise: a six-digit code that works for 10 minutes is emailed to the address they sign in with.

One of"totp""email"
sentTostring

Only when method is email: the address the code went to, masked to its first character and its domain, such as a•••@example.com.

reusedboolean

True when a code sent earlier is still waiting and nothing new was sent. Pass resend: true to send a fresh one. A code that took 5 wrong tries is never reused, so the next call sends a fresh one without resend.

StepUpStatusobject

objectstring
One of"step_up"
elevatedboolean

Whether this access token may call the operations that need a verification code right now.

elevatedUntilstring

When the current verification runs out. Null when there is none.

Can be nullFormatdate-time
methodstring

totp when the person has two-factor authentication turned on: the code comes from their authenticator app, or is one of their backup codes. email otherwise: a six-digit code that works for 10 minutes is emailed to the address they sign in with.

One of"totp""email"
minutesinteger

How long one verification lasts, in minutes.

One of60

StepUpVerificationobject

objectstring
One of"step_up"
elevatedboolean
One oftrue
elevatedUntilstring

When this verification runs out, 60 minutes from now.

Formatdate-time