# API reference

> Every REST endpoint and object, generated from the OpenAPI file.

Source: https://aimulti.inbox/docs/api-reference/

Generated from `openapi/openapi.yaml` (version 0.1.0) at build time. Base URL: `https://api.162-55-213-24.sslip.io`. The API also serves the file itself at `https://api.162-55-213-24.sslip.io/openapi.yaml`.

Every operation answers on `/v1`, the current version, and on the deprecated `/v0` alias with the same bodies and status codes. See [Versions and SDKs](https://aimulti.inbox/docs/versions.md) for the deprecation headers, the policy and the official clients. The paths below show the `/v0` prefix used by the file; replace it with `/v1` and nothing else changes.

## Endpoints

### GET /v0/auth/me

Caller summary (kept for older clients; same body as GET /v0/me)

Responses:

* `200` OK: Me
* `401` Error envelope: Error

### GET /v0/me

The authenticated caller, a person's session or an API key

Responses:

* `200` OK: Me
* `401` Error envelope: Error

### POST /v0/auth/magic-link

Email a single use sign in link (public)

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `email` | string (email) | required |
| `redirect_after` | string | Path on the site, for example /console/ |

Responses:

* `202` Sent (or accepted when the address has no account: same answer)
* `303` A form post is redirected back to the login page with sent=1
* `400` Error envelope: Error
* `429` Error envelope: Error
* `501` Error envelope: Error
* `502` Error envelope: Error

### GET /v0/auth/callback

Finish a sign in, with a magic link token or an OAuth code (public)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `token` | string | in query, Magic link token |
| `code` | string | in query, OAuth authorization code |
| `state` | string | in query, OAuth state, single use, 10 minutes |

Responses:

* `200` Session: Session
* `303` A browser goes to the login page with the session in the fragment, or with ?error=<code>
* `400` Error envelope: Error
* `401` Error envelope: Error
* `403` Error envelope: Error
* `502` Error envelope: Error

### GET /v0/auth/oauth/{provider}/start

Start Google or GitHub sign in with PKCE (public)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `provider` | google \| github | in path, required |
| `redirect` | 1 | in query, Send the browser to the provider instead of returning JSON |
| `redirect_after` | string | in query, Path on the site to return to |

Responses:

* `200` OK: OAuthStart
* `302` The browser goes to the provider
* `400` Error envelope: Error
* `501` Error envelope: Error

### POST /v0/auth/refresh

Rotate a refresh token into a new session (public)

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `refresh_token` | string | required |

Responses:

* `200` Session: Session
* `400` Error envelope: Error
* `401` Error envelope: Error
* `501` Error envelope: Error

### POST /v0/auth/logout

End a session (public)

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `refresh_token` | string |  |

Responses:

* `204` Ended (also when the token was already invalid)
* `400` Error envelope: Error
* `501` Error envelope: Error

### GET /v0/inboxes

List inboxes, newest first

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `limit` | integer | in query, min 1, max 100, default 20 |
| `page_token` | string | in query |

Responses:

* `200` OK: InboxList

### POST /v0/inboxes

Create an inbox (repeat client_id to get the same inbox)

Body (CreateInbox):

| Name | Type | Notes |
| --- | --- | --- |
| `username` | string | pattern ^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$ |
| `domain` | string |  |
| `display_name` | string | max length 200 |
| `client_id` | string | pattern ^[A-Za-z0-9._~-]{1,256}$ |

Responses:

* `200` Existing inbox for this client_id: Inbox
* `201` Created: Inbox
* `403` Error envelope: Error
* `409` Error envelope: Error

### GET /v0/inboxes/{inbox}

Get an inbox

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |

Responses:

* `200` OK: Inbox
* `404` Error envelope: Error

### PATCH /v0/inboxes/{inbox}

Update display name, or turn the inbox into a support intake

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `display_name` | string | max length 200 |
| `ticket_intake` | boolean | New threads on this inbox become tickets in the support queue. |

Responses:

* `200` OK: Inbox

### DELETE /v0/inboxes/{inbox}

Delete an inbox and all its mail

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |

Responses:

* `204` Deleted
* `404` Error envelope: Error

### GET /v0/inboxes/{inbox}/threads

List threads of an inbox

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `limit` | integer | in query, min 1, max 100, default 20 |
| `page_token` | string | in query |
| `label` | string | in query |
| `include_review` | boolean | in query, Also return messages held for security review; needs message_review, default false |

Responses:

* `200` OK: ThreadList

### GET /v0/threads

List threads across the organization

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `limit` | integer | in query, min 1, max 100, default 20 |
| `page_token` | string | in query |
| `label` | string | in query |
| `include_review` | boolean | in query, Also return messages held for security review; needs message_review, default false |

Responses:

* `200` OK: ThreadList

### GET /v0/threads/{thread}

Get a thread with the messages the caller may see, oldest first

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `thread` | string (uuid) | in path, required |
| `include_review` | boolean | in query, Also return messages held for security review; needs message_review, default false |

Responses:

* `200` OK: Thread

### GET /v0/inboxes/{inbox}/messages

List message metadata (no bodies)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `limit` | integer | in query, min 1, max 100, default 20 |
| `page_token` | string | in query |
| `label` | string | in query |
| `include_review` | boolean | in query, Also return messages held for security review; needs message_review, default false |

Responses:

* `200` OK: MessageList

### POST /v0/inboxes/{inbox}/messages/send

Send a new message (new thread)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `Idempotency-Key` | string | in header, max length 256 |

Body (Send):

| Name | Type | Notes |
| --- | --- | --- |
| `to` | string (email)[] | required, at least 1 item |
| `cc` | string (email)[] |  |
| `bcc` | string (email)[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |

Responses:

* `200` Queued: SendResult
* `403` Error envelope: Error
* `409` Error envelope: Error
* `422` Error envelope: Error
* `429` Error envelope: Error
* `503` Error envelope: Error

### GET /v0/inboxes/{inbox}/messages/{message}

Get a message with bodies, extracted_text, authentication results and gatekeeper verdict

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `message` | string | in path, required, Message UUID or RFC Message-ID (URL encoded) |
| `include_review` | boolean | in query, Also return messages held for security review; needs message_review, default false |

Responses:

* `200` OK: Message
* `403` Error envelope: Error
* `404` Error envelope: Error

### PATCH /v0/inboxes/{inbox}/messages/{message}

Add or remove custom labels

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `message` | string | in path, required, Message UUID or RFC Message-ID (URL encoded) |

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `add_labels` | string[] |  |
| `remove_labels` | string[] |  |

Responses:

* `200` OK: Message

### GET /v0/inboxes/{inbox}/messages/{message}/raw

Raw RFC 822 message

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `message` | string | in path, required, Message UUID or RFC Message-ID (URL encoded) |
| `include_review` | boolean | in query, Also return messages held for security review; needs message_review, default false |

Responses:

* `200` OK: message/rfc822

### POST /v0/inboxes/{inbox}/messages/{message}/reply

Reply in the same thread

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `message` | string | in path, required, Message UUID or RFC Message-ID (URL encoded) |
| `Idempotency-Key` | string | in header, max length 256 |

Body (Reply):

| Name | Type | Notes |
| --- | --- | --- |
| `text` | string |  |
| `html` | string |  |
| `reply_all` | boolean |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |

Responses:

* `200` Queued: SendResult
* `403` Error envelope: Error
* `422` Error envelope: Error
* `429` Error envelope: Error
* `503` Error envelope: Error

### POST /v0/inboxes/{inbox}/messages/{message}/release

Release a message held for review (needs message_release, audited)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `message` | string | in path, required, Message UUID or RFC Message-ID (URL encoded) |

Responses:

* `200` Released: Message
* `403` Error envelope: Error
* `404` Error envelope: Error
* `409` Error envelope: Error

### GET /v0/inboxes/{inbox}/messages/{message}/attachments/{attachment}

Download an attachment

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `message` | string | in path, required, Message UUID or RFC Message-ID (URL encoded) |
| `attachment` | string (uuid) | in path, required |
| `include_review` | boolean | in query, Also return messages held for security review; needs message_review, default false |

Responses:

* `200` Attachment bytes with its content type

### GET /v0/inboxes/{inbox}/drafts

List drafts, newest first (needs message_read)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `limit` | integer | in query, min 1, max 100, default 20 |
| `page_token` | string | in query |

Responses:

* `200` OK: DraftList

### POST /v0/inboxes/{inbox}/drafts

Create a draft (needs message_send)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |

Body (CreateDraft):

| Name | Type | Notes |
| --- | --- | --- |
| `to` | string (email)[] |  |
| `cc` | string (email)[] |  |
| `bcc` | string (email)[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |
| `in_reply_to` | string | Message UUID or RFC Message-ID to reply to |
| `reply_all` | boolean | With in_reply_to: copy the other recipients |
| `send_at` | string (date-time) | Send automatically at this time (future, within 365 days) |

Responses:

* `201` Created: Draft
* `400` Error envelope: Error
* `404` Error envelope: Error

### GET /v0/inboxes/{inbox}/drafts/{draft}

Get a draft (needs message_read)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `draft` | string (uuid) | in path, required |

Responses:

* `200` OK: Draft
* `404` Error envelope: Error

### PATCH /v0/inboxes/{inbox}/drafts/{draft}

Update a draft (needs message_send)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `draft` | string (uuid) | in path, required |

Body (UpdateDraft):

| Name | Type | Notes |
| --- | --- | --- |
| `to` | string (email)[] |  |
| `cc` | string (email)[] |  |
| `bcc` | string (email)[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |
| `send_at` | string,null (date-time) | null cancels a scheduled send |

Responses:

* `200` OK: Draft
* `400` Error envelope: Error
* `404` Error envelope: Error

### DELETE /v0/inboxes/{inbox}/drafts/{draft}

Delete a draft (needs message_send)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `draft` | string (uuid) | in path, required |

Responses:

* `204` Deleted

### POST /v0/inboxes/{inbox}/drafts/{draft}/send

Send a draft now and delete it (needs message_send)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `inbox` | string (email) | in path, required |
| `draft` | string (uuid) | in path, required |
| `Idempotency-Key` | string | in header, max length 256 |

Responses:

* `200` Queued: SendResult
* `400` Error envelope: Error
* `403` Error envelope: Error
* `404` Error envelope: Error
* `409` Error envelope: Error
* `429` Error envelope: Error

### GET /v0/events/stream

Live events as Server Sent Events (needs message_read)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `types` | string | in query, Comma separated event types; all when absent |
| `inbox` | string (email) | in query, Only events of this inbox |
| `Last-Event-ID` | integer | in header, Resume after this event id, min 0 |
| `last_event_id` | integer | in query, Same as Last-Event-ID, for clients that cannot set headers, min 0 |

Responses:

* `200` Event stream: string
* `400` Error envelope: Error
* `404` Error envelope: Error
* `429` Error envelope: Error

### GET /v0/domains

List own and shared domains

Responses:

* `200` OK

### POST /v0/domains

Add a custom domain; returns the DNS records to publish

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `domain` | string | required |

Responses:

* `201` Created: Domain
* `403` Error envelope: Error
* `409` Error envelope: Error

### GET /v0/domains/{domain}

Get a domain with record status

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `domain` | string | in path, required |

Responses:

* `200` OK: Domain

### DELETE /v0/domains/{domain}

Delete a domain without inboxes

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `domain` | string | in path, required |

Responses:

* `204` Deleted
* `409` Error envelope: Error

### POST /v0/domains/{domain}/verify

Check DNS now (the scheduler also checks every 5 minutes for 7 days)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `domain` | string | in path, required |

Responses:

* `200` OK: Domain

### GET /v0/api-keys

List active keys (requires api_key_read)

Responses:

* `200` OK

### POST /v0/api-keys

Create a key with at most the caller's permissions (key shown once)

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `name` | string | max length 100 |
| `permissions` | Permission[] | omit to copy the caller's permissions |
| `expires_at` | string (date-time) |  |

Responses:

* `201` Created
* `403` Error envelope: Error

### DELETE /v0/api-keys/{key}

Revoke a key immediately (requires api_key_write, no escalation)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `key` | string (uuid) | in path, required |

Responses:

* `204` Revoked

### GET /v0/audit-logs

Security relevant actions of the organization (requires audit_read)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `limit` | integer | in query, min 1, max 200, default 50 |

Responses:

* `200` OK

### GET /v0/webhooks

List webhooks

Responses:

* `200` OK

### POST /v0/webhooks

Create a webhook (secret is returned once)

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `url` | string (uri) | required |
| `event_types` | EventType[] | required, at least 1 item |
| `inbox_ids` | string (email)[] | at most 10 items |

Responses:

* `201` Created: Webhook

### DELETE /v0/webhooks/{webhook}

Delete a webhook

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `webhook` | string (uuid) | in path, required |

Responses:

* `204` Deleted

### GET /v0/billing

Plan and Stripe subscription state (requires billing_read)

Responses:

* `200` OK: Billing
* `403` Error envelope: Error
* `501` Error envelope: Error

### POST /v0/billing/checkout

Start a Stripe Checkout for a paid plan (requires billing_write, audited)

Body (Checkout):

| Name | Type | Notes |
| --- | --- | --- |
| `plan` | builder \| growth \| team | required |
| `interval` | monthly \| yearly | default monthly |
| `success_url` | string (uri) | Defaults to the deployment's MAI_BILLING_RETURN_URL |
| `cancel_url` | string (uri) | Defaults to the deployment's MAI_BILLING_RETURN_URL |

Responses:

* `200` OK: BillingSession
* `400` Error envelope: Error
* `403` Error envelope: Error
* `409` Error envelope: Error
* `501` Error envelope: Error
* `502` Error envelope: Error

### POST /v0/billing/portal

Open the Stripe customer portal (requires billing_write, audited)

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `return_url` | string (uri) |  |

Responses:

* `200` OK: BillingSession
* `403` Error envelope: Error
* `409` Error envelope: Error
* `501` Error envelope: Error
* `502` Error envelope: Error

### GET /v0/export

Export the organization's own data (requires data_export, audited)

Responses:

* `200` OK: OrgExport
* `403` Error envelope: Error

### DELETE /v0/organization

Delete the organization and all its data (requires data_delete, audited)

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `confirm` | string | required, The organization name, verbatim |

Responses:

* `200` Deleted; body carries the export taken before deletion: object
* `400` Error envelope: Error
* `403` Error envelope: Error

### GET /v0/tickets

List tickets, newest activity first

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `limit` | integer | in query, min 1, max 100, default 20 |
| `page_token` | string | in query |
| `state` | TicketState | in query |
| `priority` | TicketPriority | in query |
| `assignee` | string | in query, user id, API key id, or none |
| `inbox` | string | in query, inbox address |
| `q` | string | in query, case insensitive over subject and requester |

Responses:

* `200` OK: TicketList

### POST /v0/tickets

Open a ticket on an existing thread (requires ticket_write, audited)

Body (CreateTicket):

| Name | Type | Notes |
| --- | --- | --- |
| `thread_id` | string | required |
| `priority` | TicketPriority |  |
| `tags` | string[] |  |
| `assignee_user` | string |  |
| `assignee_key` | string |  |

Responses:

* `201` Created: Ticket
* `400` Error envelope: Error
* `403` Error envelope: Error
* `404` Error envelope: Error
* `409` Error envelope: Error

### GET /v0/tickets/stats

Queue counters and median first reply

Responses:

* `200` OK: TicketStats

### GET /v0/tickets/{ticket}

Read one ticket with its timeline and internal notes

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `ticket` | string | in path, required |

Responses:

* `200` OK: TicketDetail
* `404` Error envelope: Error

### PATCH /v0/tickets/{ticket}

Change state, priority, tags or assignment (requires ticket_write, audited)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `ticket` | string | in path, required |

Body (UpdateTicket):

| Name | Type | Notes |
| --- | --- | --- |
| `state` | TicketState |  |
| `priority` | TicketPriority |  |
| `tags` | string[] |  |
| `assignee_user` | string |  |
| `assignee_key` | string |  |

Responses:

* `200` OK: Ticket
* `400` Error envelope: Error
* `404` Error envelope: Error
* `409` Error envelope: Error

### POST /v0/tickets/{ticket}/notes

Add an internal note (requires ticket_write, audited)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `ticket` | string | in path, required |

Body:

| Name | Type | Notes |
| --- | --- | --- |
| `body` | string | required |

Responses:

* `201` Created: TicketNote
* `400` Error envelope: Error
* `404` Error envelope: Error

### POST /v0/tickets/{ticket}/reply

Reply to the customer on the ticket thread (requires message_send, audited)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `ticket` | string | in path, required |

Body (Reply):

| Name | Type | Notes |
| --- | --- | --- |
| `text` | string |  |
| `html` | string |  |
| `reply_all` | boolean |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |

Responses:

* `200` Sent: SendResult
* `403` Error envelope: Error
* `404` Error envelope: Error
* `409` Error envelope: Error

### GET /v0/push/subscriptions

List the devices the caller registered (requires notification_write)

Responses:

* `200` OK: PushSubscriptionList
* `403` Error envelope: Error
* `501` Error envelope: Error

### POST /v0/push/subscriptions

Register a browser for web push (requires notification_write, audited)

Body (PushSubscribe):

| Name | Type | Notes |
| --- | --- | --- |
| `endpoint` | string (uri) | required, The device at its push service. A capability: never logged, never returned |
| `keys` | object | required |
| `expiration_time` | integer | Milliseconds since the epoch, or null |
| `user_agent` | string | The browser that subscribed, for the device label, max length 512 |

Responses:

* `201` Created (or updated): PushSubscription
* `400` Error envelope: Error
* `403` Error envelope: Error
* `409` Error envelope: Error
* `422` Error envelope: Error
* `429` Error envelope: Error
* `501` Error envelope: Error

### DELETE /v0/push/subscriptions

Drop a device by endpoint (requires notification_write, audited)

Body (PushUnsubscribe):

| Name | Type | Notes |
| --- | --- | --- |
| `endpoint` | string (uri) | required, The endpoint the browser is dropping |

Responses:

* `204` No content, the device is off
* `400` Error envelope: Error
* `403` Error envelope: Error
* `501` Error envelope: Error

### DELETE /v0/push/subscriptions/{subscription}

Drop a device by id (requires notification_write, audited)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `subscription` | string (uuid) | in path, required |

Responses:

* `204` No content, the device is off
* `403` Error envelope: Error
* `404` Error envelope: Error
* `501` Error envelope: Error

### POST /stripe/webhook

Stripe webhook endpoint (public, Stripe-Signature verified)

Parameters:

| Name | Type | Notes |
| --- | --- | --- |
| `Stripe-Signature` | string | in header, required |

Responses:

* `200` Received; outcome is applied, duplicate, stale or ignored
* `400` Error envelope: Error
* `501` Error envelope: Error

## Objects

### User object

| Name | Type | Notes |
| --- | --- | --- |
| `user_id` | string (uuid) |  |
| `email` | string (email) |  |
| `name` | string |  |
| `provider` | magic_link \| google \| github | Identity that first proved this address |
| `role` | owner \| admin \| member |  |
| `created_at` | string (date-time) |  |
| `last_login_at` | string (date-time) |  |

### Session object

| Name | Type | Notes |
| --- | --- | --- |
| `access_token` | string | Signed JWT, send as Authorization: Bearer, 15 minutes |
| `refresh_token` | string | Single use, 30 days, rotate it at POST /v0/auth/refresh |
| `token_type` | Bearer |  |
| `expires_in` | integer | Seconds until the access token expires |
| `expires_at` | string (date-time) |  |
| `refresh_expires_at` | string (date-time) |  |
| `role` | owner \| admin \| member |  |
| `user` | User |  |
| `organization` | Org |  |

### OAuthStart object

| Name | Type | Notes |
| --- | --- | --- |
| `provider` | google \| github |  |
| `authorize_url` | string | Provider URL, with state and the S256 PKCE challenge |
| `state` | string | Single use, 10 minutes |
| `expires_at` | string (date-time) |  |

### Me object

| Name | Type | Notes |
| --- | --- | --- |
| `auth` | api_key \| session |  |
| `organization_id` | string (uuid) |  |
| `name` | string |  |
| `plan` | free \| builder \| growth \| team |  |
| `screen_before_notify` | boolean |  |
| `role` | owner \| admin \| member | Only for a session |
| `api_key_id` | string (uuid) | Only for an API key |
| `user` | object | Only for a session |
| `organization` | Org |  |
| `permissions` | string[] |  |
| `expires_at` | string (date-time) |  |

### Org object

| Name | Type | Notes |
| --- | --- | --- |
| `organization_id` | string (uuid) |  |
| `name` | string |  |
| `plan` | free \| builder \| growth \| team |  |
| `screen_before_notify` | boolean | Hold inbound mail until screened (operator setting: mai admin org-screening) |

### CreateInbox object

| Name | Type | Notes |
| --- | --- | --- |
| `username` | string | pattern ^[a-z0-9]([a-z0-9._-]{0,62}[a-z0-9])?$ |
| `domain` | string |  |
| `display_name` | string | max length 200 |
| `client_id` | string | pattern ^[A-Za-z0-9._~-]{1,256}$ |

### Inbox object

| Name | Type | Notes |
| --- | --- | --- |
| `id` | string (uuid) |  |
| `inbox_id` | string (email) |  |
| `email` | string (email) |  |
| `display_name` | string |  |
| `client_id` | string |  |
| `ticket_intake` | boolean | New threads on this inbox become tickets. |
| `sending_paused_at` | string (date-time) | Set when the bounce breaker paused sending (hard bounce rate above 5% over at least 20 sends in 24 hours); an operator resumes it. |
| `sending_paused_reason` | string |  |
| `created_at` | string (date-time) |  |
| `updated_at` | string (date-time) |  |

### InboxList object

| Name | Type | Notes |
| --- | --- | --- |
| `count` | integer |  |
| `next_page_token` | string |  |
| `inboxes` | Inbox[] |  |

### Thread object

| Name | Type | Notes |
| --- | --- | --- |
| `thread_id` | string (uuid) |  |
| `inbox_id` | string |  |
| `subject` | string |  |
| `preview` | string |  |
| `labels` | string[] |  |
| `message_count` | integer |  |
| `timestamp` | string (date-time) |  |
| `messages` | Message[] |  |

### ThreadList object

| Name | Type | Notes |
| --- | --- | --- |
| `count` | integer |  |
| `next_page_token` | string |  |
| `threads` | Thread[] |  |

### Message object

| Name | Type | Notes |
| --- | --- | --- |
| `id` | string (uuid) |  |
| `message_id` | string | RFC 5322 Message-ID with angle brackets |
| `thread_id` | string (uuid) |  |
| `inbox_id` | string |  |
| `direction` | in \| out |  |
| `from` | string |  |
| `to` | string[] |  |
| `cc` | string[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `extracted_text` | string | New text without quoted history (set by the gatekeeper) |
| `preview` | string |  |
| `labels` | string[] |  |
| `in_reply_to` | string |  |
| `references` | string[] |  |
| `status` | received \| queued \| sending \| sent \| failed |  |
| `authentication_results` | object | What the SMTP edge verified for inbound mail: dkim, spf, dmarc, dmarc_policy, antivirus, spam_score, spam_action. Mail failing DMARC is labelled dmarc-fail, and spam under p=quarantine or p=reject. |
| `gatekeeper` | object |  |
| `screening` | review \| allowed \| released | Hold state under screen before notify; absent when the message was not held. Pending messages are never returned. |
| `attachments` | object[] |  |
| `created_at` | string (date-time) |  |

### MessageList object

| Name | Type | Notes |
| --- | --- | --- |
| `count` | integer |  |
| `next_page_token` | string |  |
| `messages` | Message[] |  |

### Attachment object

| Name | Type | Notes |
| --- | --- | --- |
| `filename` | string |  |
| `content_type` | string |  |
| `content` | string (base64) | required |

### Send object

| Name | Type | Notes |
| --- | --- | --- |
| `to` | string (email)[] | required, at least 1 item |
| `cc` | string (email)[] |  |
| `bcc` | string (email)[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |

### Reply object

| Name | Type | Notes |
| --- | --- | --- |
| `text` | string |  |
| `html` | string |  |
| `reply_all` | boolean |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |

### SendResult object

| Name | Type | Notes |
| --- | --- | --- |
| `message_id` | string |  |
| `thread_id` | string (uuid) |  |

### CreateDraft object

| Name | Type | Notes |
| --- | --- | --- |
| `to` | string (email)[] |  |
| `cc` | string (email)[] |  |
| `bcc` | string (email)[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |
| `in_reply_to` | string | Message UUID or RFC Message-ID to reply to |
| `reply_all` | boolean | With in_reply_to: copy the other recipients |
| `send_at` | string (date-time) | Send automatically at this time (future, within 365 days) |

### UpdateDraft object

| Name | Type | Notes |
| --- | --- | --- |
| `to` | string (email)[] |  |
| `cc` | string (email)[] |  |
| `bcc` | string (email)[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `labels` | string[] |  |
| `attachments` | Attachment[] |  |
| `send_at` | string,null (date-time) | null cancels a scheduled send |

### Draft object

| Name | Type | Notes |
| --- | --- | --- |
| `draft_id` | string (uuid) |  |
| `inbox_id` | string (email) |  |
| `thread_id` | string (uuid) | Set for a reply draft |
| `in_reply_to` | string | RFC Message-ID of the message replied to |
| `references` | string[] |  |
| `to` | string[] |  |
| `cc` | string[] |  |
| `bcc` | string[] |  |
| `subject` | string |  |
| `text` | string |  |
| `html` | string |  |
| `labels` | string[] |  |
| `attachments` | object[] |  |
| `send_at` | string (date-time) |  |
| `status` | draft \| scheduled \| failed |  |
| `last_error` | string | Why a scheduled send was refused |
| `created_at` | string (date-time) |  |
| `updated_at` | string (date-time) |  |

### DraftList object

| Name | Type | Notes |
| --- | --- | --- |
| `count` | integer |  |
| `next_page_token` | string |  |
| `drafts` | Draft[] |  |

### Webhook object

| Name | Type | Notes |
| --- | --- | --- |
| `webhook_id` | string (uuid) |  |
| `url` | string |  |
| `event_types` | EventType[] |  |
| `inbox_ids` | string[] |  |
| `enabled` | boolean |  |
| `secret` | string | whsec_..., returned only on create |

### Domain object

| Name | Type | Notes |
| --- | --- | --- |
| `domain_id` | string (uuid) |  |
| `domain` | string |  |
| `shared` | boolean |  |
| `status` | pending \| verified |  |
| `records` | object[] |  |
| `last_checked_at` | string (date-time) |  |
| `verified_at` | string (date-time) |  |

### PushSubscribe object

| Name | Type | Notes |
| --- | --- | --- |
| `endpoint` | string (uri) | required, The device at its push service. A capability: never logged, never returned |
| `keys` | object | required |
| `expiration_time` | integer | Milliseconds since the epoch, or null |
| `user_agent` | string | The browser that subscribed, for the device label, max length 512 |

### PushUnsubscribe object

| Name | Type | Notes |
| --- | --- | --- |
| `endpoint` | string (uri) | required, The endpoint the browser is dropping |

### PushSubscription object

| Name | Type | Notes |
| --- | --- | --- |
| `push_subscription_id` | string (uuid) |  |
| `device` | string | Label derived from the user agent, for example iPhone |
| `user_agent` | string |  |
| `created_at` | string (date-time) |  |
| `last_used_at` | string (date-time) |  |

### PushSubscriptionList object

| Name | Type | Notes |
| --- | --- | --- |
| `count` | integer |  |
| `push_subscriptions` | PushSubscription[] |  |

### Ticket object

| Name | Type | Notes |
| --- | --- | --- |
| `id` | string |  |
| `number` | integer |  |
| `inbox_id` | string |  |
| `thread_id` | string |  |
| `subject` | string |  |
| `requester` | string |  |
| `state` | TicketState |  |
| `priority` | TicketPriority |  |
| `assignee_user` | string |  |
| `assignee_key` | string |  |
| `sla_policy` | string |  |
| `due_first_reply` | string (date-time) |  |
| `due_resolution` | string (date-time) |  |
| `first_reply_at` | string (date-time) |  |
| `resolved_at` | string (date-time) |  |
| `closed_at` | string (date-time) |  |
| `tags` | string[] |  |
| `created_at` | string (date-time) |  |
| `updated_at` | string (date-time) |  |

### TicketList object

| Name | Type | Notes |
| --- | --- | --- |
| `count` | integer |  |
| `next_page_token` | string |  |
| `tickets` | Ticket[] |  |

### TicketEvent object

| Name | Type | Notes |
| --- | --- | --- |
| `id` | integer |  |
| `kind` | string |  |
| `actor` | string |  |
| `detail` | object |  |
| `created_at` | string (date-time) |  |

### TicketNote object

| Name | Type | Notes |
| --- | --- | --- |
| `id` | string |  |
| `author` | string |  |
| `body` | string |  |
| `created_at` | string (date-time) |  |

### TicketDetail object

| Name | Type | Notes |
| --- | --- | --- |
| `ticket` | Ticket |  |
| `events` | TicketEvent[] |  |
| `notes` | TicketNote[] |  |

### TicketStats object

| Name | Type | Notes |
| --- | --- | --- |
| `by_state` | object |  |
| `open` | integer |  |
| `breaching` | integer |  |
| `unassigned` | integer |  |
| `median_first_reply_seconds` | integer |  |

### CreateTicket object

| Name | Type | Notes |
| --- | --- | --- |
| `thread_id` | string | required |
| `priority` | TicketPriority |  |
| `tags` | string[] |  |
| `assignee_user` | string |  |
| `assignee_key` | string |  |

### UpdateTicket object

| Name | Type | Notes |
| --- | --- | --- |
| `state` | TicketState |  |
| `priority` | TicketPriority |  |
| `tags` | string[] |  |
| `assignee_user` | string |  |
| `assignee_key` | string |  |

### Checkout object

| Name | Type | Notes |
| --- | --- | --- |
| `plan` | builder \| growth \| team | required |
| `interval` | monthly \| yearly | default monthly |
| `success_url` | string (uri) | Defaults to the deployment's MAI_BILLING_RETURN_URL |
| `cancel_url` | string (uri) | Defaults to the deployment's MAI_BILLING_RETURN_URL |

### BillingSession object

| Name | Type | Notes |
| --- | --- | --- |
| `session_id` | string |  |
| `url` | string (uri) |  |

### Billing object

| Name | Type | Notes |
| --- | --- | --- |
| `plan` | free \| builder \| growth \| team |  |
| `status` | none \| incomplete \| incomplete_expired \| trialing \| active \| past_due \| unpaid \| canceled \| paused |  |
| `interval` | monthly \| yearly |  |
| `current_period_end` | string (date-time) |  |
| `cancel_at_period_end` | boolean |  |
| `past_due_since` | string (date-time) |  |
| `grace_period_ends_at` | string (date-time) | Set while past_due or unpaid; the plan drops to free after it |

### OrgExport object

| Name | Type | Notes |
| --- | --- | --- |
| `format` | string |  |
| `generated_at` | string (date-time) |  |
| `organization` | Org |  |
| `inboxes` | object[] |  |
| `domains` | object[] |  |
| `webhooks` | object[] |  |
| `api_keys` | object[] | Metadata only; the key and its hash are never exported. |
| `counts` | object |  |
| `audit_log` | object[] |  |
| `notice` | string | What this export deliberately leaves out, in plain language |

### Error object

| Name | Type | Notes |
| --- | --- | --- |
| `name` | string | required |
| `code` | missing_authorization \| unknown_api_key \| validation_error \| not_found \| already_exists \| domain_not_verified \| conflict \| cannot_delete \| limit_exceeded \| rate_limit_exceeded \| rate_limited \| address_quarantined \| sending_paused \| missing_permission \| permission_escalation \| api_key_expired \| not_held \| billing_disabled \| billing_provider_error \| already_subscribed \| no_billing_account \| invalid_signature \| too_many_streams \| payload_too_large \| attachment_infected \| antivirus_unavailable \| auth_disabled \| provider_not_configured \| invalid_magic_link \| invalid_state \| code_rejected \| email_not_verified \| provider_unavailable \| email_delivery_failed \| invalid_refresh_token \| token_expired \| unknown_token \| session_revoked \| access_denied \| internal_error \| thread_required \| thread_not_found \| ticket_not_found \| ticket_exists \| invalid_transition \| invalid_priority \| note_required \| no_inbound_message \| push_disabled \| too_many_subscriptions | required |
| `message` | string | required |
