---
title: "Organization"
description: "Read, update and delete your MachineMail organization, invite and manage its members, and check the plan, its allowances and usage in the current period."
canonical: "https://machinemail.dev/docs/reference/api/organization"
---

# Organization

> Read, update and delete your MachineMail organization, invite and manage its members, and check the plan, its allowances and usage in the current period.

Source: https://machinemail.dev/docs/reference/api/organization

| Method   | Path                                                   | Summary                                                     |
| -------- | ------------------------------------------------------ | ----------------------------------------------------------- |
| `GET`    | `/v0/organization`                                     | [Get Organization (alias)](#get-organization-alias)         |
| `GET`    | `/v0/organizations`                                    | [Get Organization](#get-organization)                       |
| `PATCH`  | `/v0/organizations`                                    | [Update Organization](#update-organization)                 |
| `DELETE` | `/v0/organizations`                                    | [Delete Organization](#delete-organization)                 |
| `POST`   | `/v0/organizations/deletion`                           | [Start Deleting Organization](#start-deleting-organization) |
| `GET`    | `/v0/organizations/invitations`                        | [List Invitations](#list-invitations)                       |
| `POST`   | `/v0/organizations/invitations`                        | [Invite Member](#invite-member)                             |
| `DELETE` | `/v0/organizations/invitations/{invitation_id}`        | [Revoke Invitation](#revoke-invitation)                     |
| `POST`   | `/v0/organizations/invitations/{invitation_id}/resend` | [Resend Invitation](#resend-invitation)                     |
| `GET`    | `/v0/organizations/members`                            | [List Members](#list-members)                               |
| `PATCH`  | `/v0/organizations/members/{user_id}`                  | [Update Member](#update-member)                             |
| `DELETE` | `/v0/organizations/members/{user_id}`                  | [Remove Member](#remove-member)                             |
| `GET`    | `/v0/organizations/usage`                              | [Get Plan Usage](#get-plan-usage)                           |

## Get Organization (alias)

```http
GET /v0/organization
```

Alias of `GET /v0/organizations`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Response

**200** The organization (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `organization_id` | `string` | required | ID of the organization. |
| `name` | `string` | required | Name of the organization. |
| `slug` | `string` | required | URL-safe name of the organization, chosen from its name when it is created. It never changes, not even when the organization is renamed. {slug}@ on the shared domain is reserved for this organization: no other organization can create that inbox. |
| `plan` | "free" \| "pro" \| "team" \| "scale" | required | Plan of the organization. |
| `inbox_count` | `integer` | required | Number of inboxes in the credential scope. |
| `domain_count` | `integer` | required | Number of domains in the organization. |
| `domain_limit` | `integer` |  | Custom domains included in the plan. The Free plan cannot add more; paid plans can, and each extra domain is billed. Always present on GET /v0/organizations and PATCH /v0/organizations. |
| `usage` | `object` |  | Usage. Always present on GET /v0/organizations and PATCH /v0/organizations. |
| `shared_domain` | `string` |  | Domain every organization can create inboxes on, used when an inbox is created without domain. Always present on GET /v0/organizations and PATCH /v0/organizations. |
| `undo_send` | `boolean` | required | Undo send in the dashboard: when true (the default), a message sent from the MachineMail dashboard waits 5 seconds before it goes out, so it can be undone. Only the dashboard reads it: the API, the MCP server and the CLI always send at once. |
| `default_inbox_id` | `string \| null` | required | The default inbox of the organization (its inbox_id). A new organization gets {slug}@ on the shared domain, named after the organization, as its default inbox; when that address cannot be used (a reserved name), it starts without one. The dashboard lists the default inbox first. Change it with PATCH /v0/organizations. null when there is none: it was cleared, its inbox was deleted, or (for a pod or inbox credential) it is outside the credential scope. |
| `updated_at` | `string (date-time)` | required | Time at which the organization was last updated. |
| `created_at` | `string (date-time)` | required | Time at which the organization was created. |

#### Errors

| Status | Meaning                                          |
| ------ | ------------------------------------------------ |
| `401`  | Missing, invalid, expired or revoked credential. |
| `500`  | Unexpected server error.                         |

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/organization" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "organization_id": "org_01k6x2m9qd0000a7h3kq9v2c4e",
  "name": "Production",
  "slug": "string",
  "plan": "free",
  "inbox_count": 1,
  "domain_count": 1,
  "domain_limit": 1,
  "usage": {
    "message_count": 1,
    "storage_bytes": 2048,
    "messages_received_30d": 1,
    "messages_sent_30d": 1
  },
  "shared_domain": "string",
  "undo_send": true,
  "default_inbox_id": "acme@machinemail.dev",
  "updated_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z"
}
```

## Get Organization

```http
GET /v0/organizations
```

The organization of the credential, with inbox and domain counts and usage. `inbox_count` and `usage` count what the credential can see (a pod key: its pod).

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Response

**200** The organization (`application/json`)

#### Errors

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/organizations" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

## Update Organization

```http
PATCH /v0/organizations
```

Rename the organization, turn undo send in the dashboard on or off (`undo_send`), or change the default inbox (`default_inbox_id`).

The organization slug is fixed when it is created and never changes: a different `slug` in the body is `400` (the current value is accepted and ignored). The slug reserves `{slug}@` on the shared domain for this organization.

Organization-scoped credentials only: pod and inbox credentials get `403`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `name` | `string` |  | New name. Must not be blank. |
| `slug` | `string` |  | Immutable: the slug is fixed when the organization is created. Only the current value is accepted (a client echoing the organization back); any other value is a 400. |
| `undo_send` | `boolean` |  | Undo send in the dashboard: when true (the default), a message sent from the MachineMail dashboard waits 5 seconds before it goes out, so it can be undone. Only the dashboard reads it: the API, the MCP server and the CLI always send at once. |
| `default_inbox_id` | `string \| null` |  | Make this inbox (an inbox_id of the organization) the default inbox, or null to have none. An unknown inbox is a 404. |

#### Response

**200** The organization (`application/json`)

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                             |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X PATCH "https://api.machinemail.dev/v0/organizations" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Production"
}'
```

## Delete Organization

```http
DELETE /v0/organizations
```

Delete the organization, with the `code` emailed by `POST /v0/organizations/deletion`. It cannot be undone.

Access ends at once: every member's dashboard sessions and open streams for the organization stop working, every API key is revoked, connected apps and agents are disconnected, pending invitations are revoked, webhooks stop, and mail to the organization's addresses is refused as unknown. Its inboxes, messages, attachments, drafts, threads, domains, webhooks, events, pods and lists are then removed in the background. The slug stays reserved: no organization can use it again. Your session moves to another organization you belong to (as when leaving one); when there is none, it ends and the `mm_session` cookie is cleared.

Owners only, from a dashboard session (the `mm_session` cookie): admins, members, API keys and access tokens get `403`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `code` | `string` | required | The 6-digit code from the email. |

#### Response

**200** Deleted (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `ok` | `true` | required |  |
| `organization_id` | `string` | required | ID of the deleted organization. |

#### Errors

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X DELETE "https://api.machinemail.dev/v0/organizations" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "code": "123456"
}'
```

```json title="Response 200"
{
  "ok": true,
  "organization_id": "org_01k6x2m9qd0000a7h3kq9v2c4e"
}
```

## Start Deleting Organization

```http
POST /v0/organizations/deletion
```

First step of deleting the organization: send `confirm_name`, the organization's name (any case), and a 6-digit confirmation code is emailed to you. The code works once, for 10 minutes and 3 tries; asking again sends a new one (at most 3 an hour, and 10 a day for the organization). `502` when the email could not be sent: no code exists, try again. Then confirm with `DELETE /v0/organizations`.

`409` with `code: billing_subscription_active` while a paid plan is in force: cancel it under Billing first, and delete the organization once the plan has ended.

Owners only, from a dashboard session (the `mm_session` cookie): admins, members, API keys and access tokens get `403`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `confirm_name` | `string` | required | The name of the organization, exactly as it is shown (case and spaces included). |

#### Response

**202** Code sent (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `email` | `string` | required | Where the code was sent: your own address, partly hidden. |
| `expires_at` | `string (date-time)` | required | When the code stops working (10 minutes after it was sent). |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register).                                                                                                                                                                                                                                                                                               |
| `409`  | Conflicts with an existing resource.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `429`  | Rate limited.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `500`  | Unexpected server error.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `502`  | The email provider refused or failed the send (error.type is upstream\_error). Nothing was sent and no message was stored. error.message says why: a rejected message or recipient is final, and so is a used-up sending quota until it resets; a throttled or unavailable provider can be retried later. A scheduled draft whose send fails this way is marked send\_status: failed, unless the provider could not take mail for now (throttled, sending paused, quota used up, or unavailable): it stays scheduled and is retried. |

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X POST "https://api.machinemail.dev/v0/organizations/deletion" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "confirm_name": "string"
}'
```

```json title="Response 202"
{
  "email": "agent@machinemail.dev",
  "expires_at": "2026-10-01T12:00:00.000Z"
}
```

## List Invitations

```http
GET /v0/organizations/invitations
```

Pending invitations, newest first (`ascending=true`: oldest first). Dashboard session only (the `mm_session` cookie): API keys and access tokens get `403`. Owners and admins manage members; only owners grant, change or remove the owner role.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Query parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `limit` | `integer` |  | Maximum number of items to return. Default 50, maximum 500; larger values are capped. |
| `page_token` | `string` |  | next_page_token from the previous page. |
| `ascending` | `boolean` |  | Sort oldest first. |

#### Response

**200** Pending invitations (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `invitations` | `Invitation[]` | required | Pending invitations, newest first. |

Each item of `invitations` is a [Invitation](/content/docs/reference/api/objects#invitation/index.html):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `invitation_id` | `string` | required | ID of the invitation. |
| `organization_id` | `string` | required | ID of the organization the invitee joins. |
| `email` | `string` | required | Address the invitation was sent to. Only this address can accept it. |
| `role` | "owner" \| "admin" \| "member" | required | Role in the organization. Owners and admins invite, change roles and remove members; only owners grant or remove the owner role, and an organization always keeps one owner. |
| `state` | "pending" \| "accepted" \| "expired" \| "revoked" | required | State of the invitation. |
| `invited_by_user_id` | `string` |  | ID of the member who sent it, when known. |
| `expires_at` | `string (date-time)` | required | When the invitation stops working (7 days after it was sent). |
| `created_at` | `string (date-time)` | required | When the invitation was sent. |

#### Errors

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/organizations/invitations" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "invitations": [
    {
      "invitation_id": "string",
      "organization_id": "org_01k6x2m9qd0000a7h3kq9v2c4e",
      "email": "agent@machinemail.dev",
      "role": "owner",
      "state": "pending",
      "invited_by_user_id": "string",
      "expires_at": "2026-10-01T12:00:00.000Z",
      "created_at": "2026-10-01T12:00:00.000Z"
    }
  ]
}
```

## Invite Member

```http
POST /v0/organizations/invitations
```

Email an invitation to join the organization, valid for 7 days and only for that address. `409` when the address is already a member or already invited.

The email links to `/invite/{token}`, where the person signs in (email code, social or SSO) and accepts it.

`403` with `error.code` `signup_address_not_allowed` when the address is on a MachineMail domain (the shared domain, or a subdomain of it): those addresses can’t be used for accounts. The answer depends on the address’s domain only, never on whether it has an account. An address on a custom domain verified on MachineMail can be invited, but someone without an account can accept only an invitation from the organization that verified the domain; those people can join that organization but not create organizations of their own.

Dashboard session only (the `mm_session` cookie): API keys and access tokens get `403`. Owners and admins manage members; only owners grant, change or remove the owner role.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `email` | `string` | required | Address to invite. |
| `role` | "owner" \| "admin" \| "member" |  | Role on joining. Default member. Only owners can invite owners. |

#### Response

**201** Sent (`application/json`)

#### Errors

| Status | Meaning                                                                                                                                            |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request: error.type is validation and error.param names the field.                                                                         |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                   |
| `402`  | The plan allows no more members: members plus pending invitations are at its seat limit (Free: 1; limit\_key: "seats"). Existing members are kept. |
| `403`  | Not allowed to invite members, or the address is on a MachineMail domain (error.code signup\_address\_not\_allowed).                               |
| `409`  | Conflicts with an existing resource.                                                                                                               |
| `500`  | Unexpected server error.                                                                                                                           |

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X POST "https://api.machinemail.dev/v0/organizations/invitations" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "agent@machinemail.dev"
}'
```

```json title="Response 201"
{
  "invitation_id": "string",
  "organization_id": "org_01k6x2m9qd0000a7h3kq9v2c4e",
  "email": "agent@machinemail.dev",
  "role": "owner",
  "state": "pending",
  "invited_by_user_id": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z"
}
```

## Revoke Invitation

```http
DELETE /v0/organizations/invitations/{invitation_id}
```

Revoke a pending invitation: its link stops working. Dashboard session only (the `mm_session` cookie): API keys and access tokens get `403`. Owners and admins manage members; only owners grant, change or remove the owner role.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `invitation_id` | `string` | required | ID of the invitation. |

#### Response

**200** Revoked (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `ok` | `true` | required |  |

#### Errors

| Status | Meaning                                                                                                                                                                                                                                |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `401`  | Missing, invalid, expired or revoked credential.                                                                                                                                                                                       |
| `403`  | The credential lacks the permission or scope for this request (error.type forbidden), or MachineMail suspended the organization or its sending (account\_suspended: error.message says what still works; do not retry or re-register). |
| `404`  | Not found. Also returned for resources outside the credential scope.                                                                                                                                                                   |
| `500`  | Unexpected server error.                                                                                                                                                                                                               |

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X DELETE "https://api.machinemail.dev/v0/organizations/invitations/string" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "ok": true
}
```

## Resend Invitation

```http
POST /v0/organizations/invitations/{invitation_id}/resend
```

Send a pending invitation again. Dashboard session only (the `mm_session` cookie): API keys and access tokens get `403`. Owners and admins manage members; only owners grant, change or remove the owner role.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `invitation_id` | `string` | required | ID of the invitation. |

#### Response

**200** Sent again (`application/json`)

#### Errors

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X POST "https://api.machinemail.dev/v0/organizations/invitations/string/resend" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "invitation_id": "string",
  "organization_id": "org_01k6x2m9qd0000a7h3kq9v2c4e",
  "email": "agent@machinemail.dev",
  "role": "owner",
  "state": "pending",
  "invited_by_user_id": "string",
  "expires_at": "2026-10-01T12:00:00.000Z",
  "created_at": "2026-10-01T12:00:00.000Z"
}
```

## List Members

```http
GET /v0/organizations/members
```

The people who sign in to the dashboard for this organization, with their role in it, oldest first. Organization-scoped credentials only: pod and inbox credentials get `403`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Query parameters

#### Response

**200** A page of members (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `count` | `integer` | required | Number of items returned. |
| `limit` | `integer` |  | Maximum number of items per page. Default 50, maximum 500; larger values are capped. |
| `next_page_token` | `string` |  | Page token for pagination. Absent on the last page. |
| `members` | `Member[]` | required | Oldest first unless ascending=false. |

Each item of `members` is a [Member](/content/docs/reference/api/objects#member/index.html):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `user_id` | `string` | required | ID of the user. |
| `email` | `string` | required | Email address the user signs in with. |
| `name` | `string \| null` |  | Display name, when set. |
| `organization_id` | `string` | required | ID of the organization this record is about: the session’s active organization, or the organization listed. |
| `role` | "owner" \| "admin" \| "member" | required | Role in the organization. Owners and admins invite, change roles and remove members; only owners grant or remove the owner role, and an organization always keeps one owner. |
| `created_at` | `string (date-time)` | required | When the user was created. |
| `sign_in_methods` | `("email" \| "password" \| "google" \| "github" \| "apple" \| "microsoft" \| "sso" \| "passkey")[]` | required | Every method the member has used to sign in to this organization, in the order first used. Empty until their first sign-in after this was recorded. |
| `last_sign_in_method` | `("email" \| "password" \| "google" \| "github" \| "apple" \| "microsoft" \| "sso" \| "passkey" \| null) \| null` | required | How the member last signed in to this organization. null until their first sign-in after this was recorded. |

#### Errors

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/organizations/members" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "count": 1,
  "limit": 50,
  "next_page_token": "eyJvIjo1MH0",
  "members": [
    {
      "user_id": "usr_01k6x3j0rw0000n8w4yc0g3q5s",
      "email": "agent@machinemail.dev",
      "name": "Production",
      "organization_id": "org_01k6x2m9qd0000a7h3kq9v2c4e",
      "role": "owner",
      "created_at": "2026-10-01T12:00:00.000Z",
      "sign_in_methods": ["email"],
      "last_sign_in_method": "email"
    }
  ]
}
```

## Update Member

```http
PATCH /v0/organizations/members/{user_id}
```

Change a member's role. The last owner cannot be demoted (`409`). Dashboard session only (the `mm_session` cookie): API keys and access tokens get `403`. Owners and admins manage members; only owners grant, change or remove the owner role.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `user_id` | `string` | required | ID of the member (user_id). |

#### Request body

Content type: `application/json`.

| Field | Type | | Description |
| --- | --- | --- | --- |
| `role` | "owner" \| "admin" \| "member" | required | Role in the organization. Owners and admins invite, change roles and remove members; only owners grant or remove the owner role, and an organization always keeps one owner. |

#### Response

**200** The member (`application/json`)

#### Errors

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X PATCH "https://api.machinemail.dev/v0/organizations/members/usr_01k6x3j0rw0000n8w4yc0g3q5s" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "role": "owner"
}'
```

```json title="Response 200"
{
  "user_id": "usr_01k6x3j0rw0000n8w4yc0g3q5s",
  "email": "agent@machinemail.dev",
  "name": "Production",
  "organization_id": "org_01k6x2m9qd0000a7h3kq9v2c4e",
  "role": "owner",
  "created_at": "2026-10-01T12:00:00.000Z",
  "sign_in_methods": ["email"],
  "last_sign_in_method": "email"
}
```

## Remove Member

```http
DELETE /v0/organizations/members/{user_id}
```

Remove a member, or leave the organization with your own `user_id` (any role). The last owner cannot leave or be removed (`409`).

Their access ends at once: their dashboard sessions and open event streams for this organization stop working, the API keys they created here are revoked (CLI keys, and keys minted with those keys, included), the apps they connected with OAuth (MCP clients) are disconnected, and invitations they sent that are still pending are revoked. Keys that record no creator (made before organizations had members) are not revoked: review them under API keys. Leaving moves your session to another organization you belong to whose sign-in policy your session meets (its single sign-on and sign-in method rules apply, as for switching); when there is none, the session ends and the `mm_session` cookie is cleared.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Path parameters

| Field | Type | | Description |
| --- | --- | --- | --- |
| `user_id` | `string` | required | ID of the member (user_id). |

#### Response

**200** Removed (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `ok` | `true` | required |  |

#### Errors

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl -X DELETE "https://api.machinemail.dev/v0/organizations/members/usr_01k6x3j0rw0000n8w4yc0g3q5s" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "ok": true
}
```

## Get Plan Usage

```http
GET /v0/organizations/usage
```

The plan of the organization, its allowances and its usage in the current period: a calendar month in UTC, with the daily cap resetting at UTC midnight.

Inbound messages count once each; outbound messages count once per distinct recipient (to, cc and bcc). Spam and quarantined mail never count (mail your own receive lists block does). Storage includes drafts and held mail. Deleting mail frees storage but does not give back messages already counted.

On the Free plan, inbound mail past an allowance is held (`held`) and delivered when the allowance resets or after an upgrade; sends past it are refused with `402` `plan_limit_exceeded`. Paid plans are never blocked: usage past an allowance is billed as overage.

Organization-wide whatever the credential: Organization-scoped credentials only: pod and inbox credentials get `403`.

**Authentication:** `Authorization: Bearer <api key>` or the dashboard `mm_session` cookie. Permission: none beyond a valid key.

#### Response

**200** Plan allowances and usage (`application/json`)

| Field | Type | | Description |
| --- | --- | --- | --- |
| `plan` | "free" \| "pro" \| "team" \| "scale" | required | Plan of the organization: free, pro, team or scale. |
| `period` | `object` | required |  |
| `limits` | `PlanLimits` | required | The allowances that apply to the organization. |
| `usage` | `object` | required |  |
| `held` | `object` | required |  |
| `enforcement` | "enforce" \| "shadow" \| "off" \| "exempt" | required | enforce: limits apply. shadow and off: limits are not applied right now (usage is still counted). exempt: this organization has its own limits (unclaimed agent sandbox). |

`limits` is a [PlanLimits](/content/docs/reference/api/objects#planlimits/index.html):

| Field | Type | | Description |
| --- | --- | --- | --- |
| `messages_per_month` | `integer \| null` | required | Inbound messages and outbound recipients together per calendar month (UTC). null: no combined cap (paid plans). |
| `messages_per_day` | `integer \| null` | required | The same, per UTC day. null: no daily cap. |
| `inbound_per_month` | `integer \| null` | required | Inbound messages included per month on paid plans (more is billed as overage). null on Free. |
| `outbound_per_month` | `integer \| null` | required | Outbound recipients included per month on paid plans (more is billed as overage). null on Free. |
| `storage_bytes` | `integer` | required | Storage included, in bytes (1 GB = 1,000,000,000 bytes). |
| `custom_domains` | `integer` | required | Custom domains included. |
| `seats` | `integer \| null` | required | Members (including pending invitations). null: unlimited. |
| `overage` | `boolean` | required | true: usage past an allowance is billed, never blocked. false (Free): inbound past it is held and sending is refused until the reset. |

#### Errors

Error bodies use the [error envelope](/content/docs/reference/errors/index.html).

#### Example

```bash title="Request"
curl "https://api.machinemail.dev/v0/organizations/usage" \
  -H "Authorization: Bearer $MACHINEMAIL_API_KEY"
```

```json title="Response 200"
{
  "plan": "free",
  "period": {
    "start": "2026-10-01T12:00:00.000Z",
    "resets_at": "2026-10-01T12:00:00.000Z",
    "day_resets_at": "2026-10-01T12:00:00.000Z"
  },
  "limits": {
    "messages_per_month": 1,
    "messages_per_day": 1,
    "inbound_per_month": 1,
    "outbound_per_month": 1,
    "storage_bytes": 2048,
    "custom_domains": 1,
    "seats": 1,
    "overage": false
  },
  "usage": {
    "inbound": 1,
    "outbound": 1,
    "messages": 1,
    "messages_today": 1,
    "storage_bytes": 2048,
    "custom_domains": 1,
    "seats": 1
  },
  "held": {
    "count": 1,
    "bytes": 2048,
    "oldest_at": "2026-10-01T12:00:00.000Z"
  },
  "enforcement": "enforce"
}
```
