---
title: "nextmail.me"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://nextmail.sh/docs/python/reference/me"
area: "Python"
category: "Reference"
---

# nextmail.me

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

The API key or OAuth access token behind the client: its mode, scopes, role ceiling and workspace, and for a key its own request log, activity and stats.

### `me.get()`

Describe the API key or access token making the call

```python
def get(*, api_key: str | None = None, timeout: float | None = None) -> KeyResource
```

Returns what the key is: its id, whether it is a live or test key, the workspace it belongs to, the scopes it can use and the allowlists that narrow who it may send as. It needs no scope, because a key may always describe itself, so it works with any valid key and is the first call to make when a request is refused.

`scopes` is the effective list and the only one that authorises anything. It is the scopes the key was created with intersected with the permissions of the role it was issued under, resolved on every request rather than frozen into the key. `grantedScopes` is what the key was created holding and `roleId` names the role that capped it, so a scope present in `grantedScopes` and missing from `scopes` was removed by that role. A 403 `insufficient_scope` on a key the console shows as holding the scope is almost always this, and the fix is to change the role rather than to mint another key.

A key can be narrowed by whole domains, by individual addresses, or by both. `domainAllowlist` holds whole domains, and the key may send as any address on one of them, including addresses created after the key was. `addressAllowlist` holds individual addresses and covers only those. Both `None` means the key may send as any address the workspace owns. When either is set the key is narrowed, and reads of sent mail, tracking and calendar narrow to the same set. An address whose domain is already in `domainAllowlist` is dropped from `addressAllowlist` when the key is saved, so the two lists never overlap.

Called with an OAuth access token it describes the token instead, and `object` tells the two apart. A key answers with `object` set to `'api_key'` and `kind` set to `'apiKey'`. A token answers with `object` set to `'oauth_token'` and `kind` set to `'oauth'`, with `id` and `roleId` set to `None`, `clientId` naming the connected app and `mode` always `'live'`. `scopes` and `grantedScopes` are the permissions the person approved for the app, and the allowlists are the addresses and domains the app may reach. `expiresAt` is when that approval runs out, or `None` when it never does. It is not the expiry of the access token, which the app renews with its refresh token.

**Parameters**

- `api_key` (`str`): Describes this key instead of the credential the client was built with.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`KeyResource`, one of two dicts. For an API key it is `ApiKeySelfResource`, with `object` set to `'api_key'`, `kind`, `id`, `mode`, `scopes`, `grantedScopes`, `roleId`, `workspaceId`, `addressAllowlist` and `domainAllowlist`. For an OAuth access token it is `OauthTokenSelfResource`, with `object` set to `'oauth_token'`, the same keys with `id` and `roleId` set to `None`, plus `clientId` and `expiresAt`. Compare `object` before reading `clientId`, and a type checker narrows the union for you.

**Example**

```python
from nextmail import nextmail

key = nextmail.me.get()

removed_by_role = sorted(set(key['grantedScopes']) - set(key['scopes']))
print(key['mode'], key['workspaceId'], removed_by_role)

if key['object'] == 'oauth_token':
    print(key['clientId'], key['expiresAt'])
```

**Notes**

- Narrowing a role takes effect on the next request without touching the key itself, so `scopes` can shrink between two calls with the same secret. Replacing the secret is a separate act, and `me.rotate` is the call that does it.
- `roleId` is `None` on a key with no ceiling, which is what every key issued before roles existed still has.
- A failure here raises `NextmailApiError` with status 401 and a `code` naming the credential problem: `missing_api_key`, `invalid_api_key`, `revoked_api_key`, `expired_api_key`, or `inactive_api_key` for a key switched off in the console.
- An access token that has run out is a 401 `expired_access_token`, which the app answers by renewing it with its refresh token. `invalid_access_token` and the `grant_*` codes mean the app has to be connected again.

Also available in: API [`GET /keys/self`](https://nextmail.sh/docs/api/reference/account#get-keys-self); TypeScript [`nextmail.me.get()`](https://nextmail.sh/docs/sdk/reference/me#get); Ruby [`me.get`](https://nextmail.sh/docs/ruby/reference/me#get); PHP [`me->get`](https://nextmail.sh/docs/php/reference/me#get); Go [`Me.Get`](https://nextmail.sh/docs/go/reference/me#get); Java [`me().get`](https://nextmail.sh/docs/java/reference/me#get); C# [`Me.GetAsync`](https://nextmail.sh/docs/csharp/reference/me#get); CLI [`nextmail me get`](https://nextmail.sh/docs/cli/reference/me#me-get).

### `me.ping()`

Check that a key or access token authenticates

```python
def ping(*, api_key: str | None = None, timeout: float | None = None) -> PingResource
```

An authentication smoke test. It runs the same key check every other endpoint runs and answers with `ok` set to `True`, the key id, its mode, the workspace and the effective scopes. It needs no scope, so a key with none at all still gets a 200.

Use it in a health check or at process start to fail fast on a revoked, expired or mistyped key. The answer carries the same scope detail as `me.get`, `scopes` effective and `grantedScopes` as created with `roleId` naming the cap between them, but it leaves out both allowlists. Call `me.get` when you need to know which domains and which addresses the key may send as.

An OAuth access token gets the same answer with `kind` set to `'oauth'`, `keyId` and `roleId` set to `None`, `clientId` naming the connected app and `mode` always `'live'`. A key answers with `kind` set to `'apiKey'`.

**Parameters**

- `api_key` (`str`): Checks this key instead of the credential the client was built with.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`PingResource`, a dict with `ok` set to `True`, `kind`, `keyId`, `mode`, `scopes`, `grantedScopes`, `roleId` and `workspaceId`, plus `clientId` for an OAuth access token.

**Example**

```python
from nextmail import nextmail

ping = nextmail.me.ping()

if 'emails:send' not in ping['scopes']:
    raise RuntimeError(f'Key {ping["keyId"]} cannot send mail')
```

**Notes**

- The id is `keyId` here and `id` on `me.get`, and there is no `object` key in this response, so tell a key from a token by `kind`.
- A bad key never comes back with `ok` set to `False`. The call raises `NextmailApiError` with status 401, so `is_auth` is the check to make.

Also available in: API [`GET /ping`](https://nextmail.sh/docs/api/reference/account#get-ping); TypeScript [`nextmail.me.ping()`](https://nextmail.sh/docs/sdk/reference/me#ping); Ruby [`me.ping`](https://nextmail.sh/docs/ruby/reference/me#ping); PHP [`me->ping`](https://nextmail.sh/docs/php/reference/me#ping); Go [`Me.Ping`](https://nextmail.sh/docs/go/reference/me#ping); Java [`me().ping`](https://nextmail.sh/docs/java/reference/me#ping); C# [`Me.PingAsync`](https://nextmail.sh/docs/csharp/reference/me#ping); CLI [`nextmail me ping`](https://nextmail.sh/docs/cli/reference/me#me-ping).

### `me.rotate()`

Replace the calling key's own secret

```python
def rotate(
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> RotatedKeyResource
```

Mints a new secret for the key making the call and returns the key as `me.get` describes it, without `kind`, plus `token`, the new key in full. That is the only place the value appears, so store it before anything else.

Everything else about the key survives. The id, the mode, the scopes, the role ceiling, the address allowlist and the domain allowlist all come back unchanged, so `token` is the only thing your configuration has to change. Because the id is stable, an audit trail joined on it stays joined up across rotations.

There is no overlap window. The old secret stops authenticating the moment this call commits, and neither secret can be shown again. Write `token` to wherever the key is read from before the next request, and build the next client from it, because a client keeps sending the secret it was created with. A lost response is the bad case: the rotation may have committed to a secret nobody saw, which leaves the key unusable until someone mints it a new secret in the console.

Scopes: `keys:write`.

**Parameters**

- `api_key` (`str`): Rotates this key instead of the one the client was built with.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`RotatedKeyResource`, a dict with `object` set to `'api_key'`, `id`, `mode`, `scopes`, `grantedScopes`, `roleId`, `workspaceId`, `addressAllowlist` and `domainAllowlist`, then `token`, `rotationCount` and `rotatedAt`. `token` is the new key: `oe_live_` or `oe_test_`, the 24 character key id, an underscore and 43 base64url characters.

**Example**

```python
from pathlib import Path

from nextmail import nextmail

rotated = nextmail.me.rotate()

Path('nextmail-api-key').write_text(rotated['token'])

print(f'Rotation {rotated["rotationCount"]} committed at {rotated["rotatedAt"]}')
```

**Notes**

- It needs `keys:write`, and a key without it gets 403 `insufficient_scope`. No key holds that scope unless somebody gave it one in the console, so a key that cannot reach this call has to be rotated from there.
- Only an API key can rotate its own secret. Called with an OAuth access token it raises a 403 `api_key_only`, because an app renews its token with its refresh token instead.
- There is no request body and the success status is 200, not 201.
- Not retried automatically. Repeating a rotation would invalidate the secret the first attempt returned, so a failure has to be judged rather than retried.
- The 404 and the two 409s are races rather than everyday answers, because a key that is missing, revoked or expired cannot authenticate this call in the first place. They mean the key changed state between authenticating and rotating: 404 if the row went, 409 `revoked` or 409 `expired` otherwise. None of the three is rotatable, so the answer is a new key from the console.
- `rotationCount` is the count after this call, so the first rotation of a key answers 1, and `rotatedAt` is when that rotation committed.

Also available in: API [`POST /keys/self/rotate`](https://nextmail.sh/docs/api/reference/account#post-keys-self-rotate); TypeScript [`nextmail.me.rotate()`](https://nextmail.sh/docs/sdk/reference/me#rotate); Ruby [`me.rotate`](https://nextmail.sh/docs/ruby/reference/me#rotate); PHP [`me->rotate`](https://nextmail.sh/docs/php/reference/me#rotate); Go [`Me.Rotate`](https://nextmail.sh/docs/go/reference/me#rotate); Java [`me().rotate`](https://nextmail.sh/docs/java/reference/me#rotate); C# [`Me.RotateAsync`](https://nextmail.sh/docs/csharp/reference/me#rotate); CLI [`nextmail me rotate`](https://nextmail.sh/docs/cli/reference/me#me-rotate).

### `me.list_requests()`

List one page of the calling key's own request log

```python
def list_requests(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    since: datetime | str | None = None,
    until: datetime | str | None = None,
    failed_only: bool | None = None,
    statuses: Sequence[int] | None = None,
    path: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[ApiKeyRequestResource]
```

Returns one page of the calls the key making the request made, newest first. It needs no scope: a key may always read its own log, and it reads no other key's here. `keys.list_requests` and `keys.list_workspace_requests` are the ones that reach other keys, behind `keys:read`.

The request log records every authenticated call the key made: method, path, status, error code, duration, IP and user agent, and never a body or a query string. Nothing is pruned, so the log reaches back to the key's first call, and it runs straight through a rotation because it hangs off the key id. `failed_only=`, `statuses=`, `path=`, `since=` and `until=` narrow it, and they combine.

**Parameters**

- `limit` (`int`): Rows per page, a whole number from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` from the previous page, passed back unchanged. Never build one yourself.
- `since` (`datetime | str`): Only rows at or after this instant. A `datetime` is sent as ISO 8601 in UTC, and a string must already be one.
- `until` (`datetime | str`): Only rows before this instant. It has to be later than `since`, or the server answers 400 `invalid_parameter`.
- `failed_only` (`bool`): Only calls answered with a status of 400 or more.
- `statuses` (`Sequence[int]`): Only calls answered with one of these HTTP status codes, such as `[401, 403]`. At most 20, each from 100 to 599, sent comma-separated as `status`.
- `path` (`str`): Only calls to this route, whatever the method, such as `/emails` or `/emails/:id`. It matches the route, not the exact path: an id counts as `:id` and an email address as `:address`. End it with `*` to read every route that starts with it, such as `/emails/*`.
- `api_key` (`str`): Reads the log of this key instead of the one the client was built with.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[ApiKeyRequestResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each item has `id`, `keyId`, `keyName`, `requestId`, `method`, `path`, `status`, `errorCode`, `durationMs`, `ip`, `userAgent` and `createdAt`.

**Example**

```python
from nextmail import nextmail

page = nextmail.me.list_requests(statuses=[429], since='2026-10-01T00:00:00Z')

for call in page['items']:
    print(call['createdAt'], call['method'], call['path'], call['status'])
```

**Notes**

- An OAuth access token is refused with 403 `api_key_only`. Calls made with one are not logged, so there is nothing to read.
- `path=` matches the route rather than the exact path, because the log folds ids and addresses: every call to `/emails/<id>` is the one route `/emails/:id`. Each row still carries the exact `path`.
- The call that reads the log is logged too.
- The cursor is opaque and stays valid under the same filters. One this log did not hand out is a 400 `invalid_cursor`.

Also available in: API [`GET /keys/self/requests`](https://nextmail.sh/docs/api/reference/account#get-keys-self-requests); TypeScript [`nextmail.me.listRequests()`](https://nextmail.sh/docs/sdk/reference/me#listRequests); Ruby [`me.list_requests`](https://nextmail.sh/docs/ruby/reference/me#listRequests); PHP [`me->listRequests`](https://nextmail.sh/docs/php/reference/me#listRequests); Go [`Me.ListRequests`](https://nextmail.sh/docs/go/reference/me#listRequests); Java [`me().listRequests`](https://nextmail.sh/docs/java/reference/me#listRequests); C# [`Me.ListRequestsAsync`](https://nextmail.sh/docs/csharp/reference/me#listRequests); CLI [`nextmail me list-requests`](https://nextmail.sh/docs/cli/reference/me#me-list-requests).

### `me.list_all_requests()`

Collect the calling key's whole request log into one list

```python
def list_all_requests(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    since: datetime | str | None = None,
    until: datetime | str | None = None,
    failed_only: bool | None = None,
    statuses: Sequence[int] | None = None,
    path: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ApiKeyRequestResource]
```

Walks every page of `me.list_requests` under the same filters and returns every call in one list. The log is never pruned, so give it a window unless you mean to read the key's whole history, or use `me.iterate_requests` to stop early.

**Parameters**

- `limit` (`int`): Page size for each request, 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk from this cursor instead of the newest row.
- `since` (`datetime | str`): Only rows at or after this instant. A `datetime` is sent as ISO 8601 in UTC, and a string must already be one.
- `until` (`datetime | str`): Only rows before this instant. It has to be later than `since`, or the server answers 400 `invalid_parameter`.
- `failed_only` (`bool`): Only calls answered with a status of 400 or more.
- `statuses` (`Sequence[int]`): Only calls answered with one of these HTTP status codes, such as `[401, 403]`. At most 20, each from 100 to 599, sent comma-separated as `status`.
- `path` (`str`): Only calls to this route, whatever the method, such as `/emails` or `/emails/:id`. It matches the route, not the exact path: an id counts as `:id` and an email address as `:address`. End it with `*` to read every route that starts with it, such as `/emails/*`.
- `api_key` (`str`): Reads the log of this key instead of the one the client was built with, for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ApiKeyRequestResource]`, newest first. Each item has `id`, `keyId`, `keyName`, `requestId`, `method`, `path`, `status`, `errorCode`, `durationMs`, `ip`, `userAgent` and `createdAt`.

**Example**

```python
from datetime import datetime, timedelta, timezone

from nextmail import nextmail

failures = nextmail.me.list_all_requests(
    failed_only=True,
    since=datetime.now(timezone.utc) - timedelta(hours=1),
    limit=100,
)

print(len(failures), 'failed calls in the last hour')
```

**Notes**

- An OAuth access token is refused with 403 `api_key_only`. Calls made with one are not logged, so there is nothing to read.

Also available in: API [`GET /keys/self/requests`](https://nextmail.sh/docs/api/reference/account#get-keys-self-requests); TypeScript [`nextmail.me.listAllRequests()`](https://nextmail.sh/docs/sdk/reference/me#listAllRequests); Ruby [`me.list_all_requests`](https://nextmail.sh/docs/ruby/reference/me#listAllRequests); PHP [`me->listAllRequests`](https://nextmail.sh/docs/php/reference/me#listAllRequests); Go [`Me.ListAllRequests`](https://nextmail.sh/docs/go/reference/me#listAllRequests); Java [`me().listAllRequests`](https://nextmail.sh/docs/java/reference/me#listAllRequests); C# [`Me.ListAllRequestsAsync`](https://nextmail.sh/docs/csharp/reference/me#listAllRequests).

### `me.iterate_requests()`

Stream the calling key's own request log one call at a time

```python
def iterate_requests(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    since: datetime | str | None = None,
    until: datetime | str | None = None,
    failed_only: bool | None = None,
    statuses: Sequence[int] | None = None,
    path: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[ApiKeyRequestResource]
```

A generator over `me.list_requests` under the same filters, fetching a page only when the one before is drained. Nothing is requested until you start iterating, and breaking out of the loop stops the requests.

**Parameters**

- `limit` (`int`): Page size per request, 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk from this cursor instead of the newest row.
- `since` (`datetime | str`): Only rows at or after this instant. A `datetime` is sent as ISO 8601 in UTC, and a string must already be one.
- `until` (`datetime | str`): Only rows before this instant. It has to be later than `since`, or the server answers 400 `invalid_parameter`.
- `failed_only` (`bool`): Only calls answered with a status of 400 or more.
- `statuses` (`Sequence[int]`): Only calls answered with one of these HTTP status codes, such as `[401, 403]`. At most 20, each from 100 to 599, sent comma-separated as `status`.
- `path` (`str`): Only calls to this route, whatever the method, such as `/emails` or `/emails/:id`. It matches the route, not the exact path: an id counts as `:id` and an email address as `:address`. End it with `*` to read every route that starts with it, such as `/emails/*`.
- `api_key` (`str`): Reads the log of this key instead of the one the client was built with, for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[ApiKeyRequestResource]`, a generator yielding one call per step.

**Example**

```python
from nextmail import nextmail

for call in nextmail.me.iterate_requests(path='/emails', statuses=[422]):
    print(call['createdAt'], call['errorCode'], call['requestId'])
```

**Notes**

- An OAuth access token is refused with 403 `api_key_only`. Calls made with one are not logged, so there is nothing to read.

Also available in: API [`GET /keys/self/requests`](https://nextmail.sh/docs/api/reference/account#get-keys-self-requests); TypeScript [`nextmail.me.iterateRequests()`](https://nextmail.sh/docs/sdk/reference/me#iterateRequests); Ruby [`me.iterate_requests`](https://nextmail.sh/docs/ruby/reference/me#iterateRequests); PHP [`me->iterateRequests`](https://nextmail.sh/docs/php/reference/me#iterateRequests); Go [`Me.IterateRequests`](https://nextmail.sh/docs/go/reference/me#iterateRequests); Java [`me().iterateRequests`](https://nextmail.sh/docs/java/reference/me#iterateRequests); C# [`Me.IterateRequestsAsync`](https://nextmail.sh/docs/csharp/reference/me#iterateRequests).

### `me.list_activity()`

List one page of what happened to the calling key

```python
def list_activity(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    since: datetime | str | None = None,
    until: datetime | str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[ApiKeyActivityResource]
```

Returns one page of the audit log of the key making the request, newest first. It needs no scope, and it reads no other key's history: `keys.list_activity` and `keys.list_workspace_activity` do that, behind `keys:read`.

Every change to the key is a row: `created`, `updated`, `rotated`, `deactivated` and `reactivated`, plus `auth_failed` for every call that presented the key and was refused. `actor` names who made the change, a person as `@username` or a key as `API key <name>` in `label`, and `detail['source']` says where it came from: `console`, `api`, `mcp` or `documentation`.

**Parameters**

- `limit` (`int`): Rows per page, a whole number from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` from the previous page, passed back unchanged. Never build one yourself.
- `since` (`datetime | str`): Only rows at or after this instant. A `datetime` is sent as ISO 8601 in UTC, and a string must already be one.
- `until` (`datetime | str`): Only rows before this instant. It has to be later than `since`, or the server answers 400 `invalid_parameter`.
- `api_key` (`str`): Reads the log of this key instead of the one the client was built with.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[ApiKeyActivityResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each item has `id`, `keyId`, `keyName`, `type`, `createdAt`, `actor` and `detail`.

**Example**

```python
from nextmail import nextmail

page = nextmail.me.list_activity()

for change in page['items']:
    actor = change['actor']['label'] if change['actor'] else 'unknown'

    print(change['createdAt'], change['type'], actor, change['detail'].get('source'))
```

**Notes**

- An OAuth access token is refused with 403 `api_key_only`. Calls made with one are not logged, so there is nothing to read.
- A revoked or deleted key cannot authenticate, so `revoked` and `deleted` rows are only ever read by another key, through `keys.list_activity`.

Also available in: API [`GET /keys/self/activity`](https://nextmail.sh/docs/api/reference/account#get-keys-self-activity); TypeScript [`nextmail.me.listActivity()`](https://nextmail.sh/docs/sdk/reference/me#listActivity); Ruby [`me.list_activity`](https://nextmail.sh/docs/ruby/reference/me#listActivity); PHP [`me->listActivity`](https://nextmail.sh/docs/php/reference/me#listActivity); Go [`Me.ListActivity`](https://nextmail.sh/docs/go/reference/me#listActivity); Java [`me().listActivity`](https://nextmail.sh/docs/java/reference/me#listActivity); C# [`Me.ListActivityAsync`](https://nextmail.sh/docs/csharp/reference/me#listActivity); CLI [`nextmail me list-activity`](https://nextmail.sh/docs/cli/reference/me#me-list-activity).

### `me.list_all_activity()`

Collect everything that happened to the calling key into one list

```python
def list_all_activity(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    since: datetime | str | None = None,
    until: datetime | str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[ApiKeyActivityResource]
```

Walks every page of `me.list_activity` under the same window and returns every change in one list, newest first. Use `me.iterate_activity` to stop early.

**Parameters**

- `limit` (`int`): Page size for each request, 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk from this cursor instead of the newest row.
- `since` (`datetime | str`): Only rows at or after this instant. A `datetime` is sent as ISO 8601 in UTC, and a string must already be one.
- `until` (`datetime | str`): Only rows before this instant. It has to be later than `since`, or the server answers 400 `invalid_parameter`.
- `api_key` (`str`): Reads the log of this key instead of the one the client was built with, for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[ApiKeyActivityResource]`, newest first. Each item has `id`, `keyId`, `keyName`, `type`, `createdAt`, `actor` and `detail`.

**Example**

```python
from nextmail import nextmail

history = nextmail.me.list_all_activity()

rotations = [change for change in history if change['type'] == 'rotated']

print(len(rotations), 'rotations')
```

**Notes**

- An OAuth access token is refused with 403 `api_key_only`. Calls made with one are not logged, so there is nothing to read.

Also available in: API [`GET /keys/self/activity`](https://nextmail.sh/docs/api/reference/account#get-keys-self-activity); TypeScript [`nextmail.me.listAllActivity()`](https://nextmail.sh/docs/sdk/reference/me#listAllActivity); Ruby [`me.list_all_activity`](https://nextmail.sh/docs/ruby/reference/me#listAllActivity); PHP [`me->listAllActivity`](https://nextmail.sh/docs/php/reference/me#listAllActivity); Go [`Me.ListAllActivity`](https://nextmail.sh/docs/go/reference/me#listAllActivity); Java [`me().listAllActivity`](https://nextmail.sh/docs/java/reference/me#listAllActivity); C# [`Me.ListAllActivityAsync`](https://nextmail.sh/docs/csharp/reference/me#listAllActivity).

### `me.iterate_activity()`

Stream what happened to the calling key one change at a time

```python
def iterate_activity(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    since: datetime | str | None = None,
    until: datetime | str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[ApiKeyActivityResource]
```

A generator over `me.list_activity` under the same window, fetching a page only when the one before is drained. Nothing is requested until you start iterating, and breaking out of the loop stops the requests.

**Parameters**

- `limit` (`int`): Page size per request, 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk from this cursor instead of the newest row.
- `since` (`datetime | str`): Only rows at or after this instant. A `datetime` is sent as ISO 8601 in UTC, and a string must already be one.
- `until` (`datetime | str`): Only rows before this instant. It has to be later than `since`, or the server answers 400 `invalid_parameter`.
- `api_key` (`str`): Reads the log of this key instead of the one the client was built with, for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[ApiKeyActivityResource]`, a generator yielding one change per step.

**Example**

```python
from nextmail import nextmail

for change in nextmail.me.iterate_activity():
    if change['type'] == 'auth_failed':
        print('Last refused attempt at', change['createdAt'])
        break
```

**Notes**

- An OAuth access token is refused with 403 `api_key_only`. Calls made with one are not logged, so there is nothing to read.

Also available in: API [`GET /keys/self/activity`](https://nextmail.sh/docs/api/reference/account#get-keys-self-activity); TypeScript [`nextmail.me.iterateActivity()`](https://nextmail.sh/docs/sdk/reference/me#iterateActivity); Ruby [`me.iterate_activity`](https://nextmail.sh/docs/ruby/reference/me#iterateActivity); PHP [`me->iterateActivity`](https://nextmail.sh/docs/php/reference/me#iterateActivity); Go [`Me.IterateActivity`](https://nextmail.sh/docs/go/reference/me#iterateActivity); Java [`me().iterateActivity`](https://nextmail.sh/docs/java/reference/me#iterateActivity); C# [`Me.IterateActivityAsync`](https://nextmail.sh/docs/csharp/reference/me#iterateActivity).

### `me.stats()`

Read what the calling key did inside a window

```python
def stats(
    *,
    since: datetime | str | None = None,
    until: datetime | str | None = None,
    grain: TrackingGrain | None = None,
    offset_minutes: int | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> KeySelfStatsResource
```

Returns the figures of the key making the request: the requests it made and how many failed, the calls refused because its secret was wrong, revoked or expired, the routes it called most with the median time each took, and the status codes it got back. It needs no scope, and it reads no other key's figures: `keys.stats` does that.

`sends`, the mail the key sent and what became of it, is filled only when the key holds `emails:read`. Without that scope it is `None`, never zeros, so a missing permission cannot read as a quiet week.

The window runs from `since=` to `until=`, and left out it is the 30 days before now. `grain=` sets the bucket width of the series and `offset_minutes=` shifts the boundaries so days break where the reader's day does.

**Parameters**

- `since` (`datetime | str`): The start of the window, a `datetime` or an ISO 8601 instant. Defaults to 30 days before `until`.
- `until` (`datetime | str`): The end of the window, not included. Defaults to now.
- `grain` (`TrackingGrain`): Bucket width: `'minute'`, `'hour'` or `'day'`, defaulting to `'day'`.
- `offset_minutes` (`int`): Minutes east of UTC to bucket in, from -840 to 840, defaulting to 0.
- `api_key` (`str`): Reads the figures of this key instead of the one the client was built with.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`KeySelfStatsResource` with the window it covered, `keyIds` holding the id of the key alone, `sends` or `None`, `rejected`, `requests`, `routes` and `codes`.

**Example**

```python
from datetime import datetime, timedelta, timezone

from nextmail import nextmail

stats = nextmail.me.stats(grain='hour', since=datetime.now(timezone.utc) - timedelta(days=1))

failed = sum(bucket['failed'] for bucket in stats['requests'])
sends = stats['sends']

print(failed, 'failed calls in the last 24 hours')
print(sends['totals']['sends'] if sends else 'This key cannot read its sends')
```

**Notes**

- An OAuth access token is refused with 403 `api_key_only`. Calls made with one are not logged, so there is nothing to read.
- The series are sparse: a bucket with nothing in it has no entry, so a chart must fill the gaps.
- Retried automatically on network failure, since it only reads.

Also available in: API [`GET /keys/self/stats`](https://nextmail.sh/docs/api/reference/account#get-keys-self-stats); TypeScript [`nextmail.me.stats()`](https://nextmail.sh/docs/sdk/reference/me#stats); Ruby [`me.stats`](https://nextmail.sh/docs/ruby/reference/me#stats); PHP [`me->stats`](https://nextmail.sh/docs/php/reference/me#stats); Go [`Me.Stats`](https://nextmail.sh/docs/go/reference/me#stats); Java [`me().stats`](https://nextmail.sh/docs/java/reference/me#stats); C# [`Me.StatsAsync`](https://nextmail.sh/docs/csharp/reference/me#stats); CLI [`nextmail me stats`](https://nextmail.sh/docs/cli/reference/me#me-stats).
