# Zemail Developer API — OpenAPI 3.1 Specification # # This file is the machine-readable source of truth for the public Developer API # contract. It is linted in CI (specs/spectral.yaml) and drift-tested against # routes/api.php (tests/Feature/OpenApiSpecDriftTest.php) so the docs can never rot. # # x-zemail-* extensions are private to Zemail but safe for consumers to ignore: # x-zemail-scope scope required by this operation (mirrors RequireDeveloperApiScope) # x-zemail-error-codes codes this operation can emit (subset of the catalog in info) openapi: 3.1.0 info: title: Zemail Developer API version: '2026-04-23' description: >- The Zemail Developer API lets you manage disposable mailboxes and read their mail programmatically: account and usage snapshots, domain discovery, mailbox lifecycle, email retrieval (with search), mark-as-read, deletion, and short-lived signed download URLs for attachments. contact: name: Zemail Support email: support@imail.me x-zemail-supported-versions: - '2026-04-23' x-zemail-scopes: - account:read - subscription:read - usage:read - domains:read - mailboxes:read - mailboxes:create - mailboxes:delete - emails:read - emails:delete - emails:mark-read x-zemail-error-codes: - code: missing_api_key type: authentication_error status: 401 description: No Authorization Bearer header was sent. - code: invalid_api_key type: authentication_error status: 401 description: The provided API key (or its developer app) is invalid. - code: api_key_revoked type: authentication_error status: 401 description: The provided API key has been revoked. - code: api_key_expired type: authentication_error status: 401 description: The provided API key has expired. - code: account_suspended type: authentication_error status: 401 description: The account that owns the API key is suspended. - code: developer_api_not_enabled type: permission_error status: 403 description: The account's current plan does not include Developer API access. - code: insufficient_scope type: permission_error status: 403 description: The API key lacks the scope required by this endpoint. - code: unsupported_version type: invalid_request_error status: 400 param: Zemail-Version description: The Zemail-Version header requests a version that is not supported. - code: validation_failed type: invalid_request_error status: 422 description: The request payload failed validation; an `errors` map is included. - code: domain_not_accessible type: invalid_request_error status: 422 description: The requested domain is not available for the account or plan. - code: daily_limit_reached type: invalid_request_error status: 422 description: The account's daily mailbox creation limit is reached. - code: mailbox_limit_reached type: invalid_request_error status: 422 description: The account's active mailbox limit is reached. - code: mailbox_creation_failed type: invalid_request_error status: 422 description: Mailbox creation failed (e.g. invalid custom username or no Google usernames available). - code: address_in_use type: invalid_request_error status: 422 description: The requested address is already in use by another account. - code: address_in_cooldown type: invalid_request_error status: 422 description: The address was deleted recently and is in a reclaim cooldown window. - code: invalid_request type: invalid_request_error status: 422 description: Request rejected for a domain-specific reason. - code: mailbox_delete_failed type: invalid_request_error status: 422 description: Mailbox deletion refused (deleting the last mailbox would exhaust the daily creation limit). - code: attachment_not_available type: invalid_request_error status: 422 description: The attachment does not exist or has no downloadable S3 key. - code: mailbox_not_found type: not_found_error status: 404 description: No owned, visible mailbox matches the `mailbox` path parameter. - code: email_not_found type: not_found_error status: 404 description: No visible email matches the `email` path parameter within the mailbox. - code: rate_limit_exceeded type: rate_limit_error status: 429 description: Requests-per-minute limit exceeded. Retry after the Retry-After header. - code: daily_rate_limit_exceeded type: rate_limit_error status: 429 description: Requests-per-day limit exceeded. Do not retry until the Retry-After header. servers: - url: https://zemail.me description: 'Production base URL. All paths below are prefixed with /api. Requires an API key (Authorization: Bearer zm_live_...).' tags: - name: account description: Account, subscription and usage snapshots. - name: domains description: Domains available for mailbox creation. - name: mailboxes description: Mailbox lifecycle (list, create, get, delete). - name: emails description: Email retrieval, search, read state, deletion and attachment downloads. security: - BearerAuth: [] paths: /api/account: get: operationId: getAccount summary: Get the authenticated account snapshot description: >- Returns the account associated with the API key, its current plan, and the Developer API limits that apply to it. tags: - account x-zemail-scope: account:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' responses: '200': description: Account snapshot. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/AccountEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' /api/account/subscription: get: operationId: getSubscription summary: Get the current subscription snapshot description: Returns the account's current subscription state and plan. tags: - account x-zemail-scope: subscription:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' responses: '200': description: Subscription snapshot. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/SubscriptionEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' /api/account/usage: get: operationId: getUsage summary: Get mailbox, storage and Developer API usage description: >- Returns current mailbox and storage utilization plus Developer API usage (active apps/keys and the limits that apply to the account). tags: - account x-zemail-scope: usage:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' responses: '200': description: Usage snapshot. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/UsageEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' /api/domains: get: operationId: listDomains summary: List domains available for mailbox creation description: Returns active, non-archived domains accessible to the authenticated account. tags: - domains x-zemail-scope: domains:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' responses: '200': description: Domain list. Unlike paginated lists, `has_more` is false and `meta.count` is the total. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/DomainListEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' /api/mailboxes: get: operationId: listMailboxes summary: List owned mailboxes description: >- Returns the authenticated account's non-blocked mailboxes, newest first. Pagination uses Laravel page numbers: pass `page` to move forward; `next_cursor` is the next page number when another page exists. tags: - mailboxes x-zemail-scope: mailboxes:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxesLimit' - $ref: '#/components/parameters/Page' responses: '200': description: Paginated mailbox list. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/MailboxListEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '429': $ref: '#/components/responses/RateLimited' post: operationId: createMailbox summary: Create a mailbox description: >- Creates a disposable mailbox for the authenticated account. `type=random` creates a mailbox on an eligible public domain. If `domain` is provided it must be an active public domain accessible to the account. `type=custom` uses the provided `domain`. For Google alias domains the API may generate the final address from a Google username pool (honoring `google_alias_mode`); otherwise the final address is `@`. Custom usernames must be at least 6 characters and match the pattern `^[a-z0-9]+([.\-+][a-z0-9]+)*$` (letters, numbers, and single `.`, `-`, or `+` separators between alphanumeric segments). If a previously deleted address owned by the same account exists, the API may restore it instead of creating a new record. tags: - mailboxes x-zemail-scope: mailboxes:create x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - validation_failed - domain_not_accessible - daily_limit_reached - mailbox_limit_reached - mailbox_creation_failed - address_in_use - address_in_cooldown - invalid_request - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateMailboxRequest' responses: '201': description: The created mailbox. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/MailboxEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '422': $ref: '#/components/responses/UnprocessableMailboxRequest' '429': $ref: '#/components/responses/RateLimited' /api/mailboxes/{mailbox}: get: operationId: getMailbox summary: Get a single mailbox description: Returns a single owned mailbox using the same shape as mailbox list items. tags: - mailboxes x-zemail-scope: mailboxes:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - mailbox_not_found - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxId' responses: '200': description: The mailbox. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/MailboxEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' delete: operationId: deleteMailbox summary: Delete a mailbox description: >- Deletes an owned mailbox. Deletion is refused when the mailbox is the account's last active mailbox and the daily creation limit is exhausted, to avoid leaving the account without an active address. tags: - mailboxes x-zemail-scope: mailboxes:delete x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - mailbox_not_found - mailbox_delete_failed - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxId' responses: '200': description: Confirmation that the mailbox was deleted. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/DeletedMailboxEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/UnprocessableMailboxRequest' '429': $ref: '#/components/responses/RateLimited' /api/mailboxes/{mailbox}/emails: get: operationId: listEmails summary: List emails for a mailbox description: >- Returns visible emails for an owned mailbox, newest first. `search` matches the `subject`, `sender_email` and `sender_name` fields (substring LIKE). Emails from blocked sender domains are returned as blocked email summaries (`is_blocked: true`) instead of normal message details. tags: - emails x-zemail-scope: emails:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - mailbox_not_found - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxId' - $ref: '#/components/parameters/EmailsLimit' - $ref: '#/components/parameters/Page' - $ref: '#/components/parameters/Search' responses: '200': description: Paginated email summary list. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/EmailListEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /api/mailboxes/{mailbox}/emails/{email}: get: operationId: getEmail summary: Get full email details description: >- Returns full details for an owned visible email, including plain-text/HTML body and attachments. When the email is from a blocked sender domain the detail is masked: `is_blocked` is true, `blocked_domain` is set, `sender_name` and `body_html` are null, and `attachments` is an empty array. tags: - emails x-zemail-scope: emails:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - mailbox_not_found - email_not_found - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxId' - $ref: '#/components/parameters/EmailId' responses: '200': description: Email details. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/EmailDetailEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' delete: operationId: deleteEmail summary: Delete an email description: Deletes an owned visible email. tags: - emails x-zemail-scope: emails:delete x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - mailbox_not_found - email_not_found - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxId' - $ref: '#/components/parameters/EmailId' responses: '200': description: Confirmation that the email was deleted. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/DeletedEmailEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /api/mailboxes/{mailbox}/emails/{email}/mark-read: post: operationId: markEmailRead summary: Mark an email as read description: Marks an owned visible email as read. tags: - emails x-zemail-scope: emails:mark-read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - mailbox_not_found - email_not_found - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxId' - $ref: '#/components/parameters/EmailId' responses: '200': description: The email id and its new read state. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/EmailReadEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/RateLimited' /api/mailboxes/{mailbox}/emails/{email}/attachments/{attachment}/download-url: post: operationId: createAttachmentDownloadUrl summary: Create a temporary attachment download URL description: >- Creates a short-lived, signed S3 download URL (default TTL 300 seconds) for an owned attachment. Only works when the attachment exists and has a downloadable S3 key. tags: - emails x-zemail-scope: emails:read x-zemail-error-codes: - unsupported_version - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - mailbox_not_found - email_not_found - attachment_not_available - rate_limit_exceeded - daily_rate_limit_exceeded parameters: - $ref: '#/components/parameters/ZemailVersion' - $ref: '#/components/parameters/MailboxId' - $ref: '#/components/parameters/EmailId' - $ref: '#/components/parameters/AttachmentId' responses: '200': description: Temporary download URL and its expiry. headers: Zemail-Version: $ref: '#/components/headers/ZemailVersion' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/AttachmentDownloadEnvelope' '400': $ref: '#/components/responses/UnsupportedVersion' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '422': $ref: '#/components/responses/UnprocessableMailboxRequest' '429': $ref: '#/components/responses/RateLimited' components: securitySchemes: BearerAuth: type: http scheme: bearer description: >- API keys look like `zm_live_<48 lowercase alphanumeric characters>` and are issued once in the dashboard (Developer Apps > API keys). Keys are scoped: each key only grants the scopes selected when it was created, intersected with its developer app's scopes. The server stores only a hash of the key; it can never be revealed again after creation. Rate limits apply per account, not per key — all of an account's keys share the same bucket. parameters: ZemailVersion: name: Zemail-Version in: header required: false description: >- Requests the API version to use. Optional: if omitted the API resolves the developer app's default version, then the platform default. Unsupported values return 400 with code `unsupported_version` and `param: Zemail-Version`. The resolved version is echoed back in the `Zemail-Version` response header. schema: type: string enum: - '2026-04-23' default: '2026-04-23' MailboxId: name: mailbox in: path required: true description: The mailbox primary key id. schema: type: integer format: int64 EmailId: name: email in: path required: true description: The email primary key id. schema: type: integer format: int64 AttachmentId: name: attachment in: path required: true description: The attachment id (UUID as stored in the email's attachment list). schema: type: string MailboxesLimit: name: limit in: query required: false description: >- Number of mailboxes per page. Defaults to 25 and is capped by the account's plan (100 for Plus and Pro). schema: type: integer minimum: 1 default: 25 EmailsLimit: name: limit in: query required: false description: >- Number of emails per page. Defaults to 25 and is capped by the account's plan (50 for Plus, 100 for Pro). schema: type: integer minimum: 1 default: 25 Page: name: page in: query required: false description: Page number to fetch. Follow `next_cursor` from a previous response to walk pages. schema: type: integer minimum: 1 default: 1 Search: name: search in: query required: false description: >- Case-insensitive substring filter matched against `subject`, `sender_email`, and `sender_name`. schema: type: string headers: ZemailVersion: description: The API version resolved for this request. schema: type: string enum: - '2026-04-23' ZemailRequestId: description: >- Unique id for this request (zreq_...). Echoed in error payloads; reference it when contacting support or correlating against dashboard request logs. schema: type: string example: zreq_0123456789abcdef01234567 RetryAfter: description: Seconds to wait before retrying (60 for the minute limit, 86400 for the daily limit). schema: type: integer schemas: ErrorType: type: string enum: - authentication_error - permission_error - not_found_error - invalid_request_error - rate_limit_error ErrorCode: type: string enum: - missing_api_key - invalid_api_key - api_key_revoked - api_key_expired - account_suspended - developer_api_not_enabled - insufficient_scope - unsupported_version - validation_failed - domain_not_accessible - daily_limit_reached - mailbox_limit_reached - mailbox_creation_failed - address_in_use - address_in_cooldown - invalid_request - mailbox_delete_failed - attachment_not_available - mailbox_not_found - email_not_found - rate_limit_exceeded - daily_rate_limit_exceeded ErrorEnvelope: type: object description: Uniform error envelope returned by every endpoint. required: - error properties: error: type: object required: - type - code - message - param - request_id properties: type: $ref: '#/components/schemas/ErrorType' code: $ref: '#/components/schemas/ErrorCode' message: type: string param: type: - string - 'null' description: The offending request parameter (e.g. `username`, `Zemail-Version`). request_id: type: - string - 'null' description: The same request id echoed in the Zemail-Request-Id response header. ValidationErrorEnvelope: type: object description: Error envelope for `validation_failed`; adds an `errors` map keyed by field. required: - error properties: error: type: object required: - type - code - message - param - request_id - errors properties: type: type: string const: invalid_request_error code: type: string const: validation_failed message: type: string param: type: - string - 'null' request_id: type: - string - 'null' errors: type: object description: Field name to list of validation messages. additionalProperties: type: array items: type: string PlanLimits: type: object description: Developer API limits that apply to the account's current plan. required: - apps_limit - keys_limit - webhook_endpoints_limit - requests_per_minute - requests_per_day - mailboxes_page_size - emails_page_size - request_log_retention_days properties: apps_limit: type: integer description: Max active developer apps (3 Plus, 10 Pro/admin). keys_limit: type: integer description: Max active API keys (10 Plus, 30 Pro/admin). webhook_endpoints_limit: type: integer description: Reserved for future use; always 0 today. requests_per_minute: type: integer description: Per-account requests-per-minute (120 Plus, 600 Pro/admin). requests_per_day: type: integer description: Per-account requests-per-day (25000 Plus, 150000 Pro/admin). mailboxes_page_size: type: integer description: Max mailbox page size (100 on all Developer API plans). emails_page_size: type: integer description: Max email page size (50 Plus, 100 Pro/admin). request_log_retention_days: type: integer description: Dashboard request log retention (7 Plus, 30 Pro/admin). CurrentPlan: type: object required: - slug - name properties: slug: type: string example: plus name: type: string example: Plus AccountDeveloperApi: type: object required: - enabled - default_version - limits properties: enabled: type: boolean description: Whether the account's plan includes Developer API access. default_version: type: - string - 'null' description: The developer app's default API version. limits: $ref: '#/components/schemas/PlanLimits' Account: type: object required: - id - name - email - email_verified_at - tier - tier_label - current_plan - developer_api properties: id: type: integer format: int64 name: type: string email: type: string format: email email_verified_at: type: - string - 'null' format: date-time tier: type: string enum: - free - plus - pro - admin tier_label: type: string enum: - FREE - PLUS - PRO - ADMIN current_plan: oneOf: - $ref: '#/components/schemas/CurrentPlan' - type: 'null' developer_api: $ref: '#/components/schemas/AccountDeveloperApi' Subscription: type: object required: - status - tier - plan - starts_at - ends_at - cancelled_at properties: status: type: string enum: - active - inactive tier: type: string enum: - free - plus - pro - admin plan: type: - object - 'null' description: Current plan snapshot; null when there is no active subscription. properties: slug: type: string name: type: string tier_role: type: string starts_at: type: - string - 'null' format: date-time ends_at: type: - string - 'null' format: date-time cancelled_at: type: - string - 'null' format: date-time MailboxUsage: type: object required: - active_count - active_limit - daily_count - daily_limit properties: active_count: type: integer active_limit: type: integer daily_count: type: integer daily_limit: type: integer StorageUsage: type: object required: - limit_bytes - actual_used_bytes - visible_used_bytes - hidden_email_count - percent properties: limit_bytes: type: integer description: Storage limit in bytes for the account's plan. actual_used_bytes: type: integer description: Total bytes stored across all emails, including hidden overflow. visible_used_bytes: type: integer description: Bytes visible in inboxes (within the storage limit). hidden_email_count: type: integer description: Emails hidden because the storage limit was exceeded. percent: type: number minimum: 0 maximum: 100 description: Percent of the storage limit used (capped at 100). DeveloperApiUsage: type: object required: - apps_count - keys_count - limits properties: apps_count: type: integer description: Active developer apps on the account. keys_count: type: integer description: Active (non-revoked) API keys across all apps. limits: $ref: '#/components/schemas/PlanLimits' Usage: type: object required: - mailboxes - storage - developer_api properties: mailboxes: $ref: '#/components/schemas/MailboxUsage' storage: $ref: '#/components/schemas/StorageUsage' developer_api: $ref: '#/components/schemas/DeveloperApiUsage' Domain: type: object required: - id - name - allowed_types properties: id: type: integer format: int64 name: type: string example: imail.app allowed_types: type: array description: Use cases this domain supports (e.g. `public`, `custom`, `premium`). items: type: string Mailbox: type: object required: - id - address - type - domain - expires_at - created_at - unread_count - emails_count properties: id: type: integer format: int64 address: type: string format: email example: user@imail.app type: type: string description: Mailbox type (`public` or `custom`; Google alias mailboxes use the source username type). domain: type: string description: Domain portion of the address. expires_at: type: - string - 'null' format: date-time created_at: type: - string - 'null' format: date-time unread_count: type: integer description: Visible unread emails in this mailbox. emails_count: type: integer description: Visible emails in this mailbox (blocked-sender emails excluded). EmailSummary: type: object required: - id - sender - sender_email - subject - preview - received_at - is_read - is_blocked - attachments_count properties: id: type: integer format: int64 sender: type: string description: Display name, falling back to the sender email; masked for blocked senders. sender_email: type: string format: email subject: type: string preview: type: string received_at: type: - string - 'null' format: date-time is_read: type: boolean is_blocked: type: boolean description: True when the sender domain is blocked by mailbox policy; the summary is then masked. attachments_count: type: integer EmailAttachment: type: object required: - id - name - size - downloadable properties: id: type: - string - 'null' description: Attachment id (UUID); null if the stored metadata lacks one. name: type: - string - 'null' size: type: - integer - 'null' description: Size in bytes. downloadable: type: boolean description: True when the attachment has a stored S3 key and can be fetched via the download-url endpoint. EmailDetail: type: object required: - id - sender - sender_name - sender_email - subject - preview - received_at - is_read - is_blocked - blocked_domain - body_text - body_html - attachments properties: id: type: integer format: int64 sender: type: string sender_name: type: - string - 'null' description: Null for masked (blocked-sender) emails. sender_email: type: string format: email subject: type: string preview: type: string received_at: type: - string - 'null' format: date-time is_read: type: boolean is_blocked: type: boolean blocked_domain: type: - string - 'null' description: Sender domain when the email is blocked; null otherwise. body_text: type: - string - 'null' body_html: type: - string - 'null' description: Always null for masked (blocked-sender) emails. attachments: type: array description: Always empty for masked (blocked-sender) emails. items: $ref: '#/components/schemas/EmailAttachment' AttachmentDownload: type: object required: - url - expires_at properties: url: type: string format: uri description: Short-TTL signed S3 URL (default 300 seconds). expires_at: type: string format: date-time EmailReadState: type: object required: - id - is_read properties: id: type: integer format: int64 is_read: type: boolean const: true DeletedObject: type: object required: - deleted - id properties: deleted: type: boolean const: true id: type: integer format: int64 description: Id of the deleted resource. CreateMailboxRequest: type: object required: - type properties: type: type: string enum: - random - custom domain: type: - string - 'null' description: >- Required when `type=custom`. For `type=random` it selects a specific public domain; omitted, the API picks an eligible public domain. username: type: - string - 'null' description: >- Required when `type=custom` on non-Google-alias domains. At least 6 characters matching `^[a-z0-9]+([.\-+][a-z0-9]+)*$` (letters, numbers, and single `.`, `-`, or `+` separators between alphanumeric segments). google_alias_mode: type: - string - 'null' description: Alias mode for Google alias domains. enum: - auto - dot - plus - combined example: type: custom domain: imail.app username: api-test google_alias_mode: auto ListEnvelope: type: object description: >- Paginated list envelope. `next_cursor` is the next page number when another page exists; pass it as the `page` query parameter to continue. `meta` varies by endpoint (pagination totals for mailboxes/emails, count for domains). required: - object - data - has_more - next_cursor - meta properties: object: type: string const: list data: type: array description: Item payloads; the item schema is defined by each endpoint's list envelope. items: {} has_more: type: boolean description: True when the next page exists. next_cursor: type: - integer - 'null' description: Next page number when another page exists; null otherwise. meta: type: object MailboxListEnvelope: allOf: - $ref: '#/components/schemas/ListEnvelope' - type: object properties: data: type: array items: $ref: '#/components/schemas/Mailbox' meta: $ref: '#/components/schemas/PageMeta' EmailListEnvelope: allOf: - $ref: '#/components/schemas/ListEnvelope' - type: object properties: data: type: array items: $ref: '#/components/schemas/EmailSummary' meta: $ref: '#/components/schemas/PageMeta' DomainListEnvelope: allOf: - $ref: '#/components/schemas/ListEnvelope' - type: object properties: data: type: array items: $ref: '#/components/schemas/Domain' meta: type: object required: - count properties: count: type: integer AccountEnvelope: type: object required: - object - data - meta properties: object: type: string const: account data: $ref: '#/components/schemas/Account' meta: type: object SubscriptionEnvelope: type: object required: - object - data - meta properties: object: type: string const: subscription data: $ref: '#/components/schemas/Subscription' meta: type: object UsageEnvelope: type: object required: - object - data - meta properties: object: type: string const: usage data: $ref: '#/components/schemas/Usage' meta: type: object MailboxEnvelope: type: object required: - object - data - meta properties: object: type: string const: mailbox data: $ref: '#/components/schemas/Mailbox' meta: type: object EmailDetailEnvelope: type: object required: - object - data - meta properties: object: type: string const: email data: $ref: '#/components/schemas/EmailDetail' meta: type: object EmailReadEnvelope: type: object required: - object - data - meta properties: object: type: string const: email data: $ref: '#/components/schemas/EmailReadState' meta: type: object DeletedMailboxEnvelope: type: object required: - object - data - meta properties: object: type: string const: mailbox.deleted data: $ref: '#/components/schemas/DeletedObject' meta: type: object DeletedEmailEnvelope: type: object required: - object - data - meta properties: object: type: string const: email.deleted data: $ref: '#/components/schemas/DeletedObject' meta: type: object AttachmentDownloadEnvelope: type: object required: - object - data - meta properties: object: type: string const: attachment.download data: $ref: '#/components/schemas/AttachmentDownload' meta: type: object PageMeta: type: object required: - current_page - per_page - total properties: current_page: type: integer per_page: type: integer total: type: integer responses: UnsupportedVersion: description: The Zemail-Version header requests an unsupported API version. headers: Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: error: type: invalid_request_error code: unsupported_version message: The requested Zemail API version is not supported. param: Zemail-Version request_id: zreq_0123456789abcdef01234567 Unauthorized: description: Missing, invalid, revoked, expired or suspended authentication. headers: Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: error: type: authentication_error code: missing_api_key message: A Zemail API key must be provided via the Authorization Bearer header. param: null request_id: zreq_0123456789abcdef01234567 Forbidden: description: The account's plan has no Developer API access or the key lacks the required scope. headers: Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: error: type: permission_error code: insufficient_scope message: Your Zemail API key does not have permission to perform this action. param: null request_id: zreq_0123456789abcdef01234567 NotFound: description: The referenced mailbox or email does not exist for this account. headers: Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' example: error: type: not_found_error code: mailbox_not_found message: Mailbox not found. param: null request_id: zreq_0123456789abcdef01234567 UnprocessableMailboxRequest: description: >- Request rejected by the mailbox/attachment business rules (invalid_request_error with one of the operation's x-zemail-error-codes), or request validation failed (validation_failed) which additionally includes an `errors` map keyed by field. headers: Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: oneOf: - $ref: '#/components/schemas/ErrorEnvelope' - $ref: '#/components/schemas/ValidationErrorEnvelope' RateLimited: description: Per-account rate limit exceeded (minute or daily bucket). headers: Retry-After: $ref: '#/components/headers/RetryAfter' Zemail-Request-Id: $ref: '#/components/headers/ZemailRequestId' content: application/json: schema: $ref: '#/components/schemas/ErrorEnvelope' examples: rate_limit_exceeded: summary: Requests-per-minute limit exceeded value: error: type: rate_limit_error code: rate_limit_exceeded message: You have exceeded your Zemail API requests-per-minute limit. param: null request_id: zreq_0123456789abcdef01234567 daily_rate_limit_exceeded: summary: Requests-per-day limit exceeded value: error: type: rate_limit_error code: daily_rate_limit_exceeded message: You have exceeded your Zemail API daily request limit. param: null request_id: zreq_0123456789abcdef01234567