openapi: 3.0.3 info: title: Stalwart Management API description: | REST Management API for Stalwart server. These endpoints are helpers that complement the JMAP API — most of the server's configuration and data is managed via JMAP (see `POST /jmap/`). The endpoints documented here cover interactive login, account introspection, configuration schema retrieval and live (Server-Sent Events) telemetry streams. version: "1.0" license: name: AGPL-3.0-only OR LicenseRef-SEL servers: - url: https://{host} description: Stalwart server variables: host: default: mail.example.com description: The hostname of Stalwart server security: - bearerAuth: [] - basicAuth: [] paths: /api/auth: post: operationId: login summary: Authenticate a user and obtain an authorization code description: | Anonymous endpoint used by the web UI and device-flow clients to exchange user credentials (plus optional MFA token and PKCE challenge) for an OAuth authorization `client_code` that can then be exchanged for an access token via `POST /auth/token`. Rate-limited as an anonymous request. security: [] tags: [Authentication] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' examples: authCode: summary: Authorization-code flow (web UI) value: type: authCode accountName: jane@example.com accountSecret: s3cret clientId: webadmin redirectUri: https://mail.example.com/login codeChallenge: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM codeChallengeMethod: S256 authDevice: summary: Device-flow completion value: type: authDevice accountName: jane@example.com accountSecret: s3cret code: BDWP-HQPK responses: '200': description: Result of the authentication attempt content: application/json: schema: $ref: '#/components/schemas/LoginResponse' examples: authenticated: value: type: authenticated clientCode: 3F7A9C1E4B2D8E6F mfaRequired: value: type: mfaRequired failure: value: type: failure '400': $ref: '#/components/responses/BadRequest' '429': $ref: '#/components/responses/TooManyRequests' /api/discover/{email}: get: operationId: discoverOidc summary: Discover the OpenID Connect provider for an email address description: | Returns the OpenID Connect discovery document for the directory that owns the domain part of `email`. If the domain is not bound to an external OIDC directory, the server's own OIDC discovery document (equivalent to `/.well-known/openid-configuration`) is returned. Anonymous endpoint, rate-limited. security: [] tags: [Authentication] parameters: - name: email in: path required: true description: Email address or account name schema: type: string format: email responses: '200': description: OpenID Connect discovery document content: application/json: schema: type: object description: OIDC discovery metadata (RFC 8414) additionalProperties: true '404': $ref: '#/components/responses/NotFound' '429': $ref: '#/components/responses/TooManyRequests' /api/account: get: operationId: getAccount summary: Return the authenticated account's permissions, edition and locale tags: [Account] responses: '200': description: Account descriptor content: application/json: schema: $ref: '#/components/schemas/Account' '401': $ref: '#/components/responses/Unauthorized' /api/schema: get: operationId: getSchemaRedirect summary: Redirect to the versioned configuration schema URL description: | Redirects (302) to `/api/schema/{hash}` where `{hash}` is the SHA-256 of the current configuration schema. Use this when you do not yet know the hash; once you have cached a schema at a given hash the immutable cache will never require re-download. tags: [Schema] responses: '302': description: Redirect to the hashed schema URL headers: Location: schema: type: string example: /api/schema/a1b2c3d4e5f6... '401': $ref: '#/components/responses/Unauthorized' /api/schema/{hash}: get: operationId: getSchema summary: Return the configuration schema at a specific hash description: | Returns the JSON Schema describing the full Stalwart configuration tree. The response is always gzip-encoded (`Content-Encoding: gzip`) and served with an immutable cache policy — the schema for a given hash never changes. If the hash does not match the server's current schema, the server redirects to the correct URL. tags: [Schema] parameters: - name: hash in: path required: true description: SHA-256 hex digest of the configuration schema schema: type: string responses: '200': description: Gzipped JSON Schema document headers: Content-Encoding: schema: type: string example: gzip Cache-Control: schema: type: string example: public, max-age=31536000, immutable content: application/json: schema: type: object description: JSON Schema document describing Stalwart config additionalProperties: true '302': description: Redirect to the current schema URL when the hash is stale '401': $ref: '#/components/responses/Unauthorized' /api/token/delivery: get: operationId: issueDeliveryToken summary: Issue a short-lived token for live delivery diagnostics description: | Returns a plain-text bearer token, valid for 60 seconds, that authorises connecting to `/api/live/delivery/{target}` as a query parameter (`?token=...`). Useful for EventSource clients that cannot send `Authorization` headers. Requires `LiveDeliveryTest` permission. tags: [Live Telemetry] responses: '200': description: Short-lived delivery token content: text/plain: schema: type: string example: eyJhbGciOi... '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/token/tracing: get: operationId: issueTracingToken summary: Issue a short-lived token for live tracing (Enterprise) description: | Returns a plain-text bearer token, valid for 60 seconds, that authorises connecting to `/api/live/tracing` as a query parameter. Requires the `LiveTracing` permission. Available only in the Enterprise edition. tags: [Live Telemetry] responses: '200': description: Short-lived tracing token content: text/plain: schema: type: string '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': description: Enterprise feature not available in this edition /api/token/metrics: get: operationId: issueMetricsToken summary: Issue a short-lived token for live metrics (Enterprise) description: | Returns a plain-text bearer token, valid for 60 seconds, that authorises connecting to `/api/live/metrics` as a query parameter. Requires the `LiveMetrics` permission. Available only in the Enterprise edition. tags: [Live Telemetry] responses: '200': description: Short-lived metrics token content: text/plain: schema: type: string '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': description: Enterprise feature not available in this edition /api/live/delivery/{target}: get: operationId: liveDelivery summary: Stream outbound-delivery diagnostics as Server-Sent Events description: | Opens a `text/event-stream` connection that streams each stage of an outbound delivery attempt to `target` (a domain or email address): MX lookup, MTA-STS fetch, TLSA/DANE validation, SMTP conversation, and so on. Each SSE frame carries a JSON-encoded `DeliveryStage` wrapped in a single-element array (`data: [{...}]`). The stream ends with a final `completed` event. Requires `LiveDeliveryTest` permission; may also be authenticated via the `?token=` query parameter obtained from `/api/token/delivery`. tags: [Live Telemetry] security: - bearerAuth: [] - basicAuth: [] - liveToken: [] parameters: - name: target in: path required: true description: Target domain or email address to diagnose schema: type: string - name: timeout in: query required: false description: Maximum stream lifetime in seconds (minimum 1, default 30) schema: type: integer minimum: 1 default: 30 responses: '200': description: Server-Sent Events stream of delivery-diagnose stages content: text/event-stream: schema: type: string description: | Series of SSE frames. Each `event: event` frame carries `data: []`; the final frame's stage is `{"type":"completed"}`. '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /api/live/tracing: get: operationId: liveTracing summary: Stream live tracing events (Enterprise) description: | Opens a `text/event-stream` connection streaming server trace events in real time. Requires `LiveTracing` permission; may be authenticated via the `?token=` query parameter from `/api/token/tracing`. Enterprise only. tags: [Live Telemetry] security: - bearerAuth: [] - basicAuth: [] - liveToken: [] responses: '200': description: Server-Sent Events stream of tracing events content: text/event-stream: schema: type: string '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': description: Enterprise feature not available in this edition /api/live/metrics: get: operationId: liveMetrics summary: Stream live metrics events (Enterprise) description: | Opens a `text/event-stream` connection streaming server metrics in real time. Requires `LiveMetrics` permission; may be authenticated via the `?token=` query parameter from `/api/token/metrics`. Enterprise only. tags: [Live Telemetry] security: - bearerAuth: [] - basicAuth: [] - liveToken: [] responses: '200': description: Server-Sent Events stream of metric events content: text/event-stream: schema: type: string '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': description: Enterprise feature not available in this edition components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT description: OAuth2 access token issued by `POST /auth/token`. basicAuth: type: http scheme: basic description: HTTP Basic authentication using account name and secret. liveToken: type: apiKey in: query name: token description: | Short-lived token (60s lifetime) issued by `/api/token/{kind}` and used to authorise Server-Sent Events streams where an `Authorization` header cannot be set (e.g. browser `EventSource`). responses: BadRequest: description: Request payload is malformed or fails validation content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' Unauthorized: description: Missing or invalid credentials headers: WWW-Authenticate: schema: type: string example: Bearer realm="Stalwart Server" content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' Forbidden: description: Authenticated principal lacks the required permission content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' NotFound: description: Resource not found content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' TooManyRequests: description: Anonymous-request rate limit exceeded content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' schemas: LoginRequest: type: object description: | Tagged union discriminated by `type`. Use `authCode` for the standard OAuth authorization-code flow (optionally with PKCE) and `authDevice` to complete an OAuth device-authorization flow. oneOf: - $ref: '#/components/schemas/LoginRequestAuthCode' - $ref: '#/components/schemas/LoginRequestAuthDevice' discriminator: propertyName: type mapping: authCode: '#/components/schemas/LoginRequestAuthCode' authDevice: '#/components/schemas/LoginRequestAuthDevice' LoginRequestAuthCode: type: object required: [type, accountName, accountSecret, clientId] properties: type: type: string enum: [authCode] accountName: type: string accountSecret: type: string format: password mfaToken: type: string nullable: true description: MFA token returned by a previous `mfaRequired` response clientId: type: string description: OAuth client identifier redirectUri: type: string format: uri nullable: true description: Must use `https://` unless the server is in recovery or dev mode nonce: type: string nullable: true scope: type: string nullable: true codeChallenge: type: string nullable: true description: PKCE code challenge (RFC 7636) codeChallengeMethod: type: string enum: [plain, S256] nullable: true description: Defaults to `plain` when a `codeChallenge` is present state: type: string nullable: true LoginRequestAuthDevice: type: object required: [type, accountName, accountSecret, code] properties: type: type: string enum: [authDevice] accountName: type: string accountSecret: type: string format: password mfaToken: type: string nullable: true code: type: string description: User-facing device code issued by `POST /auth/device` LoginResponse: type: object description: Tagged union discriminated by `type`. oneOf: - $ref: '#/components/schemas/LoginResponseAuthenticated' - $ref: '#/components/schemas/LoginResponseVerified' - $ref: '#/components/schemas/LoginResponseMfaRequired' - $ref: '#/components/schemas/LoginResponseFailure' discriminator: propertyName: type mapping: authenticated: '#/components/schemas/LoginResponseAuthenticated' verified: '#/components/schemas/LoginResponseVerified' mfaRequired: '#/components/schemas/LoginResponseMfaRequired' failure: '#/components/schemas/LoginResponseFailure' LoginResponseAuthenticated: type: object required: [type, clientCode] properties: type: type: string enum: [authenticated] clientCode: type: string description: Authorization code to exchange at `POST /auth/token` LoginResponseVerified: type: object required: [type] properties: type: type: string enum: [verified] LoginResponseMfaRequired: type: object required: [type] properties: type: type: string enum: [mfaRequired] LoginResponseFailure: type: object required: [type] properties: type: type: string enum: [failure] Account: type: object required: [permissions, edition, locale] properties: permissions: type: array description: | Effective permissions for the authenticated principal, filtered to exclude internal/system-only permissions. Values are from the `Permission` enum (e.g. `authenticate`, `jmap-email-get`, `sys-account-settings-get`). items: type: string edition: type: string enum: [oss, community, enterprise] description: Server edition locale: type: string description: Preferred locale for the account (IETF BCP 47-style tag) ProblemDetails: type: object description: RFC 7807 problem details document properties: type: type: string format: uri title: type: string status: type: integer detail: type: string instance: type: string