openapi: 3.1.0 info: description: >- mailcow is complete e-mailing solution with advanced antispam, antivirus, nice UI and API. In order to use this API you have to create a API key and add your IP address to the whitelist of allowed IPs this can be done by logging into the Mailcow UI using your admin account, then go to Configuration > Access > Edit administrator details > API. There you will find a collapsed API menu. There are two types of API keys - The read only key can only be used for all get endpoints - The read write key can be used for all endpoints You can create as many keys of both types as you need. Service keys defined in mailcow.conf (API_KEY, API_KEY_READ_ONLY, API_ALLOW_FROM) are shown in the UI, but can only be changed in mailcow.conf. **Requests** - `get` routes use `GET`, all other routes use `POST` (else `405`). - `add` routes take the new object as the JSON body. - `edit` routes take `{"items": [...], "attr": {...}}`: `items` names the objects to change, `attr` holds the values to set. - `delete` routes take the JSON array of the objects to delete. - Send `Content-Type: application/json`. A body that is no valid JSON is answered with `400`. **Answers** - Write routes answer with the list of messages of the call (`type` `success`, `danger`, `warning` or `info`; `msg` is a key of the mailcow language files, alone or with its values). A call without messages answers `{"type": "success", "msg": "Task completed"}` or `{"type": "error", "msg": "Cannot add item"}`. The HTTP status is 200 in both cases: check `type`. - Read routes answer with the object or list, `{}` or `[]` when there is nothing. - Unknown routes answer `404` with `{"type": "error", "msg": "route not found"}`. title: mailcow API version: "1.0.0" servers: - url: / components: securitySchemes: ApiKeyAuth: type: apiKey in: header name: X-API-Key schemas: Message: type: object description: >- One message of a write call, as the mailcow UI shows it. `msg` is a key of the mailcow language files, alone or as a list with the values of its placeholders. properties: type: type: string enum: - success - danger - warning - info log: type: array description: >- The call that produced the message (function, action, object, data), secrets replaced by `*` items: {} msg: oneOf: - type: string - type: array items: {} required: - type - msg Messages: type: array items: $ref: "#/components/schemas/Message" TaskResult: type: object description: Answer of a write call that produced no message properties: type: type: string enum: - success - error msg: type: string examples: - Task completed - Cannot add item required: - type - msg Error: type: object properties: type: type: string const: error msg: type: string required: - type - msg Domain: type: object description: >- A domain. Quotas and sizes are in bytes; the `*_int` fields repeat the value before them. properties: domain_name: type: string description: Domain name (IDN as punycode) domain_h_name: type: string description: Domain name in Unicode description: type: string description: Description max_new_mailbox_quota: type: integer description: >- Largest quota a new mailbox can get: the free domain quota, at most the max. mailbox quota def_new_mailbox_quota: type: integer description: Quota a new mailbox gets by default quota_used_in_domain: type: [string, integer] description: Sum of the quotas of the mailboxes bytes_total: type: [string, integer] description: Storage used by the mailboxes msgs_total: type: [string, integer] description: Number of messages in the mailboxes mboxes_in_domain: type: integer description: Number of mailboxes mboxes_left: type: integer description: Mailboxes that can still be added max_num_aliases_for_domain: type: integer description: Max. number of aliases max_num_mboxes_for_domain: type: integer description: Max. number of mailboxes def_quota_for_mbox: type: integer description: Default quota of new mailboxes max_quota_for_mbox: type: integer description: Max. quota per mailbox max_quota_for_domain: type: integer description: Quota of the whole domain relayhost: type: [string, integer] description: ID of the sender-dependent transport, "0" = none backupmx: type: integer enum: [0, 1] description: Relay domain (backup MX) backupmx_int: type: integer enum: [0, 1] gal: type: integer enum: [0, 1] description: Global address list in SOGo gal_int: type: integer enum: [0, 1] rl: description: Rate limit, `false` if none oneOf: - $ref: "#/components/schemas/DomainRatelimit" - type: boolean const: false active: type: integer enum: [0, 1] description: Domain active active_int: type: integer enum: [0, 1] relay_all_recipients: type: integer enum: [0, 1] description: Relay all recipients relay_all_recipients_int: type: integer enum: [0, 1] relay_unknown_only: type: integer enum: [0, 1] description: Relay non-existing mailboxes only relay_unknown_only_int: type: integer enum: [0, 1] created: type: string description: Creation time (`YYYY-MM-DD hh:mm:ss`) modified: type: [string, "null"] description: Time of the last change aliases_in_domain: type: [integer, string] description: Number of aliases, including those of its alias domains aliases_left: type: integer description: Aliases that can still be added domain_admins: type: string description: >- Comma-separated domain admins, `-` if none; only for admins (and API keys) tags: type: array items: type: string description: Tags; missing when the domain has none DomainRatelimit: type: object description: Rate limit of a domain or alias domain properties: value: type: string description: Messages per time frame frame: type: string enum: [s, m, h, d] description: Time frame (second, minute, hour, day) DomainTemplate: type: object properties: id: type: integer description: Template ID template: type: string description: Template name type: type: string const: domain attributes: $ref: "#/components/schemas/DomainTemplateAttributes" created: type: string description: Creation time modified: type: [string, "null"] description: Time of the last change DomainTemplateAttributes: type: object description: Values of a domain template as stored; quotas in bytes properties: tags: type: array items: type: string max_num_aliases_for_domain: type: integer max_num_mboxes_for_domain: type: integer def_quota_for_mbox: type: integer max_quota_for_mbox: type: integer max_quota_for_domain: type: integer rl_frame: type: string enum: [s, m, h, d] rl_value: type: [string, integer] description: Rate limit, "" = none active: type: integer enum: [0, 1] gal: type: integer enum: [0, 1] backupmx: type: integer enum: [0, 1] relay_all_recipients: type: integer enum: [0, 1] relay_unknown_only: type: integer enum: [0, 1] dkim_selector: type: string key_size: type: integer DomainTemplateInput: type: object description: >- Values of a domain template, quotas in MiB. The defaults apply when a template is added; for edit/domain/template see there. properties: template: type: string description: Template name, unique tags: type: array items: type: string default: [] description: Tags of new domains max_num_aliases_for_domain: type: [integer, string] default: 400 description: Max. number of aliases max_num_mboxes_for_domain: type: [integer, string] default: 10 description: Max. number of mailboxes def_quota_for_mbox: type: [integer, string] default: 3072 description: Default quota of new mailboxes in MiB max_quota_for_mbox: type: [integer, string] default: 10240 description: Max. quota per mailbox in MiB max_quota_for_domain: type: [integer, string] default: 10240 description: Quota of the whole domain in MiB rl_value: type: [integer, string] default: "" description: Rate limit, messages per `rl_frame`; empty = none rl_frame: type: string enum: [s, m, h, d] default: s description: Rate limit time frame active: type: [integer, string] enum: [0, 1, "0", "1"] default: 1 gal: type: [integer, string] enum: [0, 1, "0", "1"] default: 1 description: Global address list in SOGo backupmx: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: Relay domain (backup MX) relay_all_recipients: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 relay_unknown_only: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 dkim_selector: type: string default: dkim description: Selector of the DKIM key of new domains key_size: type: [integer, string] default: 2048 description: DKIM key size in bits of new domains AliasDomain: type: object properties: alias_domain: type: string description: Alias domain name target_domain: type: string description: Domain the alias domain points to parent_is_backupmx: type: integer enum: [0, 1] description: The target domain is a relay domain active: type: integer enum: [0, 1] description: Alias domain active active_int: type: integer enum: [0, 1] rl: description: Rate limit, `false` if none oneOf: - $ref: "#/components/schemas/DomainRatelimit" - type: boolean const: false created: type: string description: Creation time modified: type: [string, "null"] description: Time of the last change DomainPolicyEntry: type: object properties: object: type: string description: The domain or mailbox the entry belongs to value: type: string description: Sender address or pattern prefid: type: integer description: Entry ID (for delete/domain-policy) DkimKey: type: object properties: pubkey: type: string description: Public key (base64, without PEM lines) length: type: string enum: ["1024", "2048", "3072", "4096", ">= 8192"] description: Key size in bits, estimated from the public key dkim_txt: type: string description: >- Content of the DNS TXT record; split into quoted strings of 255 characters when `SPLIT_DKIM_255` is enabled dkim_selector: type: string description: Selector privkey: type: string description: >- Private key in PEM format, base64-encoded, when `SHOW_DKIM_PRIV_KEYS` is enabled, else empty DomainSearchRequest: type: object description: >- Server-side processing parameters of DataTables (https://datatables.net/manual/server-side). Without `start` and `length` all matching domains are answered. properties: draw: type: integer description: Request counter, answered as it is start: type: integer description: Offset of the first domain length: type: integer description: Number of domains, -1 = all search: type: object properties: value: type: string description: Searched in all columns with `searchable` true order: type: array items: type: object properties: column: type: integer description: Position in `columns` (see the description of the route) dir: type: string enum: [asc, desc] columns: type: array description: >- The columns of the table; the position gives the database column (see the description of the route) items: type: object properties: data: type: [string, integer] description: >- Column name of the UI; a column position makes `search.value` of this entry filter that column searchable: type: [boolean, string] description: Column takes part in the global search orderable: type: [boolean, string] description: Column may be sorted by search: type: object properties: value: type: string description: Filter of this column DomainSearchResult: type: object properties: draw: type: integer description: "`draw` of the request" recordsTotal: type: integer description: Number of domains the caller can see recordsFiltered: type: integer description: Number of domains that match the search data: type: array description: The domains of the page items: $ref: "#/components/schemas/Domain" MailboxItems: description: One mailbox address or a list of them oneOf: - type: string - type: array items: type: string UserAclName: type: string description: >- A user ACL: what the mailbox user may do in the UI (time-limited aliases, TLS policy, spam score, allow/deny lists, tagged mail, sync jobs, ActiveSync and SOGo reset, Pushover, quarantine actions, attachments, notification interval and category, app passwords, password reset) enum: - spam_alias - tls_policy - spam_score - spam_policy - delimiter_action - syncjobs - eas_reset - sogo_profile_reset - pushover - quarantine - quarantine_attachments - quarantine_notification - quarantine_category - app_passwds - pw_reset ProtocolAccessName: type: string enum: - imap - pop3 - smtp - sieve - eas - dav MailboxAttributes: type: object description: Settings of a mailbox; flags are "0" or "1" properties: force_pw_update: type: [string, integer] description: The user must change the password at the next login force_tfa: type: [string, integer] description: The user must set up two-factor authentication at the next login tls_enforce_in: type: [string, integer] tls_enforce_out: type: [string, integer] sogo_access: type: [string, integer] description: Login to SOGo from the UI imap_access: type: [string, integer] pop3_access: type: [string, integer] smtp_access: type: [string, integer] sieve_access: type: [string, integer] eas_access: type: [string, integer] dav_access: type: [string, integer] relayhost: type: [string, integer] description: ID of the sender-dependent transport, "0" for none shared: type: [string, integer] description: Shared mailbox without a login of its own passwd_update: type: [string, integer] description: Time of the last password change mailbox_format: type: string quarantine_notification: type: string enum: - never - hourly - daily - weekly quarantine_category: type: string enum: - reject - add_header - all attribute_hash: type: string description: Hash of the template the settings came from, cleared by manual changes recovery_email: type: string description: Address for password reset mails (present after it was set) template: type: string description: Name of the template the mailbox was created from (if any) Mailbox: type: object description: >- Details of a mailbox. `max_new_quota`, `spam_aliases`, `pushover_active`, `rl`, `rl_scope`, `is_relayed` and `sender_acl` are missing in the reduced form; `tags` is missing when the mailbox has no tags. properties: username: type: string description: Address of the mailbox active: type: integer description: 1 active, 0 inactive, 2 login disabled (mail is still accepted) active_int: type: integer description: Same as `active` domain: type: string name: type: string description: Full name local_part: type: string quota: type: integer description: Quota in bytes, 0 = unlimited messages: type: integer description: Number of messages attributes: $ref: "#/components/schemas/MailboxAttributes" custom_attributes: type: [object, array] description: Custom attributes (an empty list when there are none) additionalProperties: type: string quota_used: type: integer description: Used bytes percent_in_use: type: [integer, string] description: Used part of the quota in percent, "- " for unlimited quota created: type: string modified: type: [string, "null"] authsource: type: string enum: - mailcow - keycloak - generic-oidc - ldap percent_class: type: string enum: - info - success - warning - danger description: info (unlimited), success, warning (≥ 75 %), danger (≥ 90 %) last_imap_login: type: integer description: Unix time of the last IMAP login, 0 = none last_smtp_login: type: integer last_pop3_login: type: integer last_sso_login: type: integer last_sogo_login: type: integer max_new_quota: type: integer description: Largest quota in bytes the mailbox may get (domain limits) spam_aliases: type: integer description: Number of valid time-limited aliases pushover_active: type: integer enum: [0, 1] rl: description: Effective rate limit, `false` if there is none oneOf: - $ref: "#/components/schemas/MailboxRatelimit" - type: boolean const: false rl_scope: type: string enum: - mailbox - domain description: Whether `rl` is the mailbox's own limit or the domain limit is_relayed: type: integer description: 1 if the domain is relayed (backup MX) sender_acl: type: array description: Addresses and @domains the mailbox may send as (internal, set by `sender_acl`) items: type: string tags: type: array items: type: string MailboxAdd: type: object required: - local_part - domain properties: local_part: type: string description: Part before the @ domain: type: string description: Domain of the mailbox name: type: string description: Full name (`<` and `>` at the ends are removed) authsource: type: string enum: - mailcow - keycloak - generic-oidc - ldap default: mailcow description: >- Where the user logs in; an external source is accepted only if it is the configured identity provider, else mailcow is used password: type: string format: password description: >- Password (authsource mailcow), checked against the password policy; may be left empty together with `password2` for a shared mailbox password2: type: string format: password description: The password again template: type: [integer, string] description: ID of the mailbox template that provides the defaults (default the first template) quota: type: [integer, string] description: Quota in MiB, 0 = unlimited active: type: [integer, string] enum: [0, 1, 2, "0", "1", "2"] description: 1 active, 0 inactive, 2 login disabled (mail is accepted) tags: type: array items: type: string description: Tags (the first 26 are saved) protocol_access: type: array description: Allowed protocols; when sent it replaces the single `*_access` fields items: $ref: "#/components/schemas/ProtocolAccessName" imap_access: type: [integer, string] enum: [0, 1, "0", "1"] pop3_access: type: [integer, string] enum: [0, 1, "0", "1"] smtp_access: type: [integer, string] enum: [0, 1, "0", "1"] sieve_access: type: [integer, string] enum: [0, 1, "0", "1"] eas_access: type: [integer, string] enum: [0, 1, "0", "1"] dav_access: type: [integer, string] enum: [0, 1, "0", "1"] sogo_access: type: [integer, string] enum: [0, 1, "0", "1"] description: Login to SOGo from the UI force_pw_update: type: [integer, string] enum: [0, 1, "0", "1"] description: Password change at the first login (always 0 for external sources) force_tfa: type: [integer, string] enum: [0, 1, "0", "1"] description: Two-factor setup at the first login (always 0 for generic-oidc) tls_enforce_in: type: [integer, string] enum: [0, 1, "0", "1"] tls_enforce_out: type: [integer, string] enum: [0, 1, "0", "1"] relayhost: type: [integer, string] default: 0 description: ID of a sender-dependent transport, 0 = none shared: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: Shared mailbox without a login of its own; members of groups open it quarantine_notification: type: string enum: - never - hourly - daily - weekly quarantine_category: type: string enum: - reject - add_header - all tagged_mail_handler: type: string enum: - subfolder - subject - none description: Handling of mail to address+tag, see `edit/delimiter_action` acl: type: array description: User ACLs to grant; when sent, all others are off items: $ref: "#/components/schemas/UserAclName" rl_value: type: [integer, string] description: Rate limit, messages per `rl_frame` (0 or empty = none) rl_frame: type: string enum: - s - m - h - d attribute_hash: type: string description: Internal, the hash of the template the values come from MailboxEdit: type: object properties: active: type: [integer, string] enum: [0, 1, 2, "0", "1", "2"] description: 1 active, 0 inactive, 2 login disabled (mail is accepted) name: type: string description: Full name; empty keeps the current name quota: type: [integer, string] description: Quota in MiB, 0 = unlimited; empty keeps the current quota password: type: string format: password description: New password (authsource mailcow only), checked against the password policy password2: type: string format: password authsource: type: string enum: - mailcow - keycloak - generic-oidc - ldap description: An external source is accepted only if it is the configured identity provider pw_recovery_email: type: string description: Address for password reset mails (authsource mailcow only) force_pw_update: type: [integer, string] enum: [0, 1, "0", "1"] force_tfa: type: [integer, string] enum: [0, 1, "0", "1"] sogo_access: type: [integer, string] enum: [0, 1, "0", "1"] protocol_access: type: array description: Allowed protocols; when sent it replaces the single `*_access` fields items: $ref: "#/components/schemas/ProtocolAccessName" imap_access: type: [integer, string] enum: [0, 1, "0", "1"] pop3_access: type: [integer, string] enum: [0, 1, "0", "1"] smtp_access: type: [integer, string] enum: [0, 1, "0", "1"] sieve_access: type: [integer, string] enum: [0, 1, "0", "1"] eas_access: type: [integer, string] enum: [0, 1, "0", "1"] dav_access: type: [integer, string] enum: [0, 1, "0", "1"] relayhost: type: [integer, string] description: ID of a sender-dependent transport, 0 = none shared: type: [integer, string] enum: [0, 1, "0", "1"] tags: type: array items: type: string description: Tags to add (existing tags stay; remove them with `delete/mailbox/tag/{mailbox}`) tagged_mail_handler: type: string enum: - subfolder - subject - none sender_acl: description: >- Replaces the internal addresses and @domains the mailbox may send as; `*` (admins only) allows every address, `["default"]` removes all oneOf: - type: string - type: array items: type: string extended_sender_acl: type: string description: >- Replaces the external sender addresses or domains (not hosted on this server), separated by space, comma or semicolon attribute_hash: type: string description: Internal; cleared when not sent MailboxTemplateInput: type: object properties: quota: type: [integer, string] default: 0 description: Quota in MiB, 0 = unlimited tags: type: array items: type: string tagged_mail_handler: type: string enum: - subfolder - subject - none default: none quarantine_notification: type: string enum: - never - hourly - daily - weekly default: hourly quarantine_category: type: string enum: - reject - add_header - all default: reject rl_value: type: [integer, string] description: Rate limit, messages per `rl_frame` rl_frame: type: string enum: - s - m - h - d default: s force_pw_update: type: [integer, string] enum: [0, 1, "0", "1"] force_tfa: type: [integer, string] enum: [0, 1, "0", "1"] sogo_access: type: [integer, string] enum: [0, 1, "0", "1"] active: type: [integer, string] enum: [0, 1, 2, "0", "1", "2"] default: 1 tls_enforce_in: type: [integer, string] enum: [0, 1, "0", "1"] tls_enforce_out: type: [integer, string] enum: [0, 1, "0", "1"] protocol_access: type: array items: $ref: "#/components/schemas/ProtocolAccessName" description: Allowed protocols (default all) acl: type: array items: $ref: "#/components/schemas/UserAclName" description: User ACLs granted to new mailboxes MailboxTemplate: type: object properties: id: type: integer template: type: string description: Name type: type: string const: mailbox attributes: type: object description: >- The settings: quota in bytes, tags, tagged_mail_handler, quarantine_notification, quarantine_category, rl_value, rl_frame, the flags force_pw_update, force_tfa, sogo_access, active, tls_enforce_in, tls_enforce_out, imap_access ... dav_access and one acl_ flag per user ACL additionalProperties: true created: type: string modified: type: [string, "null"] Resource: type: object properties: name: type: string description: Address of the resource kind: type: string enum: - location - group - thing multiple_bookings: type: integer description: 0 always free, -1 unlimited but busy when booked, n ≥ 1 at most n bookings description: type: string active: type: integer active_int: type: integer domain: type: string local_part: type: string AppPassword: type: object properties: id: type: integer name: type: string mailbox: type: string domain: type: string password: type: string description: Hash of the app password created: type: string modified: type: [string, "null"] imap_access: type: integer smtp_access: type: integer dav_access: type: integer eas_access: type: integer pop3_access: type: integer sieve_access: type: integer active: type: integer SieveFilter: type: object properties: active: type: integer enum: [0, 1] id: type: integer username: type: string description: Mailbox filter_type: type: string enum: - prefilter - postfilter script_data: type: string description: The Sieve script script_desc: type: string TimeLimitedAlias: type: object properties: address: type: string goto: type: string description: Mailbox the alias delivers to description: type: string validity: type: integer description: Unix time of expiry (0 for permanent aliases set by an edit) created: type: string modified: type: [string, "null"] permanent: type: integer enum: [0, 1] SpamPolicyEntry: type: object properties: object: type: string description: Mailbox or domain the entry belongs to value: type: string description: Sender address, domain or pattern prefid: type: integer description: ID for `delete/mailbox-policy` MailboxRatelimit: type: object properties: value: type: string description: Number of messages frame: type: string enum: - s - m - h - d description: Per second, minute, hour or day SpamScore: type: object properties: score: type: string description: '"low,high": junk folder from low, rejected from high' LastLogin: type: object properties: ui: description: Previous successful UI login, `false` if there is none, `[]` for domain admins oneOf: - type: object properties: remote: type: string description: IP address time: type: integer description: Unix time - type: boolean const: false - type: array maxItems: 0 sasl: type: array items: type: object properties: real_rip: type: string description: Client IP; private and internal addresses as "Web/EAS/Internal (address)" datetime: type: string service: type: string description: e.g. imap, smtp, pop3, SOGO, SSO, MAILCOWUI app_password: type: integer description: ID of the app password used, 0 = none app_password_name: type: [string, "null"] location: type: string description: Country code of public addresses (if known) MailboxSearchRequest: type: object description: DataTables server-side request properties: draw: type: integer description: Counter, returned unchanged start: type: integer description: Offset of the first row; without it all rows are returned length: type: integer description: Rows per page, -1 = all search: type: object properties: value: type: string description: Searched in all searchable columns (substring) order: type: array items: type: object properties: column: type: integer description: Column number (index in `columns`) dir: type: string enum: - asc - desc columns: type: array description: Column n of the table is `columns[n]` items: type: object properties: data: type: [string, integer] description: >- Name of the column in the UI; for a filter by `search.value` of this column only a column number selects the column, other values filter the address searchable: type: [boolean, string] description: true or "true" includes the column in the search orderable: type: [boolean, string] description: true or "true" allows sorting by the column search: type: object properties: value: type: string description: Filter for this column only (substring) MailboxSearchResult: type: object properties: draw: type: integer recordsTotal: type: integer recordsFiltered: type: integer data: type: array items: $ref: "#/components/schemas/Mailbox" Alias: type: object properties: in_primary_domain: type: string description: Target domain if `domain` is an alias domain, else empty id: type: integer domain: type: string description: Domain of the address, a domain or alias domain public_comment: type: [string, "null"] description: Comment shown to the users private_comment: type: [string, "null"] description: Comment for administrators goto: type: string description: >- Comma-separated destination addresses, or `null@localhost` (discard), `spam@localhost` (learn as spam), `ham@localhost` (learn as ham) address: type: string description: Alias address, `@example.org` for a catch-all is_catch_all: type: integer description: 1 = catch-all of the domain internal: type: integer description: 1 = only reachable from its own domain and its alias domains active: type: integer description: 1 = active, 0 = inactive active_int: type: integer description: Same as `active` sogo_visible: type: integer description: 1 = selectable sender in SOGo sogo_visible_int: type: integer description: Same as `sogo_visible` sender_allowed: type: integer description: 1 = the destination mailboxes may send as this alias group_id: type: [integer, "null"] description: >- Mailbox group whose distribution address this is; such aliases can only be changed in the group group_name: type: [string, "null"] description: Name of that mailbox group created: type: string description: Creation time, `YYYY-MM-DD hh:mm:ss` modified: type: [string, "null"] description: Time of the last change BccMap: type: object properties: id: type: integer local_dest: type: string description: Domain as `@example.org` or the address of a mailbox or alias bcc_dest: type: string description: Address that gets the copies active: type: integer description: 1 = active, 0 = inactive type: type: string enum: - sender - rcpt description: "`sender`: mail sent by `local_dest`; `rcpt`: mail sent to it" created: type: string domain: type: string description: Domain of `local_dest` modified: type: [string, "null"] RecipientMap: type: object properties: id: type: integer recipient_map_old: type: string description: Original recipient, an address or `@domain` recipient_map_new: type: string description: New recipient, an address or `@domain` active: type: integer description: 1 = active, 0 = inactive created: type: string modified: type: [string, "null"] TlsPolicyMap: type: object properties: id: type: integer dest: type: string description: Destination policy: type: string enum: - none - may - encrypt - dane - dane-only - fingerprint - verify - secure parameters: type: [string, "null"] description: Space-separated `name=value` attributes, may be empty active: type: integer description: 1 = active, 0 = inactive created: type: string modified: type: [string, "null"] Relayhost: type: object description: A sender-dependent transport properties: id: type: integer description: The value domains and mailboxes use as `relayhost` hostname: type: string description: Next hop username: type: string description: SMTP user name, empty without authentication password: type: string description: SMTP password in clear text active: type: integer description: 1 = active, 0 = inactive password_short: type: string description: First three characters of the password and `...` used_by_domains: type: string description: Comma-separated domains that use the transport, may be empty used_by_mailboxes: type: string description: Comma-separated mailboxes that use the transport, may be empty Transport: type: object description: A transport map entry properties: id: type: integer is_mx_based: type: integer description: 1 = `destination` is a regular expression for MX names destination: type: string nexthop: type: string username: type: string description: SMTP user name, empty without authentication password: type: string description: SMTP password in clear text active: type: integer description: 1 = active, 0 = inactive password_short: type: string description: First three characters of the password and `...` ForwardingHost: type: object properties: host: type: string description: IP address or network source: type: string description: What was added (host name, domain or the address itself) keep_spam: type: string enum: - "yes" - "no" description: "`yes` = the mail of the host is not scanned for spam" RspamdSettingAttributes: type: object properties: desc: type: string description: Short description content: type: string description: Rule content in Rspamd settings syntax active: type: [boolean, integer, string] description: 1 = the rule is handed to Rspamd default: 0 required: - desc - content RspamdSetting: type: object properties: id: type: integer desc: type: string description: Short description content: type: string description: Rule content in Rspamd settings syntax active: type: integer enum: - 0 - 1 RspamdRegexMap: type: string description: File name of a global Rspamd regex map enum: - global_mime_from_blacklist.map - global_mime_from_whitelist.map - global_smtp_from_blacklist.map - global_smtp_from_whitelist.map - global_rcpt_blacklist.map - global_rcpt_whitelist.map - fishy_tlds.map - bad_words.map - bad_words_de.map - bad_languages.map - bulk_header.map - bad_header.map - monitoring_nolog.map Preset: type: object properties: name: type: string description: File name of the preset without `.yml` headline: type: string description: Title, translated content: type: string description: Rspamd settings rule or Sieve script GlobalFilters: type: object properties: prefilter: type: string description: Sieve script that runs before the filters of the mailboxes postfilter: type: string description: Sieve script that runs after the filters of the mailboxes MailQueueItem: type: object description: One message of `postqueue -j` properties: queue_name: type: string description: Queue, e.g. `active`, `deferred`, `hold`, `incoming` queue_id: type: string arrival_time: type: integer description: Unix time message_size: type: integer description: Size in bytes forced_expire: type: boolean sender: type: string recipients: type: array description: Recipient addresses, with the reason of the delay in brackets if there is one items: type: string QuarantineItem: type: object properties: id: type: integer qid: type: string description: Postfix queue ID of the message subject: type: string virus_flag: type: integer description: Greater than 0 when Rspamd found a virus (`VIRUS_FOUND`) score: type: number description: Rspamd score rcpt: type: string description: Mailbox the item belongs to sender: type: string action: type: string description: Rspamd action, e.g. `reject` or `add header` created: type: integer description: Unix time notified: type: integer description: 1 when the recipient got a notification QuarantineItemDetails: type: object properties: id: type: integer qid: type: string description: Postfix queue ID of the message subject: type: string score: type: number description: Rspamd score ip: type: string description: IP of the sending server action: type: string description: Rspamd action symbols: type: string description: Rspamd symbols as JSON text fuzzy_hashes: type: string description: Fuzzy hashes as JSON text sender: type: string rcpt: type: string description: Mailbox the item belongs to msg: type: string description: The raw message domain: type: [string, "null"] notified: type: integer description: 1 when the recipient got a notification created: type: string description: Time of quarantining (`YYYY-MM-DD HH:MM:SS`) user: type: string description: Authenticated sender, `unknown` if none qhash: type: string description: Secret of the release and delete links in the notification mails Fail2Ban: type: object properties: ban_time: type: integer description: Ban time in seconds ban_time_increment: type: boolean description: The ban time grows with every ban of a network max_ban_time: type: integer description: Maximum ban time in seconds netban_ipv4: type: integer description: IPv4 prefix length that is banned netban_ipv6: type: integer description: IPv6 prefix length that is banned max_attempts: type: integer description: Failed logins within `retry_window` that lead to a ban retry_window: type: integer description: Window in seconds for `max_attempts` banlist_id: type: string description: ID of the banlist URL `/f2b-banlist?id=` manage_external: type: integer description: 1 = netfilter sets no rules, the banlist is for an external firewall regex: type: object description: Regex filters by number additionalProperties: type: string whitelist: type: string description: Allowlisted networks and hosts, one per line blacklist: type: string description: Denylisted networks, one per line perm_bans: type: array description: Permanent bans (only present when there are any) items: type: object properties: network: type: string ip: type: string active_bans: type: array description: Active bans (only present when there are any) items: type: object properties: queued_for_unban: type: integer description: 1 when the ban is about to be lifted network: type: string ip: type: string banned_until: type: string description: Remaining ban time, e.g. `00h 27m 08s` Admin: type: object description: An administrator account properties: username: type: string tfa_active: type: integer enum: [0, 1] description: 1 = two-factor authentication is active tfa_active_int: type: integer enum: [0, 1] description: Same as `tfa_active` active: type: integer enum: [0, 1] description: 1 = the account can log in active_int: type: integer enum: [0, 1] description: Same as `active` created: type: string description: Creation time (`YYYY-MM-DD hh:mm:ss`) attributes: type: object properties: force_tfa: type: string enum: ["0", "1"] description: "\"1\" = two-factor authentication must be set up at the next login" force_pw_update: type: string enum: ["0", "1"] description: "\"1\" = the password must be changed at the next login" DomainAdmin: type: object description: A domain administrator account properties: username: type: string tfa_active: type: integer enum: [0, 1] description: 1 = two-factor authentication is active tfa_active_int: type: integer enum: [0, 1] description: Same as `tfa_active` active: type: integer enum: [0, 1] description: 1 = the account can log in active_int: type: integer enum: [0, 1] description: Same as `active` created: type: string description: Creation time (`YYYY-MM-DD hh:mm:ss`) attributes: type: object properties: force_tfa: type: string enum: ["0", "1"] description: "\"1\" = two-factor authentication must be set up at the next login" force_pw_update: type: string enum: ["0", "1"] description: "\"1\" = the password must be changed at the next login" selected_domains: type: array description: The domains the account administers items: type: string unselected_domains: description: All other domains, `""` if there are none oneOf: - type: array items: type: string - type: string const: "" DomainAdminAcl: type: string description: >- ACL of a domain administrator (default in brackets): `syncjobs` sync jobs (on), `quarantine` quarantine (on), `login_as` log in as mailbox user (on), `sogo_access` direct SOGo access of users (on), `app_passwds` app passwords (on), `bcc_maps` BCC maps (on), `pushover` Pushover (off), `filters` filters (on), `ratelimit` rate limits (on), `spam_policy` spam policy (on), `extend_sender_acl` sender ACL with external addresses (off), `unlimited_quota` unlimited mailbox quota (off), `protocol_access` protocol access of mailboxes (on), `smtp_ip_access` SMTP IP restrictions (on), `alias_domains` alias domains (off), `mailbox_relayhost` relay host of mailboxes (on), `domain_relayhost` relay host of domains (on), `domain_desc` domain description (off), `alias_external_goto` aliases with external destinations (on), `mailbox_groups` mailbox groups (on) enum: - syncjobs - quarantine - login_as - sogo_access - app_passwds - bcc_maps - pushover - filters - ratelimit - spam_policy - extend_sender_acl - unlimited_quota - protocol_access - smtp_ip_access - alias_domains - mailbox_relayhost - domain_relayhost - domain_desc - alias_external_goto - mailbox_groups ApiKey: type: object description: An API key properties: id: type: integer description: type: string description: Free text; `API_KEY` or `API_KEY_READ_ONLY` for the service keys access: type: string enum: - ro - rw description: "`ro`: read-only (get routes only), `rw`: read/write" api_key: type: string description: The key value, sent in the `X-API-Key` header allow_from: type: array description: IP addresses and networks the key may be used from items: type: string skip_ip_check: type: integer enum: [0, 1] description: 1 = the key may be used from any address service: type: integer enum: [0, 1] description: 1 = service key from mailcow.conf, can only be changed there active: type: integer enum: [0, 1] created: type: [string, "null"] modified: type: [string, "null"] ApiKeyAttributes: type: object description: Attributes of an API key for add and edit properties: description: type: string description: Free text, at most 255 characters default: "" access: type: string enum: - ro - rw description: "`ro`: read-only (get routes only), `rw`: read/write. Required for a new key." allow_from: description: >- IP addresses and CIDR networks the key may be used from: a list or a string separated by spaces, commas, semicolons or newlines. An edit replaces the list. oneOf: - type: string - type: array items: type: string skip_ip_check: type: [integer, string, boolean] description: 1/true = allow the key from any address default: 0 active: type: [integer, string, boolean] description: 1/true = active default: 1 OAuth2Client: type: object description: An OAuth2 client of the mailcow OAuth2 server properties: id: type: integer description: ID used by the API client_id: type: string description: OAuth2 client ID client_secret: type: string description: OAuth2 client secret redirect_uri: type: string description: Redirect (callback) URL grant_types: type: [string, "null"] description: Not used scope: type: string description: Always `profile` user_id: type: [string, "null"] description: Not used SyslogLogEntry: type: object description: A log line of a container properties: time: type: string description: Unix time program: type: string description: Process that wrote the line priority: type: string description: Syslog priority (`info`, `warning`, `err`, ...) message: type: string AcmeLogEntry: type: object properties: time: type: string description: Unix time message: type: string ApiLogEntry: type: object properties: time: type: integer description: Unix time uri: type: string description: Request URI method: type: string description: HTTP method remote: type: string description: Client address data: type: string description: Form data of the request (`attr`, `items`), secrets replaced by `*` AutodiscoverLogEntry: type: object properties: time: type: integer description: Unix time ua: type: string description: User agent of the client user: type: string description: Requested e-mail address ip: type: string description: Client address service: type: string description: Offered service (`activesync`, `imap`) or an error CronLogEntry: type: object properties: time: type: integer description: Unix time priority: type: string task: type: string description: Name of the task message: type: string NetfilterLogEntry: type: object properties: time: type: integer description: Unix time priority: type: string description: "`info`, `warn` or `crit` (bans)" message: type: string RatelimitLogEntry: type: object properties: time: type: integer description: Unix time rcpt: type: string description: Recipients, comma separated from: type: string description: Envelope sender user: type: string description: Authenticated user rl_info: type: string description: Rate limit that was hit, `name(hash)` rl_name: type: string description: Name of the rate limit (mailbox or domain) rl_hash: type: string description: Hash of the rate limit bucket (see `delete/rlhash`) qid: type: string description: Postfix queue ID ip: type: string description: Client address message_id: type: string header_subject: type: string header_from: type: string RspamdHistoryEntry: type: object description: A message scanned by Rspamd (fields of Rspamd's history) properties: unix_time: type: integer time_real: type: number description: Scan time in seconds score: type: number required_score: type: number action: type: string description: "`no action`, `greylist`, `add header`, `rewrite subject`, `soft reject` or `reject`" thresholds: type: object additionalProperties: type: number ip: type: string user: type: string description: Authenticated user, `unknown` for incoming mail sender_smtp: type: string sender_mime: type: string rcpt_smtp: type: array items: type: string rcpt_mime: type: array items: type: string subject: type: string message-id: type: string size: type: integer is_skipped: type: boolean symbols: type: object description: Matched symbols by name additionalProperties: type: object properties: name: type: string score: type: number metric_score: type: number description: type: string options: type: array items: type: string additionalProperties: true RspamdStats: type: object description: Statistics of Rspamd's controller (`/stat`) properties: version: type: string config_id: type: string uptime: type: integer description: Seconds read_only: type: boolean scanned: type: integer learned: type: integer actions: type: object description: Number of messages per action additionalProperties: type: integer scan_times: type: array items: type: [number, "null"] spam_count: type: integer ham_count: type: integer connections: type: integer control_connections: type: integer total_learns: type: integer statfiles: type: array description: Bayes statistics items: type: object fuzzy_hashes: type: object additionalProperties: type: integer additionalProperties: true SaslLogEntry: type: object properties: service: type: string description: "`imap`, `pop3`, `smtp`, `sieve`, `SSO` (SOGo), ..." app_password: type: integer description: ID of the app password used, 0 = the mailbox password username: type: string real_rip: type: string description: Client address datetime: type: string description: Time of the last login (`YYYY-MM-DD hh:mm:ss`) UiLogEntry: type: object properties: id: type: integer task: type: string description: ID of the request type: type: string description: "`success`, `danger`, `warning` or `info`" msg: type: string description: The message (JSON) call: type: string description: The call that produced it (JSON) user: type: string description: Account (`API` for API keys) role: type: string remote: type: string description: Client address time: type: integer description: Unix time WatchdogLogEntry: type: object properties: time: type: string description: Unix time service: type: string description: Checked service lvl: type: string description: Health in percent hpnow: type: string description: Current health points hptotal: type: string description: Maximum health points hpdiff: type: string description: Change since the last check ContainerStatus: type: object properties: type: type: string const: info container: type: string description: Service name state: type: string description: Docker state (`running`, `exited`, `restarting`, ...) started_at: type: string description: Start time (RFC 3339) image: type: string HostStats: type: object properties: cpu: type: object properties: cores: type: integer usage: type: number description: CPU usage in percent memory: type: object properties: total: type: integer description: Bytes usage: type: number description: Memory usage in percent swap: type: array description: Swap total, used, free (bytes), used percent, swapped in, swapped out (bytes) items: type: number uptime: type: number description: Seconds since boot system_time: type: string description: Host time (`DD.MM.YYYY hh:mm:ss`) architecture: type: string PasswordPolicy: type: object description: Password policy; the values are strings, `false` if never saved properties: length: type: [string, boolean] description: Minimum length chars: type: [string, boolean] description: "\"1\": at least one letter" special_chars: type: [string, boolean] description: "\"1\": at least one character that is no letter or digit" lowerupper: type: [string, boolean] description: "\"1\": lowercase and uppercase letters" numbers: type: [string, boolean] description: "\"1\": at least one digit" MailboxGroupAttributes: type: object description: >- Attributes of a mailbox group. Flags take `1`, `true`, `on` or `yes` for on, anything else is off. Lists take a JSON array or a string separated by spaces, commas, semicolons or newlines; entries are lowercased. properties: name: type: string description: 1-100 characters, unique in the domain description: type: string description: Max. 255 characters default: "" address: type: string description: >- Distribution address in the domain of the group (must not be used by a mailbox, alias or spam alias); empty = no address. Wins over `local_part`. local_part: type: string description: Local part of the address in the domain of the group; empty = no address members: type: - array - string items: type: string description: >- Mailboxes (regular mailboxes, any domain the caller manages); replaces the manual members the caller may access add_members: type: - array - string items: type: string description: Mailboxes to add remove_members: type: - array - string items: type: string description: Manual members to remove send_as: type: - array - string items: type: string description: >- Replaces the addresses the members may send as: existing mailbox or alias addresses (not the group address, see `sender_allowed`) or `@domain` for a domain or alias domain sender_allowed: type: - boolean - integer - string description: Members may send as the group address default: 0 sogo_visible: type: - boolean - integer - string description: Offer the group address as identity in SOGo (only with `sender_allowed`) default: 0 internal: type: - boolean - integer - string description: Only senders of the domain may write to the group address default: 0 active: type: - boolean - integer - string default: 1 imap_grants: description: >- Replaces the mailboxes the members may open (Dovecot ACL on every folder, shown as Shared/); the caller needs access to every owner. `subscribe` = a cron subscribes the folders for every member once (within 15 minutes). A JSON string of the list is accepted too. type: - array - string items: type: object required: - owner properties: owner: type: string description: Mailbox to share rights: type: string description: >- read = lookup, read, write-seen; write = also write, write-deleted, insert, post, expunge, create; full = also delete enum: - read - write - full default: read subscribe: type: - boolean - integer - string default: 0 sogo_grants: description: >- Replaces the SOGo folders shared with the members; the caller needs access to every owner. Pending until the owner opened SOGo once. `subscribe` = a cron subscribes every member once. A JSON string of the list is accepted too. type: - array - string items: type: object required: - owner properties: owner: type: string description: Mailbox whose folder is shared type: type: string enum: - Calendar - Contacts default: Calendar folder: type: string description: SOGo folder id (`A-Za-z0-9._-`, max. 100 characters) default: personal access: type: string enum: - read - write default: read subscribe: type: - boolean - integer - string default: 0 template_id: type: - integer - string - "null" description: >- Admins only: id of a mailbox template applied to the members; null, empty or 0 = none priority: type: - integer - string description: >- Admins only: -1000000 to 1000000; of several groups with a template the one with the highest priority wins default: 0 idp_ref: type: string description: >- Admins only: id of the Identity Provider group whose members the identity sync keeps in the group (max. 255 characters); empty = none MailboxGroup: type: object properties: id: type: integer domain: type: string name: type: string description: type: string address: type: - string - "null" description: Distribution address, null = none sender_allowed: type: integer enum: - 0 - 1 sogo_visible: type: integer enum: - 0 - 1 internal: type: integer enum: - 0 - 1 template_id: type: - integer - "null" template: type: - string - "null" description: Name of the template priority: type: integer idp_ref: type: - string - "null" description: Identity Provider group id, set = members are synced members: type: array items: type: object properties: username: type: string source: type: string description: manual = added in mailcow, idp = added by the identity sync enum: - manual - idp send_as: type: array description: Addresses and `@domain` entries the members may send as items: type: string imap_grants: type: array items: type: object properties: owner: type: string rights: type: string enum: - read - write - full subscribe: type: integer enum: - 0 - 1 state: type: string description: pending until Dovecot has the ACL, failed when setting it failed (retried by a cron) enum: - pending - applied - failed last_error: type: - string - "null" sogo_grants: type: array items: type: object properties: owner: type: string type: type: string enum: - Calendar - Contacts folder: type: string access: type: string enum: - read - write subscribe: type: integer enum: - 0 - 1 state: type: string description: pending (`last_error` folder_missing) until the SOGo folder of the owner exists enum: - pending - applied - failed last_error: type: - string - "null" active: type: integer enum: - 0 - 1 created: type: - string - "null" modified: type: - string - "null" can_edit: type: boolean description: The caller may change the group (false for read-only API keys) MailboxGroupMemberships: type: object properties: groups: type: array items: type: object properties: id: type: integer domain: type: string name: type: string address: type: - string - "null" sender_allowed: type: integer active: type: integer priority: type: integer template: type: - string - "null" source: type: string enum: - manual - idp template: type: - string - "null" description: Effective group template of the mailbox send_as: type: array description: Addresses the mailbox may send as through its active groups items: type: string SyncJobAttributes: type: object description: >- Attributes of a sync job. Flags are cast to integers: `1`, `"1"` and `true` are on, `0`, `"0"`, `false` (and the string `"true"`) are off; on add they default to off. The former connection attributes `host1`, `port1`, `enc1` and `authmech1` are ignored (see `source_id`). properties: source_id: type: - integer - string description: >- Id of an active sync source the caller may see (`GET /api/v1/get/syncjob_source/all`) user1: type: string description: Remote username at the source, max. 255 characters password1: type: string format: password description: >- Remote password; required unless the source uses XOAUTH2 (ignored then); empty on edit = keep mins_interval: type: - integer - string description: Minutes between runs, 1-43800 subfolder2: type: string description: Folder in the local mailbox to sync into, empty = none default: "" exclude: type: string description: Regular expression of remote folders to skip default: "" maxage: type: - integer - string description: Only sync messages up to this age in days (1-32000), 0 = all default: 0 maxbytespersecond: type: - integer - string description: >- Bandwidth limit in bytes/s (1-125000000), 0 = unlimited; the global limit of `edit/imapsync_settings` caps it default: 0 timeout1: type: - integer - string description: Timeout of the remote connection in seconds (1-32000) default: 600 timeout2: type: - integer - string description: Timeout of the local connection in seconds (1-32000) default: 600 custom_params: type: array description: >- Extra imapsync options, replaces the stored list; empty list, empty string or null = none. Each option must be allowed by `GET /api/v1/get/syncjob_options`: `true` needs a value, `false` must have none, a string is the regular expression the value must match. Max. 100 options, values max. 1000 characters, each value is passed as one argument. items: $ref: "#/components/schemas/SyncJobCustomParam" delete2duplicates: type: - integer - string - boolean description: Delete duplicates in the local mailbox (--delete2duplicates) delete1: type: - integer - string - boolean description: Delete messages at the source after they were synced (--delete1) delete2: type: - integer - string - boolean description: Delete local messages that are not at the source (--delete2) automap: type: - integer - string - boolean description: Map special folders like "Sent Items" to their local counterparts (--automap) skipcrossduplicates: type: - integer - string - boolean description: Skip duplicates across folders, first come first served (--skipcrossduplicates) subscribeall: type: - integer - string - boolean description: Subscribe all folders (--subscribeall) dry: type: - integer - string - boolean description: Dry run, change nothing (--dry) active: type: - integer - string - boolean description: The job runs; a new job without `active` is inactive default: 0 SyncJobCustomParam: type: object required: - o properties: o: type: string description: Option name, case-insensitive, leading dashes are removed example: folder v: type: string description: Value, empty for flags default: "" example: INBOX SyncJob: type: object description: A sync job; the remote password is never returned. properties: id: type: integer user2: type: string description: Local mailbox user1: type: string description: Remote username source_id: type: integer subfolder2: type: string exclude: type: string maxage: type: integer mins_interval: type: integer maxbytespersecond: type: string description: Bytes/s as text, "0" = unlimited timeout1: type: integer timeout2: type: integer custom_params: type: - string - "null" description: 'JSON text of the option list, e.g. `[{"o":"folder","v":"INBOX"}]`' custom_params_legacy: type: - string - "null" description: Free-text options of jobs from before the allowlist, not used by the runner delete2duplicates: type: integer delete1: type: integer delete2: type: integer automap: type: integer skipcrossduplicates: type: integer subscribeall: type: integer dry: type: integer active: type: integer regextrans2: type: - string - "null" description: Legacy column, read-only authmd51: type: integer description: Legacy column, read-only domain2: type: string description: Legacy column, read-only is_running: type: integer last_run: type: - string - "null" success: type: - integer - "null" description: Result of the last run (1 = imapsync exited with 0), null = not run yet exit_status: type: - string - "null" description: Exit status name of imapsync, e.g. EX_OK prio: type: integer description: Queue priority, higher runs first created: type: - string - "null" modified: type: - string - "null" source_name: type: - string - "null" source_description: type: - string - "null" source_host: type: - string - "null" source_port: type: - integer - "null" source_enc: type: - string - "null" source_auth_type: type: - string - "null" source_created_by: type: - string - "null" source_scope: type: - string - "null" source_oauth_flow: type: - string - "null" source_active: type: - integer - "null" source_oauth_token_expires: type: - integer - "null" description: Expiry (Unix time) of the token of a client_credentials source source_oauth_last_refresh_error: type: - string - "null" user_token_expires: type: - integer - "null" description: Expiry (Unix time) of the sign-in token of an authorization_code source user_token_error: type: - string - "null" user_token_exists: type: integer description: 1 = a sign-in token exists for (source, local mailbox, user1) log: type: string description: Output of the last run; empty with `no_log` SyncJobSourceAttributes: type: object description: >- Attributes of a sync source; on edit, attributes that are not sent keep their value. properties: name: type: string description: 1-100 characters, unique per creator description: type: string description: Max. 255 characters default: "" host: type: string description: IMAP server, host name or IP address (`host1` is accepted too) port: type: - integer - string description: 1-65535 (`port1` is accepted too) enc: type: string description: TLS = STARTTLS (`enc1` is accepted too) enum: - TLS - SSL - PLAIN default: TLS verify_cert: type: - integer - string - boolean description: Verify the server certificate; `0`, `"0"`, `false` or empty = no default: 1 auth_type: type: string description: >- PLAIN, LOGIN and CRAM-MD5 use the password of each job, XOAUTH2 an OAuth2 token. Users cannot create or edit XOAUTH2 sources, but may use the ones they see. enum: - PLAIN - LOGIN - CRAM-MD5 - XOAUTH2 default: PLAIN active: type: - integer - string description: 1 = usable for jobs (cast to integer) default: 1 scope: type: string description: >- Who may use the source: `all` (admins only), `domain` (the `domains`), `user` (the `users`; none = only the creator). Sources of users and client_credentials sources are always `user` without targets. On edit the targets only change when `scope` is sent. enum: - all - domain - user default: user domains: type: array description: Domains for scope `domain` (at least one), the caller must manage them items: type: string users: type: array description: Mailboxes for scope `user`, the caller must have access to them items: type: string oauth_flow: type: string description: >- XOAUTH2 grant: client_credentials = one token of the source for all mailboxes of the tenant; authorization_code = every mailbox owner signs in once in the mailcow UI (redirect URI `https:///syncjob-oauth`) enum: - client_credentials - authorization_code default: client_credentials oauth_token_endpoint: type: string description: XOAUTH2, required; https:// URL oauth_authorize_endpoint: type: string description: XOAUTH2 authorization_code, required; https:// URL oauth_userinfo_endpoint: type: string description: >- XOAUTH2 authorization_code, optional: identity endpoint used when the token response has no usable id_token; https:// URL oauth_client_id: type: string description: XOAUTH2, required, max. 500 characters oauth_client_secret: type: string format: password description: >- XOAUTH2, required, max. 500 characters; empty on edit = keep. Never returned. oauth_scope: type: string description: XOAUTH2, required, max. 500 characters oauth_extra_params: description: >- XOAUTH2, optional: extra OAuth2 parameters as JSON object with scalar values, or as JSON text (max. 4000 characters); empty = none. `prompt`, `access_type`, `login_hint`, `domain_hint` and `hd` go to the sign-in URL, all others to the token request; they never replace client id, secret, scope or grant fields. type: - object - string - "null" additionalProperties: type: - string - number - boolean - "null" SyncJobSource: type: object description: A sync source; secrets are never returned. properties: id: type: integer name: type: string description: type: string created_by: type: string description: Creator; `API` for API keys, empty = handed over to the admins (creator deleted) scope: type: string enum: - all - domain - user domains: type: array items: type: string users: type: array items: type: string host: type: string port: type: integer enc: type: - string - "null" host1: type: string description: Same as `host` port1: type: integer description: Same as `port` enc1: type: - string - "null" description: Same as `enc` verify_cert: type: integer auth_type: type: - string - "null" active: type: integer oauth_flow: type: string oauth_token_endpoint: type: - string - "null" oauth_authorize_endpoint: type: - string - "null" oauth_userinfo_endpoint: type: - string - "null" oauth_client_id: type: - string - "null" oauth_scope: type: - string - "null" oauth_extra_params: type: - string - "null" description: JSON text, null for callers who may not edit the source oauth_token_expires: type: - integer - "null" description: Expiry (Unix time) of the token of a client_credentials source oauth_last_refresh_error: type: - string - "null" oauth_last_refresh_attempt: type: - string - "null" oauth_redirect_uri: type: string description: Only for authorization_code sources, register it at the provider has_client_secret: type: boolean has_access_token: type: boolean can_edit: type: boolean description: The caller may change the source (false for read-only API keys) created: type: - string - "null" modified: type: - string - "null" SyncJobSettings: type: object properties: max_parallel: type: integer description: Sync jobs running at the same time max_bytes_per_second: type: integer description: Bandwidth limit per sync job process in bytes/s, 0 = unlimited max_kb_per_second: type: integer description: The same in KB/s IdentityProviderAttributes: type: object required: - authsource description: >- Configuration of the Identity Provider. Flags marked (boolean) are on for any non-empty value except `0`/`"0"`/`false`; flags marked (0/1) are cast to integers (the string `"true"` is 0). properties: authsource: type: string description: Provider type (case-insensitive) enum: - keycloak - generic-oidc - ldap client_id: type: string description: Keycloak, generic-oidc client_secret: type: string format: password description: Keycloak, generic-oidc redirect_url: type: string description: Keycloak, generic-oidc; URL of mailcow the provider redirects to after the login redirect_url_extra: type: - array - string items: type: string description: Keycloak, generic-oidc; more allowed redirect URLs, only changed when sent server_url: type: string description: Keycloak base URL realm: type: string description: Keycloak realm version: type: string description: Keycloak version mailpassword_flow: type: - integer - string - boolean description: (0/1) Keycloak; check mail client passwords through the Keycloak admin API default: 0 group_paths: type: string description: >- Keycloak group sync: comma-separated group paths, e.g. `/Sales` or `/mailcow/*` (all groups below /mailcow); needs the `query-groups` permission default: "" authorize_url: type: string description: generic-oidc authorization endpoint token_url: type: string description: generic-oidc token endpoint userinfo_url: type: string description: generic-oidc userinfo endpoint client_scopes: type: string description: generic-oidc scopes default: openid profile email mailcow_template host: type: string description: LDAP server, several separated by commas (spaces are removed) default: "" port: type: - integer - string description: LDAP port default: 389 use_ssl: type: - boolean - integer - string description: (boolean) LDAP over SSL default: false use_tls: type: - boolean - integer - string description: (boolean) LDAP StartTLS, ignored with `use_ssl` default: false basedn: type: string description: LDAP base DN of the user search binddn: type: string description: LDAP bind DN bindpass: type: string format: password description: LDAP bind password username_field: type: string description: LDAP attribute with the mail address of the user default: mail filter: type: string description: LDAP filter of the users that may log in default: "" attribute_field: type: string description: LDAP attribute whose value is matched against `mappers` default: "" id_attribute: type: string description: >- LDAP attribute with a stable user id; empty = objectGUID, entryUUID, nsUniqueId or ipaUniqueID, else the DN default: "" disabled_filter: type: string description: >- LDAP filter of disabled users (Active Directory accounts with ACCOUNTDISABLE are always disabled) default: "" group_basedn: type: string description: LDAP base DN of the group search, empty = `basedn` default: "" group_filter: type: string description: LDAP filter of the synced groups, required for the group sync default: "" group_name_attribute: type: string default: cn group_member_mode: type: string description: >- LDAP source of the memberships: memberOf of the users, member attribute of the groups, or Active Directory incl. nested groups enum: - memberof - member - ad_chain default: memberof group_member_attribute: type: string description: LDAP member attribute for `group_member_mode` member default: member ignore_ssl_error: type: - boolean - integer - string description: (boolean) Do not verify TLS certificates default: false login_provisioning: type: - boolean - integer - string description: (boolean) Create mailboxes at their first login default: false periodic_sync: type: - integer - string - boolean description: (0/1) Keycloak, LDAP; update mailboxes from the provider every `sync_interval` default: 0 import_users: type: - integer - string - boolean description: (0/1) Keycloak, LDAP; create mailboxes for new users of the provider default: 0 sync_interval: type: - integer - string description: Keycloak, LDAP; minutes between sync runs (at least 1) default: 15 offboarding: type: - integer - string - boolean description: >- (0/1) Keycloak, LDAP; users disabled at the provider, or missing in two complete sync runs, get login disabled (mailbox active = 2, mail is still received); only mailboxes disabled by the sync are enabled again default: 0 offboarding_limit: type: - integer - string description: >- Keycloak, LDAP; runs that would disable more mailboxes or remove more group memberships than this share in percent (0-100, at least 5 mailboxes) are blocked default: 10 group_sync: type: - integer - string - boolean description: >- (0/1) Keycloak, LDAP; sync the members of provider groups to mailbox groups (members added in mailcow stay) default: 0 group_autocreate: type: - integer - string - boolean description: >- (0/1) Keycloak, LDAP; create a mailbox group for every provider group without one default: 0 group_default_domain: type: string description: Keycloak, LDAP; domain of new groups whose mail is not in a mailcow domain default: "" default_template: type: string description: >- Mailbox template for new mailboxes without a matching mapping (not used for updates); only changed when sent mappers: type: array items: type: string description: >- Attribute values, each mapped to the template at the same position in `templates`; only changed when both lists are sent, pairs with an empty value are dropped templates: type: array items: type: string description: Mailbox templates for `mappers` IdentityProviderSettings: type: object description: >- Stored settings (see `IdentityProviderAttributes`). Values are strings as stored, except `redirect_url_extra`, `mappers` and `templates` (lists) and `use_ssl`, `use_tls`, `login_provisioning`, `ignore_ssl_error` (booleans; `login_provisioning` is `1` when never stored). Secrets are `*` for read-only API keys. properties: authsource: type: string client_secret: type: string bindpass: type: string access_token: type: string description: Cached admin API token of Keycloak redirect_url_extra: type: array items: type: string mappers: type: array items: type: string templates: type: array items: type: string use_ssl: type: boolean use_tls: type: boolean ignore_ssl_error: type: boolean login_provisioning: type: - boolean - integer additionalProperties: type: string IdentitySyncStatus: type: object properties: provider: type: string description: Configured provider, empty = none can_sync: type: boolean description: The provider can be synced (Keycloak, LDAP) sync_enabled: type: boolean description: Scheduled runs are on (periodic sync, import, offboarding or group sync) counts: type: object description: Mailboxes of the provider (no resources) properties: accounts: type: integer linked: type: integer description: Linked to a user of the provider idp_disabled: type: integer description: Login disabled by the sync missing: type: integer description: Not found at the provider by the last runs runs: type: array description: The last 10 runs, newest first items: $ref: "#/components/schemas/IdentitySyncRun" IdentitySyncRun: type: object properties: id: type: integer provider: type: string mode: type: string enum: - run - preview - force status: type: string enum: - pending - running - ok - blocked - error requested_by: type: string description: Who requested the run, empty for scheduled runs created: type: string started: type: - string - "null" finished: type: - string - "null" summary: type: - object - "null" description: >- Result, null while pending: `counts` per kind of change, `details` (up to 50 entries per list), `blocked`, `errors` (up to 50), `mode`; failed runs only `error` and `mode` properties: counts: type: object additionalProperties: type: integer details: type: object additionalProperties: type: array items: type: string blocked: type: boolean errors: type: array items: type: string error: type: string mode: type: string IdentityLink: type: object properties: provider: type: string external_id: type: - string - "null" description: User id at the provider idp_disabled: type: integer description: 1 = login disabled by the identity sync missing_since: type: - string - "null" description: Since when the sync does not find the user at the provider responses: Unauthorized: description: >- The API key is unknown or inactive (`authentication failed`), or the request comes from an address the key does not allow (`api access denied for ip
`) content: application/json: schema: $ref: "#/components/schemas/Error" example: type: error msg: authentication failed Forbidden: description: The API key is read-only content: application/json: schema: $ref: "#/components/schemas/Error" example: type: error msg: API read/write access denied WriteResult: description: >- The messages of the call; `{"type": "success", "msg": "Task completed"}` or `{"type": "error", "msg": "Cannot ... item"}` if it produced none. Check `type`: the status is 200 for failures too. content: application/json: schema: oneOf: - $ref: "#/components/schemas/Messages" - $ref: "#/components/schemas/TaskResult" examples: messages: summary: Messages of the call value: - type: success log: - mailbox - add - alias - address: alias@example.org goto: user@example.org msg: - alias_added - alias@example.org failed: summary: Failed without a message value: type: error msg: Cannot add item AcmeLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/AcmeLogEntry" example: - time: "1794308452" message: mail.example.org - Certificates were successfully validated, no changes or renewals required, sleeping for another day. ApiLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/ApiLogEntry" example: - time: 1794334338 uri: /api/v1/edit/domain-admin method: POST remote: 192.0.2.10 data: "attr='{\"active\":\"1\",\"password\":\"*\",\"password2\":\"*\"}', items='[\"domainadmin1\"]'" - time: 1794334330 uri: /api/v1/get/status/version method: GET remote: 192.0.2.10 data: "" AutodiscoverLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/AutodiscoverLogEntry" example: - time: 1794311051 ua: Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.17928; Pro) user: john.doe@example.org ip: 198.51.100.7 service: activesync CronLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/CronLogEntry" example: - time: 1794096901 priority: warning task: Group Shares message: "group_imap_grant_failed: info@example.org" DovecotLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/SyslogLogEntry" example: - time: "1794334332" program: dovecot priority: info message: "imap-login: Login: user=, method=PLAIN, rip=198.51.100.7, lip=172.22.1.250, mpid=310, TLS, session=" NetfilterLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/NetfilterLogEntry" example: - time: 1794334242 priority: crit message: Banning 203.0.113.5/32 for 30 minutes - time: 1794334242 priority: warn message: "203.0.113.5 matched rule id 3 (warning: unknown[203.0.113.5]: SASL LOGIN authentication failed: ...)" PostfixLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/SyslogLogEntry" example: - time: "1794334301" program: postfix/smtpd priority: info message: "connect from mail.example.com[198.51.100.20]" RatelimitLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/RatelimitLogEntry" example: - time: 1794334001 rcpt: jane@example.com from: john.doe@example.org user: john.doe@example.org rl_info: john.doe@example.org(RLsdz3tuabozgd4oacbdh8kc78) rl_name: john.doe@example.org rl_hash: RLsdz3tuabozgd4oacbdh8kc78 qid: E3CF91500458 ip: 172.22.1.248 message_id: 6a-5d892500-7-240abd80@example.org header_subject: Newsletter header_from: "\"John Doe\" " RspamdHistory: description: The scanned messages, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/RspamdHistoryEntry" example: - unix_time: 1794333685 time_real: 0.510136 score: -1.229459 required_score: 15 action: no action thresholds: reject: 15 add header: 8 greylist: 7 ip: 198.51.100.20 user: unknown sender_smtp: jane@example.com sender_mime: jane@example.com rcpt_smtp: - john.doe@example.org rcpt_mime: - john.doe@example.org subject: Meeting tomorrow message-id: 20261110190100.428319@mail.example.com size: 1015 is_skipped: false symbols: DMARC_POLICY_ALLOW: name: DMARC_POLICY_ALLOW score: -0.5 metric_score: -0.5 description: DMARC permit policy options: - example.com - none SaslLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/SaslLogEntry" example: - service: imap app_password: 0 username: john.doe@example.org real_rip: 198.51.100.7 datetime: "2026-11-10 19:07:01" SogoLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/SyslogLogEntry" example: - time: "1794334303" program: sogod priority: notice message: "[52]: 198.51.100.7 \"GET /SOGo/so/john.doe@example.org/Mail/view HTTP/1.1\" 200 2578/0 0.002 - - 0 - 12" UiLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/UiLogEntry" example: - id: 1819 task: 97D254 type: success msg: "[\"domain_admin_modified\",\"domainadmin1\"]" call: "[\"domain_admin\",\"edit\",{\"username\":[\"domainadmin1\"],\"active\":\"1\"}]" user: API role: admin remote: 192.0.2.10 time: 1794319670 WatchdogLogs: description: The log entries, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/WatchdogLogEntry" example: - time: "1794334334" service: Dovecot lvl: "100" hpnow: "12" hptotal: "12" hpdiff: "0" parameters: MailboxTagsQuery: name: tags in: query required: false description: >- Comma-separated tags: only mailboxes with a tag that contains one of them (substring match) schema: type: string example: sales,support examples: MailboxDetails: summary: A mailbox value: username: john.doe@example.org active: 1 active_int: 1 domain: example.org name: John Doe local_part: john.doe quota: 3221225472 messages: 1207 attributes: force_pw_update: "0" force_tfa: "0" tls_enforce_in: "0" tls_enforce_out: "0" sogo_access: "1" imap_access: "1" pop3_access: "0" smtp_access: "1" sieve_access: "1" eas_access: "1" dav_access: "1" relayhost: "0" shared: "0" passwd_update: "2026-11-03 09:37:37" mailbox_format: "maildir:" quarantine_notification: hourly quarantine_category: reject attribute_hash: "" recovery_email: "" custom_attributes: department: Sales quota_used: 418219315 percent_in_use: 13 created: "2026-11-03 09:37:37" modified: "2026-11-06 20:20:46" authsource: mailcow percent_class: success last_imap_login: 1794052021 last_smtp_login: 1793788402 last_pop3_login: 0 last_sso_login: 1794005152 last_sogo_login: 1794005160 max_new_quota: 10737418240 spam_aliases: 1 pushover_active: 0 rl: value: "100" frame: h rl_scope: mailbox is_relayed: 0 sender_acl: - sales@example.org tags: - sales MailboxList: summary: Mailboxes value: - username: john.doe@example.org active: 1 active_int: 1 domain: example.org name: John Doe local_part: john.doe quota: 3221225472 messages: 1207 attributes: force_pw_update: "0" force_tfa: "0" tls_enforce_in: "0" tls_enforce_out: "0" sogo_access: "1" imap_access: "1" pop3_access: "0" smtp_access: "1" sieve_access: "1" eas_access: "1" dav_access: "1" relayhost: "0" shared: "0" passwd_update: "2026-11-03 09:37:37" mailbox_format: "maildir:" quarantine_notification: hourly quarantine_category: reject attribute_hash: "" custom_attributes: [] quota_used: 418219315 percent_in_use: 13 created: "2026-11-03 09:37:37" modified: "2026-11-06 20:20:46" authsource: mailcow percent_class: success last_imap_login: 1794052021 last_smtp_login: 1793788402 last_pop3_login: 0 last_sso_login: 0 last_sogo_login: 1794005160 max_new_quota: 10737418240 spam_aliases: 0 pushover_active: 0 rl: false rl_scope: domain is_relayed: 0 sender_acl: [] MailboxSearchPage: summary: One page of mailboxes value: draw: 1 recordsTotal: 1 recordsFiltered: 1 data: - username: john.doe@example.org active: 1 active_int: 1 domain: example.org name: John Doe local_part: john.doe quota: 3221225472 messages: 1207 attributes: force_pw_update: "0" force_tfa: "0" tls_enforce_in: "0" tls_enforce_out: "0" sogo_access: "1" imap_access: "1" pop3_access: "0" smtp_access: "1" sieve_access: "1" eas_access: "1" dav_access: "1" relayhost: "0" shared: "0" passwd_update: "2026-11-03 09:37:37" mailbox_format: "maildir:" quarantine_notification: hourly quarantine_category: reject attribute_hash: "" custom_attributes: [] quota_used: 418219315 percent_in_use: 13 created: "2026-11-03 09:37:37" modified: "2026-11-06 20:20:46" authsource: mailcow percent_class: success last_imap_login: 1794052021 last_smtp_login: 1793788402 last_pop3_login: 0 last_sso_login: 0 last_sogo_login: 1794005160 max_new_quota: 10737418240 spam_aliases: 0 pushover_active: 0 rl: false rl_scope: domain is_relayed: 0 sender_acl: [] tags: - sales security: - ApiKeyAuth: [] paths: /api/v1/add/alias: post: tags: - Aliases summary: Create aliases operationId: addAlias description: >- Creates an alias for every address in `address`, all with the same destinations. Administrators and API keys may use every domain, domain administrators their own domains. Every address is checked on its own and gets its own message: `alias_added` (address, new ID) on success, else `is_alias_or_mailbox`, `domain_not_found`, `is_spam_alias`, `alias_invalid` or `access_denied`. An address that is also one of the destinations is skipped without message. `max_alias_exceeded` (the domain has no aliases left) stops the call at that address. Nothing is created with `comment_too_long`, `alias_empty`, `goto_empty` or when no destination is valid (`goto_invalid`: no e-mail address or a resource; `external_goto_denied`: not a mailcow domain while external destinations are disabled by `ALIAS_DISABLE_EXTERNAL_DOMAINS` or by the ACL of the domain administrator). Flags that are left out are 0: send `active`, `sogo_visible` and `sender_allowed` as 1 to get an alias like the UI creates it. requestBody: content: application/json: schema: type: object properties: address: type: string description: >- Alias address, `@example.org` for a catch-all of the domain. Several addresses can be separated by spaces, commas, semicolons or line breaks. The domain must be a mailcow domain or alias domain (IDN domains are stored in punycode). goto: type: string description: >- Destination addresses, separated by spaces, commas, semicolons or line breaks. Required unless `goto_null`, `goto_spam` or `goto_ham` is 1; ignored then. goto_null: type: [boolean, integer, string] description: >- 1 = discard the mail silently (destination `null@localhost`). Wins over `goto_spam` and `goto_ham`. default: 0 goto_spam: type: [boolean, integer, string] description: >- 1 = learn the mail as spam (destination `spam@localhost`). Wins over `goto_ham`. default: 0 goto_ham: type: [boolean, integer, string] description: 1 = learn the mail as ham (destination `ham@localhost`) default: 0 active: type: [boolean, integer, string] description: 1 = active, 0 = inactive default: 0 sogo_visible: type: [boolean, integer, string] description: >- 1 = the alias is a selectable sender in SOGo (aliases that point to at least one local mailbox) default: 0 internal: type: [boolean, integer, string] description: >- 1 = internal alias: only reachable from its own domain and the alias domains of it default: 0 sender_allowed: type: [boolean, integer, string] description: 1 = the destination mailboxes may send as this alias default: 0 public_comment: type: string description: Comment shown to the users, at most 160 characters maxLength: 160 private_comment: type: string description: Comment for administrators, at most 160 characters maxLength: 160 required: - address example: address: sales@example.org goto: anna@example.org,ben@example.org active: "1" sogo_visible: "1" sender_allowed: "1" internal: "0" public_comment: Sales team responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/time_limited_alias: post: tags: - Mailbox user settings summary: Create a time-limited alias operationId: addTimeLimitedAlias description: >- Creates a random alias (e.g. `kahd.osuwe@example.org`) that delivers to the mailbox until it expires. Needs the user ACL `spam_alias`. API keys must send `username` (without it the session user "API" is used and the call fails with `domain_invalid`); a mailbox user may leave it out (own mailbox) and gets `access_denied` for other mailboxes. `domain` must be the domain of the mailbox or one of its alias domains (`domain_invalid`). Errors: `validity_missing` (validity out of range). Success: `mailbox_modified`. requestBody: required: true content: application/json: schema: type: object required: - domain properties: username: type: string description: Mailbox the alias delivers to (required for API keys) domain: type: string description: Domain of the alias description: type: string default: "" description: Note for the alias validity: type: [integer, string] minimum: 1 maximum: 87600 default: 8760 description: Hours until the alias expires permanent: type: [boolean, integer, string] default: false description: true keeps the alias after it expired example: username: john.doe@example.org domain: example.org description: Online shop registration validity: 720 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/api-key: post: summary: Create API key description: >- Creates an API key with a random value. The answer does not contain the value: read it with `get/api-key/all`. Without `skip_ip_check` the key needs at least one IP address or network in `allow_from`. Success: `api_key_added` (with the description, else the ID). Errors: `api_key_access_invalid`, `api_key_description_invalid`, `api_key_network_invalid`, `ip_list_empty`. operationId: addApiKey tags: - API Keys requestBody: required: true content: application/json: schema: allOf: - $ref: "#/components/schemas/ApiKeyAttributes" - required: - access example: description: provisioning access: rw allow_from: "192.0.2.10, 2001:db8::/64" skip_ip_check: 0 active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/app-passwd: post: tags: - App Passwords summary: Create an app password operationId: addAppPassword description: >- Adds an app password to a mailbox: a separate password for a client, valid only for the protocols listed. Needs the user ACL `app_passwds`. API keys must send `username` (without it the session user "API" is used and the call fails with `access_denied`); a mailbox user may leave it out (own mailbox). Errors: `access_denied`, `password_complexity`, `password_mismatch`, `app_name_empty`. Success: `app_passwd_added`. requestBody: required: true content: application/json: schema: type: object required: - app_name - app_passwd - app_passwd2 properties: username: type: string description: Mailbox the app password belongs to (required for API keys) app_name: type: string description: Name of the app password app_passwd: type: string format: password description: The password, checked against the password policy app_passwd2: type: string format: password description: The password again protocols: description: Protocols the password is valid for; not listed = not allowed type: array items: type: string enum: - imap_access - smtp_access - pop3_access - sieve_access - dav_access - eas_access active: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: 1 active, 0 inactive example: username: john.doe@example.org app_name: Thunderbird app_passwd: eiQu4ahz8iePh app_passwd2: eiQu4ahz8iePh protocols: - imap_access - smtp_access - sieve_access active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/bcc: post: tags: - Address Rewriting summary: Create a BCC map operationId: addBccMap description: >- Sends a hidden copy of the mail of a domain or address to `bcc_dest`. Administrators, API keys and domain administrators with the BCC maps ACL may call it (domain administrators for their domains). The local destination is not informed when the copy cannot be delivered. Messages: `bcc_saved`; `invalid_bcc_map_type`, `bcc_empty`, `bcc_must_be_email`, `bcc_exists` (same `local_dest` and `type`), `access_denied` (no ACL, a domain or address the caller does not manage, or an address that is no mailbox or alias). A `local_dest` that is neither a domain nor an e-mail address answers `Cannot add item`. requestBody: content: application/json: schema: type: object properties: local_dest: type: string description: >- A mailcow domain (stored as `@example.org`, IDN in punycode) or the address of a mailbox or alias bcc_dest: type: string description: >- One e-mail address that gets the copies (use an alias for several) type: type: string enum: - sender - rcpt description: >- `sender`: copy the mail sent by `local_dest`; `rcpt`: copy the mail sent to it active: type: [boolean, integer, string] description: 1 = active, 0 = inactive default: 0 required: - local_dest - bcc_dest - type example: local_dest: example.org bcc_dest: archive@example.com type: rcpt active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/dkim: post: summary: Generate DKIM keys description: >- Generates an RSA DKIM key for each domain. Every domain must be a domain or alias domain the caller has access to and must not have a key yet. Success `dkim_added`, errors `dkim_domain_or_sel_invalid` (invalid domain or selector, key exists, key generation failed), `access_denied`. operationId: addDkimKey tags: - DKIM requestBody: required: true content: application/json: schema: type: object required: - domains - key_size properties: domains: type: string description: >- Domain names, separated by spaces, commas, semicolons or line breaks dkim_selector: type: string default: dkim description: Selector (letters, digits, `-`, `_`, `.`) key_size: type: [integer, string] description: RSA key size in bits (the UI offers 1024, 2048, 3072, 4096) example: domains: example.org, example.net dkim_selector: dkim key_size: 2048 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/dkim_duplicate: post: summary: Copy a DKIM key to other domains description: >- Copies the DKIM key and selector of `from_domain` to the domains in `to_domain`, replacing their keys. Admins only. The target names are not checked. Success `dkim_duplicated` per target, errors `access_denied`, `dkim_domain_or_sel_invalid` (`from_domain` has no key). operationId: duplicateDkimKey tags: - DKIM requestBody: required: true content: application/json: schema: type: object required: - from_domain - to_domain properties: from_domain: type: string description: Domain whose key is copied to_domain: description: Domain name or list of domain names that get the key oneOf: - type: string - type: array items: type: string example: from_domain: example.org to_domain: - example.net - example.com responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/domain: post: summary: Add a domain description: >- Adds a domain. Admins only (API keys are admins). Quotas are in MiB. Values that are not sent come from the domain template given in `template`, else from the first domain template (`Default`). `relay_all_recipients` = 1 sets `backupmx`, `relay_unknown_only` = 1 sets both. Besides the domain this sets the rate limit (`rl_value`, `rl_frame`) and creates a DKIM key (`key_size`, `dkim_selector`; the Default template creates a 2048 bit key with selector `dkim`), so the answer can hold `rl_saved` and `dkim_added` (or `domain_add_dkim_available` when a key exists already) before `domain_added`. Errors: `access_denied`, `mailbox_defquota_exceeds_mailbox_maxquota`, `mailbox_quota_exceeds_domain_quota`, `defquota_empty`, `maxquota_empty`, `domain_invalid`, `object_is_not_numeric`, `domain_exists` (also for an alias domain of that name), `domain_cannot_match_hostname`. SOGo knows the domain only after a restart (`restart_sogo`); a failed restart answers `domain_added_sogo_failed` (warning) although the domain was added. operationId: addDomain tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - domain properties: domain: type: string description: Domain name (stored lowercase, IDN as punycode) description: type: string description: Description, default the domain name template: type: [integer, string] description: >- ID of the domain template that gives the values not sent (see get/domain/template/all); default the first template aliases: type: [integer, string] description: Max. number of aliases mailboxes: type: [integer, string] description: Max. number of mailboxes defquota: type: [integer, string] description: Default quota of new mailboxes in MiB, must not be 0 maxquota: type: [integer, string] description: Max. quota per mailbox in MiB, must not be 0 quota: type: [integer, string] description: Quota of the whole domain in MiB active: type: [integer, string] enum: [0, 1, "0", "1"] description: Domain active gal: type: [integer, string] enum: [0, 1, "0", "1"] description: >- Global address list in SOGo (all objects of the domain) backupmx: type: [integer, string] enum: [0, 1, "0", "1"] description: Relay this domain (backup MX) relay_all_recipients: type: [integer, string] enum: [0, 1, "0", "1"] description: >- Relay all recipients; else a mailbox is needed for every recipient to relay. 1 sets `backupmx`. relay_unknown_only: type: [integer, string] enum: [0, 1, "0", "1"] description: >- Relay non-existing mailboxes only, existing mailboxes are delivered locally. 1 sets `backupmx` and `relay_all_recipients`. tags: type: array items: type: string description: >- Tags of the domain; tags beyond the tagging limit (`TAGGING_LIMIT`, 25) are dropped with the warning `tag_limit_exceeded` rl_value: type: [integer, string] description: Rate limit, messages per `rl_frame`; empty or 0 = none rl_frame: type: string enum: [s, m, h, d] description: Rate limit time frame dkim_selector: type: string description: Selector of the DKIM key that is created key_size: type: [integer, string] description: >- RSA key size in bits of the DKIM key that is created (the UI offers 1024, 2048, 3072, 4096); empty or 0 = no key restart_sogo: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: Restart SOGo afterwards example: domain: example.org description: Example Org aliases: 400 mailboxes: 10 defquota: 3072 maxquota: 10240 quota: 10240 active: 1 gal: 1 backupmx: 0 relay_all_recipients: 0 relay_unknown_only: 0 tags: - customer rl_value: 100 rl_frame: h dkim_selector: dkim key_size: 2048 restart_sogo: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/domain-admin: post: summary: Create domain administrator description: >- Creates a domain administrator, who manages mailboxes, aliases and the other objects of the given domains. The username is converted to lowercase; it may only contain letters, digits, `_`, `.` and `-` and must not be used by a mailbox, an administrator or a domain administrator. The new account gets the default ACLs (see `edit/da-acl`). Success: `domain_admin_added`. Errors: `domain_invalid` (no domain or an unknown domain), `username_invalid`, `object_exists`, `password_complexity`, `password_mismatch`. Unknown domains are skipped with `domain_invalid`; if none of the domains exists, the account is not created although `domain_admin_added` is reported too. operationId: addDomainAdmin tags: - Domain admin requestBody: required: true content: application/json: schema: type: object properties: username: type: string description: Username (letters, digits, `_`, `.`, `-`; stored in lowercase) password: type: string description: Password, must match the password policy password2: type: string description: The password again domains: description: Domain or list of domains the account administers oneOf: - type: string - type: array items: type: string active: type: [integer, string, boolean] description: >- 1 = the account can log in, 0 = disabled. Missing counts as 0: send `1` for a usable account. default: 0 required: - username - password - password2 - domains example: username: domainadmin1 password: "Rand0m-Passw0rd!" password2: "Rand0m-Passw0rd!" domains: - example.org - example.com active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/sso/domain-admin: post: summary: Issue domain administrator login token description: >- Issues a one-time login token for a domain administrator. Opening `https:///domainadmin/mailbox?sso_token=` in a browser logs in as this domain administrator without password or two-factor authentication. The token is valid for 30 seconds, can be used once and only works for an active domain administrator. Errors: `access_denied`, `object_doesnt_exist` (no domain administrator of this name). operationId: issueDomainAdminSsoToken tags: - Single Sign-On requestBody: required: true content: application/json: schema: type: object properties: username: type: string description: Username of the domain administrator required: - username example: username: domainadmin1 responses: "200": description: The token, or the error messages content: application/json: schema: oneOf: - type: object properties: token: type: string description: Login token (five groups of six hex digits) required: - token - $ref: "#/components/schemas/Messages" example: token: 591F6D-5C3DD2-7455CD-DAF1C1-AA4FCC "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/da-acl: post: summary: Edit domain administrator ACLs description: >- Sets the ACLs (permissions) of domain administrators: the ACLs in `da_acl` are granted, all others are revoked. An empty or missing `da_acl` revokes every ACL. One message per account. Success: `acl_saved`. Error: `Cannot determine current ACL` (no domain administrator of this name). operationId: editDomainAdminAcl tags: - Domain admin requestBody: required: true content: application/json: schema: type: object properties: items: description: Username of the domain administrator, or a list of usernames oneOf: - type: string - type: array items: type: string attr: type: object properties: da_acl: description: The ACLs to grant (a single name or a list) oneOf: - $ref: "#/components/schemas/DomainAdminAcl" - type: array items: $ref: "#/components/schemas/DomainAdminAcl" required: - items - attr example: items: - domainadmin1 attr: da_acl: - syncjobs - quarantine - login_as - sogo_access - app_passwds - bcc_maps - filters - ratelimit - spam_policy - protocol_access - smtp_ip_access - mailbox_relayhost - domain_relayhost - alias_external_goto - mailbox_groups responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/domain-admin: post: summary: Edit domain administrators description: >- Changes domain administrators. Attributes that are not sent keep their value, except `force_tfa` and `force_pw_update`, which are set to 0 when they are missing. `domains` replaces the list of domains; an empty or missing list keeps it. The password is only changed when `password` is not empty. Renaming an account moves its ACLs, two-factor authentication and the sync job sources it created to the new name (FIDO2 keys stay with the old name). One message per account. Success: `domain_admin_modified`. Errors: `access_denied` (no domain administrator of this name), `domain_invalid`, `username_invalid` (invalid `username_new` or the name of another domain administrator). A password that does not match `password2` or the policy (`password_mismatch`, `password_complexity`) stops the call, after the domains were already saved. operationId: editDomainAdmin tags: - Domain admin requestBody: required: true content: application/json: schema: type: object properties: items: description: Username of the domain administrator, or a list of usernames oneOf: - type: string - type: array items: type: string attr: type: object properties: active: type: [integer, string, boolean] description: 1 = active, 0 = disabled username_new: type: string description: New username (letters, digits, `_`, `.`, `-`) domains: description: The domains the account administers from now on oneOf: - type: string - type: array items: type: string password: type: string description: New password; empty or missing keeps the password password2: type: string description: The new password again force_tfa: type: [integer, string, boolean] description: 1 = two-factor authentication must be set up at the next login default: 0 force_pw_update: type: [integer, string, boolean] description: 1 = the password must be changed at the next login default: 0 disable_tfa: type: [integer, string, boolean] description: >- Present (with any value) = deactivate the two-factor authentication of the account required: - items - attr example: items: - domainadmin1 attr: active: 1 domains: - example.org password: "N3w-Passw0rd!" password2: "N3w-Passw0rd!" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/domain-policy: post: summary: Add a sender to a domain allow or deny list description: >- Adds an entry to the allow list (`wl`) or deny list (`bl`) of a domain. Needs the ACL `spam_policy` (API keys have it). Success `domain_modified`, errors `access_denied` (also for an invalid domain or list), `policy_list_from_invalid`, `policy_list_from_exists` (the sender is on one of the lists already). operationId: addDomainPolicy tags: - Domain antispam policies requestBody: required: true content: application/json: schema: type: object required: - domain - object_list - object_from properties: domain: type: string description: Domain the entry belongs to object_list: type: string enum: [wl, bl] description: "`wl` allow list (whitelist), `bl` deny list (blacklist)" object_from: type: string description: >- Sender address or pattern, `*` as wildcard (e.g. `*@example.com`); letters, digits and `@ _ . - *` example: domain: example.org object_list: wl object_from: "*@partner.example.com" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/fwdhost: post: tags: - Forwarding Hosts summary: Add forwarding hosts operationId: addForwardingHost description: >- Adds hosts whose incoming mail is accepted unconditionally: no DNSBL checks, no greylisting, spam is never rejected. A host or domain name is resolved to the addresses of its SPF record, else its MX hosts, else its A/AAAA records; every address becomes an entry with the name as `source`. Administrators and API keys only. Messages: `forwarding_host_added` (the addresses), `invalid_host` (nothing resolved), `access_denied`, `redis_error`. requestBody: content: application/json: schema: type: object properties: hostname: type: string description: >- IPv4 or IPv6 address, network in CIDR notation, host name or domain name filter_spam: type: [boolean, integer, string] description: >- 1 = scan the mail of the hosts for spam (it can be filed into the junk folder, never rejected); anything else = do not scan it default: 0 required: - hostname example: hostname: relay.example.com filter_spam: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/fwdhost: post: tags: - Forwarding Hosts summary: Update forwarding hosts operationId: editForwardingHost description: >- Turns the spam filter of forwarding hosts on or off. Administrators and API keys only. Per host: `object_modified`, `access_denied` (unknown host), `redis_error`. Without `keep_spam` the spam filter is turned on. requestBody: content: application/json: schema: type: object properties: items: description: >- Hosts as get/fwdhost lists them (`host`), a single value or a list oneOf: - type: array items: type: string - type: string attr: type: object properties: keep_spam: type: [boolean, integer, string] description: >- 1 = do not scan the mail of the host for spam; anything else = scan it required: - items - attr example: items: - 192.0.2.25 - "2001:db8::25" attr: keep_spam: "0" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/mailbox: post: tags: - Mailboxes summary: Create a mailbox operationId: addMailbox description: >- Creates the mailbox `local_part@domain` with its alias entry, quota record and user ACL. Fields that are not sent take the values of the mailbox template given in `template` (by default the first template, "Default"). Admins and domain admins (for their domains) may call it, mailbox users get `access_denied`. Checks: valid address (`mailbox_invalid`), domain exists (`domain_not_found`), address not in use by a mailbox, alias or time-limited alias (`object_exists`, `is_alias`, `is_spam_alias`), password policy (`password_complexity`, `password_mismatch`), mailbox count and quota limits of the domain (`max_mailbox_exceeded`, `mailbox_quota_exceeded`, `mailbox_quota_left_exceeded`), quota 0 (unlimited) needs the `unlimited_quota` ACL (`unlimited_quota_acl`). Success: `mailbox_added`. Tags after the 26th are dropped with the warning `tag_limit_exceeded`. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MailboxAdd" example: local_part: john.doe domain: example.org name: John Doe password: Moo2Shoh5aiXu password2: Moo2Shoh5aiXu quota: 3072 active: 1 force_pw_update: 1 tags: - sales acl: - spam_alias - spam_score - delimiter_action - app_passwds responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/mailbox-group: post: summary: Create mailbox group description: >- Creates a mailbox group in a domain. With an address the group is a distribution list: mail to the address goes to all members (the group keeps an alias row for it that the alias API shows read-only, and it counts against the alias limit of the domain; it is inactive while the group is inactive or has no members). A group can also let its members send as other addresses (`send_as`, `sender_allowed`), open other mailboxes (`imap_grants`), see shared SOGo folders (`sogo_grants`) and get a mailbox template (`template_id`). Admins (every read/write API key) may create groups in every domain and set `template_id`, `priority` and `idp_ref`; domain admins need the `mailbox_groups` ACL and may only use their domains and mailboxes. Members must be regular mailboxes (no resources). Answers `group_added` plus warnings of the follow-up work (`group_too_many_members` when a group with an address has more than 1000 members, `group_template_failed`, `group_imap_grant_failed`, `group_subscription_failed`, `group_sogo_init_failed`; failed steps are retried by a cron). Errors (nothing is written): `domain_not_found`, `access_denied`, `group_name_invalid`, `group_description_invalid`, `object_exists` (name exists in the domain), `group_address_invalid`, `is_alias_or_mailbox`, `max_alias_exceeded`, `group_member_invalid`, `group_member_idp`, `group_send_as_invalid`, `group_grant_invalid`, `template_id_invalid`, `group_priority_invalid`. operationId: addMailboxGroup tags: - Groups requestBody: content: application/json: schema: allOf: - type: object required: - domain - name properties: domain: type: string description: Domain of the group (no alias domain), case-insensitive - $ref: "#/components/schemas/MailboxGroupAttributes" example: domain: example.org name: Sales description: Sales team local_part: sales members: - alice@example.org - bob@example.org send_as: - orders@example.org sender_allowed: 1 imap_grants: - owner: shared@example.org rights: write subscribe: 1 template_id: 3 priority: 10 active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/oauth2-client: post: summary: Create OAuth2 client description: >- Creates an OAuth2 client with a random client ID and secret and the scope `profile`. The answer does not contain them: read them with `get/oauth2-client/all`. The redirect URI is not validated. Success: `Added client access`. operationId: addOAuth2Client tags: - oAuth Clients requestBody: required: true content: application/json: schema: type: object properties: redirect_uri: type: string description: Redirect (callback) URL of the client application required: - redirect_uri example: redirect_uri: https://app.example.org/oauth/callback responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/recipient_map: post: tags: - Address Rewriting summary: Create a recipient map operationId: addRecipientMap description: >- Replaces the recipient address of mail before delivery: mail to `recipient_map_old` goes to `recipient_map_new`. Administrators and API keys only (other callers get `Cannot add item`). Messages: `recipient_map_entry_saved`; `invalid_recipient_map_old`, `invalid_recipient_map_new`, `recipient_map_entry_exists`. requestBody: content: application/json: schema: type: object properties: recipient_map_old: type: string description: >- Original recipient: an e-mail address or a domain (stored as `@example.org`, IDN in punycode; a leading `@` is optional) recipient_map_new: type: string description: New recipient, an e-mail address or a domain active: type: [boolean, integer, string] description: 1 = active, 0 = inactive default: 0 required: - recipient_map_old - recipient_map_new example: recipient_map_old: jane.doe@example.org recipient_map_new: jane.smith@example.org active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/relayhost: post: tags: - Routing summary: Create a sender-dependent transport operationId: addRelayhost description: >- Adds a relay host for outgoing mail. Domains and mailboxes use it when their `relayhost` attribute is set to its ID (edit/domain, edit/mailbox). The service is always `smtp:` (TLS when offered, no SMTPS). The new entry is always active. Colons in `username` and `password` are stored as `\:` (in the password as `\\:`); edit/relayhost stores both unchanged. Administrators and API keys only. Messages: `relayhost_added` (without the host name), `invalid_host` (empty `hostname`), `access_denied`, `mysql_error`. requestBody: content: application/json: schema: type: object properties: hostname: type: string description: >- Next hop: `host`, `host:25`, `[host]:587`, `[192.0.2.1]` or `[192.0.2.1]:25` username: type: string description: SMTP user name, empty for no authentication default: "" password: type: string description: SMTP password default: "" required: - hostname example: hostname: "[smtp.example.com]:587" username: relay-user@example.com password: Secret-Passphrase-42 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/resource: post: tags: - Resources summary: Create a resource operationId: addResource description: >- Creates a bookable resource (room, equipment, group) for SOGo. Its address is made of the letters and digits of the description and the domain (description "Meeting Room 1" → `MeetingRoom1@example.org`). Admins and domain admins (for their domains). Errors: `resource_invalid` (address or kind invalid), `description_invalid`, `domain_invalid`, `access_denied`, `object_exists`, `is_alias`, `is_spam_alias`, `domain_not_found`. Success: `resource_added`. requestBody: required: true content: application/json: schema: type: object required: - domain - description - kind properties: domain: type: string description: Domain of the resource description: type: string description: Name of the resource, also the source of its address kind: type: string enum: - location - group - thing description: Type of resource multiple_bookings: type: [integer, string] default: 0 description: >- Booking rule: 0 always free (bookings are not limited and do not block), -1 unlimited but shown as busy when booked, n ≥ 1 at most n overlapping bookings; values below -1 mean -1 active: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: 1 active, 0 inactive example: domain: example.org description: Meeting Room 1 kind: location multiple_bookings: 0 active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/syncjob: post: summary: Create sync job description: >- Creates a sync job that copies the remote account `user1` of a sync source into a local mailbox. Host, port, encryption and authentication come from the source (`source_id`); the job only carries the remote login. Needs the `syncjobs` ACL (admins and API keys have it); the caller must have access to the local mailbox. An API key must name the mailbox in `username` (`no_user_defined` otherwise), mailbox users always create jobs for their own mailbox. Answers `mailbox_modified` with the local mailbox. Errors: `access_denied` (no access to the mailbox, `mins_interval` missing or not 1-43800, `exclude` no valid regular expression), `no_user_defined`, `imapsync_user1_invalid`, `imapsync_source_unavailable` (source unknown, invisible to the caller or inactive), `password_empty`, `imapsync_source_oauth_connect_required` (XOAUTH2 authorization_code source without a sign-in of the mailbox for `user1`), `object_exists` (the mailbox already syncs `user1` from this source), `imapsync_custom_params_format`, `imapsync_custom_param_invalid`, `imapsync_custom_param_value`. Passwords are logged as `*`. operationId: addSyncJob tags: - Sync jobs requestBody: content: application/json: schema: allOf: - type: object required: - username - source_id - user1 - mins_interval properties: username: type: string description: >- Local mailbox the job syncs into (`user2`); cannot be changed later. Optional for mailbox users (own mailbox). - $ref: "#/components/schemas/SyncJobAttributes" example: username: alice@example.org source_id: 3 user1: alice@old-provider.example.com password1: remote-password mins_interval: 20 subfolder2: Imported exclude: "(?i)spam|(?i)junk" custom_params: - o: folder v: INBOX delete2duplicates: 1 automap: 1 subscribeall: 1 active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/syncjob_source: post: summary: Create sync source description: >- Creates a reusable IMAP sync source: server, encryption and authentication of a remote system that sync jobs use by `source_id`. Password sources (PLAIN, LOGIN, CRAM-MD5) take the password from each job, XOAUTH2 sources carry an OAuth2 client (client_credentials: one token of the source for every mailbox of the tenant; authorization_code: every mailbox owner signs in once in the mailcow UI). The creator is stored in `created_by` (`API` for API keys); names are unique per creator. Needs the `syncjobs` ACL. Only admins create `all` sources; XOAUTH2 sources are created by admins and domain admins only. OAuth2 endpoints must be https:// URLs with a valid host name; for sources not created by a superadmin or an API key every resolved address must also be public. A client_credentials source fetches its token while it is saved: the source is saved even when that fails, the answer then also has `imapsync_source_token_refresh_failed`. Answers `imapsync_source_added`. Errors (nothing is written): `imapsync_source_name_invalid`, `imapsync_source_description_invalid`, `imapsync_source_host_invalid`, `imapsync_source_port_invalid`, `imapsync_source_enc_invalid`, `imapsync_source_auth_invalid`, `imapsync_source_oauth_required`, `imapsync_source_oauth_invalid`, `imapsync_source_oauth_endpoint_invalid`, `imapsync_source_oauth_extra_invalid`, `imapsync_source_scope_empty`, `access_denied` (scope or targets not allowed), `object_exists`. Secrets are logged as `*`. operationId: addSyncJobSource tags: - Sync jobs requestBody: content: application/json: schema: allOf: - type: object required: - name - host - port - $ref: "#/components/schemas/SyncJobSourceAttributes" examples: password: summary: Password source for a domain value: name: Old provider description: Migration from the old provider host: imap.example.com port: 993 enc: SSL auth_type: PLAIN scope: domain domains: - example.org active: 1 client_credentials: summary: XOAUTH2 source with an app token (Microsoft 365) value: name: M365 tenant host: outlook.office365.com port: 993 enc: SSL auth_type: XOAUTH2 oauth_flow: client_credentials oauth_token_endpoint: https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/oauth2/v2.0/token oauth_client_id: 11111111-2222-3333-4444-555555555555 oauth_client_secret: client-secret-value oauth_scope: https://outlook.office365.com/.default active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/tls-policy-map: post: tags: - Outgoing TLS Policy Map Overrides summary: Create a TLS policy map operationId: addTlsPolicyMap description: >- Sets the TLS policy Postfix uses for outgoing connections to a destination (`smtp_tls_policy_maps`). Administrators and API keys only (other callers get `Cannot add item`). Messages: `tls_policy_map_entry_saved`; `tls_policy_map_dest_invalid` (empty, `.`, `*` or `@`), `tls_policy_map_parameter_invalid`, `tls_policy_map_entry_exists`. The backend does not check `policy`; the database only takes the listed values. requestBody: content: application/json: schema: type: object properties: dest: type: string description: >- Destination, e.g. `example.org`, `.example.org` (its subdomains) or `[mail.example.org]:25`; IDN in punycode policy: type: string description: >- Postfix TLS security level (see [smtp_tls_policy_maps](https://www.postfix.org/TLS_README.html#client_tls_policy)) enum: - none - may - encrypt - dane - dane-only - fingerprint - verify - secure parameters: type: string description: >- Empty or space-separated `name=value` attributes, e.g. `protocols=!SSLv2 ciphers=medium exclude=3DES` default: "" active: type: [boolean, integer, string] description: 1 = active, 0 = inactive default: 0 required: - dest - policy example: dest: example.com policy: encrypt parameters: protocols=>=TLSv1.2 active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/transport: post: tags: - Routing summary: Create transport maps operationId: addTransport description: >- Routes mail for the destinations over a next hop. A transport map entry overrules a sender-dependent transport. The service is always `smtp:` (TLS when offered, no SMTPS); addresses matching `/localhost$/` are always delivered locally. Administrators and API keys only. One entry is created per destination. All entries with the same next hop share one set of credentials: `username` and `password` are written to every existing entry of `nexthop`. Messages: `relayhost_added` (without names); `invalid_nexthop` (empty), `invalid_nexthop_authenticated` (the next hop exists with another user name), `next_hop_interferes`, `next_hop_interferes_any` (`host` and `[host]:port` cannot be used side by side) stop the call; `transport_dest_exists` and `invalid_destination` drop the destination. The destinations are only checked when other transport maps exist. Without any valid destination the answer is the messages or `Cannot add item`. requestBody: content: application/json: schema: type: object properties: destination: type: string description: >- Destinations, separated by spaces, commas, semicolons or line breaks: `example.org`, `.example.org` (its subdomains), `*` or `box@example.org`. With `is_mx_based` a regular expression matched against the MX name of the recipient domain, e.g. `.*\.google\.com`. nexthop: type: string description: >- `host`, `host:25`, `[host]:25` or an IP address (put in brackets automatically) username: type: string description: SMTP user name, empty for no authentication default: "" password: type: string description: SMTP password default: "" is_mx_based: type: [boolean, integer, string] description: 1 = `destination` is a regular expression for MX names default: 0 active: type: [boolean, integer, string] description: 1 = active, 0 = inactive default: 0 required: - destination - nexthop example: destination: example.com, .example.com nexthop: "[mx.example.com]:25" username: "" password: "" is_mx_based: "0" active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/alias: post: tags: - Aliases summary: Delete aliases operationId: deleteAlias description: >- Deletes the aliases and the send-as rights granted for their addresses (`alias_removed` per alias). Unknown aliases and aliases of domains the caller does not manage answer `access_denied`; distribution addresses of mailbox groups `alias_group_managed`. requestBody: content: application/json: schema: type: array description: IDs or addresses of the aliases (a single value works too) items: type: [integer, string] example: - 12 - info@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/api-key: post: summary: Delete API keys description: >- Deletes API keys. Service keys from mailcow.conf cannot be deleted here. One message per key. Success: `api_key_deleted`. Errors: `access_denied` (no key with this ID), `api_key_service_locked`. operationId: deleteApiKey tags: - API Keys requestBody: required: true content: application/json: schema: type: array description: IDs of the API keys items: type: [integer, string] example: - 3 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/app-passwd: post: tags: - App Passwords summary: Delete app passwords operationId: deleteAppPassword description: >- Deletes app passwords by ID. Needs the user ACL `app_passwds` and access to the owning mailbox (API keys: any). Messages: `app_passwd_removed` per ID; the first unknown ID (`app_passwd_id_invalid`) or foreign ID (`access_denied`) stops the call. requestBody: required: true content: application/json: schema: type: array description: App password IDs items: type: integer example: - 23 - 24 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/bcc: post: tags: - Address Rewriting summary: Delete BCC maps operationId: deleteBccMap description: >- Deletes BCC maps by ID (`bcc_deleted` per map, `access_denied` for a map of a domain the caller does not manage). A non-numeric ID stops the call. Same callers as add/bcc. requestBody: content: application/json: schema: type: array description: IDs of the BCC maps (a single value works too) items: type: [integer, string] example: - 3 - 4 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/dkim: post: summary: Delete DKIM keys description: >- Deletes the DKIM keys of domains. Admins only. Success `dkim_removed` per domain (also when it had no key), errors `access_denied`, `dkim_domain_or_sel_invalid`. operationId: deleteDkimKeys tags: - DKIM requestBody: required: true content: application/json: schema: type: array description: Domain names items: type: string example: - example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/domain: post: summary: Delete domains description: >- Deletes domains. Admins only. A domain that still has mailboxes or resources is kept (`domain_not_empty`). With the domain go its aliases, alias domains, domain admin assignments, BCC maps, MTA-STS policy, spam policies, mailbox group data and rate limit; domain admins left without any domain are deleted. Its mail storage is moved to the garbage collector of Dovecot (a failure is a warning). The DKIM key is kept. Success `domain_removed`, errors `access_denied`, `domain_invalid`. operationId: deleteDomain tags: - Domains requestBody: required: true content: application/json: schema: type: array description: Domain names items: type: string example: - example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/domain-admin: post: summary: Delete domain administrators description: >- Deletes domain administrators with their ACLs, two-factor authentication and FIDO2 keys; sync job sources they created are released. The domains and their mailboxes stay. One message per account. Success: `domain_admin_removed`. Error: `username_invalid` (no domain administrator of this name). operationId: deleteDomainAdmin tags: - Domain admin requestBody: required: true content: application/json: schema: type: array description: Usernames of the domain administrators items: type: string example: - domainadmin1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/domain-policy: post: summary: Delete entries of domain allow and deny lists description: >- Deletes allow or deny list entries of domains by their `prefid` (see get/policy_wl_domain and get/policy_bl_domain). Needs the ACL `spam_policy`. Success `item_deleted` per entry, error `access_denied` (non-numeric ID, entry of a mailbox or unknown entry). operationId: deleteDomainPolicy tags: - Domain antispam policies requestBody: required: true content: application/json: schema: type: array description: Entry IDs (`prefid`) items: type: [integer, string] example: - 12 - 13 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/fwdhost: post: tags: - Forwarding Hosts summary: Delete forwarding hosts operationId: deleteForwardingHost description: >- Removes forwarding hosts (`forwarding_host_removed` per host, also for hosts that do not exist). Administrators and API keys only. requestBody: content: application/json: schema: type: array description: Hosts as get/fwdhost lists them (`host`; a single value works too) items: type: string example: - 192.0.2.25 - "2001:db8::25" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/fwdhost/{host}: get: tags: - Forwarding Hosts summary: Get forwarding hosts operationId: getForwardingHosts description: >- `all` lists the forwarding hosts (administrators and API keys), a host address returns that entry (every logged-in role). `{}` if there is none. Networks (`198.51.100.0/24`) cannot be read one by one: the path ends at the slash. parameters: - name: host in: path required: true description: "`all` or the address of a forwarding host" schema: type: string example: all responses: "200": description: The list of forwarding hosts (`all`) or one host content: application/json: schema: anyOf: - type: array items: $ref: "#/components/schemas/ForwardingHost" - $ref: "#/components/schemas/ForwardingHost" examples: all: summary: get/fwdhost/all value: - host: 192.0.2.25 source: relay.example.com keep_spam: "no" - host: "2001:db8::25" source: relay.example.com keep_spam: "no" - host: 198.51.100.0/24 source: 198.51.100.0/24 keep_spam: "yes" one: summary: get/fwdhost/192.0.2.25 value: host: 192.0.2.25 source: relay.example.com keep_spam: "no" "401": $ref: "#/components/responses/Unauthorized" /api/v1/delete/mailbox: post: tags: - Mailboxes summary: Delete mailboxes operationId: deleteMailbox description: >- Deletes mailboxes with everything that belongs to them: maildir (moved to the garbage collector), aliases pointing only to them, ACLs, quarantine, filters, policies, time-limited aliases, sync jobs, SOGo data, BCC maps, OAuth tokens, 2FA keys, rate limit; the address is removed from the targets of other aliases. Admins and domain admins (for their domains); a mailbox user session passes the check for its own mailbox and can delete it. Messages per mailbox: `mailbox_removed` or `access_denied`; a warning if the maildir could not be moved. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MailboxItems" example: - john.doe@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/mailbox-group: post: summary: Delete mailbox groups description: >- Deletes groups. The group alias, the send-as rights and the grants of the group are removed; members keep their mailbox settings (another group's template may become effective). Same permissions as edit. Answers one message per id: `group_removed` or `access_denied` (unknown id, id that is no number, no permission). operationId: deleteMailboxGroups tags: - Groups requestBody: content: application/json: schema: type: array description: Group ids items: type: - integer - string example: - 7 - 8 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/identity-link: post: summary: Remove identity links description: >- Admins only (every read/write API key). Removes the links of mailboxes to their Identity Provider users; the next sync run or login links them again by email. Use it to resolve conflicts, e.g. when an address was given to another user of the provider. Answers `iam_link_removed` per mailbox (also for mailboxes without a link), or `access_denied`. operationId: deleteIdentityLinks tags: - Identity Provider requestBody: content: application/json: schema: type: array description: Mailboxes items: type: string example: - alice@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/mailq: post: summary: Delete queued messages description: >- Deletes messages from the Postfix queue (`postsuper -d `; admins only, an API key may). To empty the whole queue use `super_delete` of `POST /api/v1/edit/mailq`. Queue IDs that are not hexadecimal are skipped. Messages: `queue_command_success`; `Error: command failed: ...` (danger); `unknown` (danger) when the command gave no answer; `access_denied` for non-admins. operationId: deleteQueuedMessages tags: - Queue Manager requestBody: content: application/json: schema: description: Queue IDs (a single ID works too) oneOf: - type: array items: type: string - type: string example: - B98C6260CA1 - 4C1E8260D02 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/oauth2-client: post: summary: Delete OAuth2 clients description: >- Deletes OAuth2 clients. Tokens issued to them are not deleted (use `revoke_tokens` of `edit/oauth2-client` first). Success: one `items_deleted` message for the whole call. Error: `access_denied` for an ID that is no number. operationId: deleteOAuth2Client tags: - oAuth Clients requestBody: required: true content: application/json: schema: type: array description: IDs of the clients items: type: [integer, string] example: - 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/qitem: post: summary: Delete quarantined messages description: >- Deletes quarantine items without releasing or learning them. An API key (admin) may delete every item, others only items of their mailboxes or domains; the quarantine module must be allowed for the caller. Messages: `item_deleted` per item (for an admin also for IDs that do not exist); `access_denied` (danger) for a non-numeric or foreign ID. operationId: deleteQuarantineItems tags: - Quarantine requestBody: content: application/json: schema: description: IDs of quarantine items (a single ID works too) oneOf: - type: array items: type: [integer, string] - type: [integer, string] example: - "33" - "34" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/qitem: post: summary: Release or learn quarantined messages description: >- Acts on quarantine items. An API key (admin) may act on every item, others only on items of their mailboxes or domains; the quarantine module must be allowed for the caller. - `release` and `learnham` do the same: the message is delivered to its recipient (through Postfix, as the original message or as an attachment, see the release format of `POST /api/v1/edit/quarantine`), deleted from the quarantine and learned as ham by Rspamd. - `learnspam` deletes the item and learns the message as spam. Messages: `item_released` and `learned_ham`, or `qlearn_spam`, per item; `access_denied` (danger) for a non-numeric, unknown or foreign ID; `release_send_failed` (warning); `ham_learn_error`, `spam_learn_error` (danger); `fuzzy_learn_error` (warning). When Postfix cannot be reached or answers with an unexpected SMTP code, the remaining items are not processed. An unknown `action` does nothing and is answered with `Task completed`. operationId: editQuarantineItems tags: - Quarantine requestBody: content: application/json: schema: type: object properties: items: description: ID or list of IDs of quarantine items oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: action: type: string enum: - release - learnham - learnspam required: - action required: - items - attr example: items: - "33" - "34" attr: action: release responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/recipient_map: post: tags: - Address Rewriting summary: Delete recipient maps operationId: deleteRecipientMap description: >- Deletes recipient maps by ID (`recipient_map_entry_deleted` per ID, also for IDs that do not exist). A non-numeric ID stops the call. Administrators and API keys only. requestBody: content: application/json: schema: type: array description: IDs of the recipient maps (a single value works too) items: type: [integer, string] example: - 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/relayhost: post: tags: - Routing summary: Delete sender-dependent transports operationId: deleteRelayhost description: >- Deletes sender-dependent transports by ID (`relayhost_removed` per ID, also for IDs that do not exist). Domains that used one fall back to the default route (`relayhost` 0); the `relayhost` attribute of mailboxes is not reset. Administrators and API keys only. requestBody: content: application/json: schema: type: array description: IDs of the sender-dependent transports (a single value works too) items: type: [integer, string] example: - 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/resource: post: tags: - Resources summary: Delete resources operationId: deleteResource description: >- Deletes resources and their SOGo data. Admins and domain admins (for their domains). Messages per resource: `resource_removed` or `access_denied`. requestBody: required: true content: application/json: schema: type: array description: Resource addresses items: type: string example: - MeetingRoom1@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/syncjob: post: summary: Delete sync jobs description: >- Deletes sync jobs. Needs the `syncjobs` ACL and access to the local mailbox of each job (admins and API keys: every job). The OAuth2 token of the job's remote account is deleted when no other job uses it. Answers one message per id: `deleted_syncjob` with the id as sent, or `access_denied` (unknown job, id that is no number, no access). operationId: deleteSyncJobs tags: - Sync jobs requestBody: content: application/json: schema: type: array description: Job ids items: type: - integer - string example: - 12 - 13 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/syncjob_source: post: summary: Delete sync sources description: >- Deletes sync sources that no sync job uses (ids that are no number are skipped). Needs the `syncjobs` ACL and edit rights on the source. Answers one message per id: `imapsync_source_deleted`, or `imapsync_source_in_use`, `access_denied` (unknown id, no edit rights). operationId: deleteSyncJobSources tags: - Sync jobs requestBody: content: application/json: schema: type: array description: Source ids items: type: - integer - string example: - 3 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/tls-policy-map: post: tags: - Outgoing TLS Policy Map Overrides summary: Delete TLS policy maps operationId: deleteTlsPolicyMap description: >- Deletes TLS policy maps by ID (`tls_policy_map_entry_deleted` per ID, also for IDs that do not exist). A non-numeric ID stops the call. Administrators and API keys only. requestBody: content: application/json: schema: type: array description: IDs of the TLS policy maps (a single value works too) items: type: [integer, string] example: - 2 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/transport: post: tags: - Routing summary: Delete transport maps operationId: deleteTransport description: >- Deletes transport maps by ID (`relayhost_removed` per ID, also for IDs that do not exist). Administrators and API keys only. requestBody: content: application/json: schema: type: array description: IDs of the transport maps (a single value works too) items: type: [integer, string] example: - 4 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/mailbox/tag/{mailbox}: post: tags: - Mailboxes summary: Remove tags from a mailbox operationId: deleteMailboxTags description: >- Removes the given tags from the mailbox. Admins and domain admins (for their domains); `access_denied` otherwise. Answers `mailbox_modified`, or "Cannot delete item" when no tag was sent. parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org requestBody: required: true content: application/json: schema: type: array description: Tag names items: type: string example: - sales - trainee responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/domain/tag/{domain}: post: summary: Remove tags of a domain description: >- Removes tags from a domain. Answers `domain_modified` when at least one tag name was sent (also when the domain did not have it), `Cannot delete item` for an empty list; errors `domain_invalid`, `access_denied`. operationId: deleteDomainTags tags: - Domains parameters: - name: domain in: path required: true description: Domain name schema: type: string example: example.org requestBody: required: true content: application/json: schema: type: array description: Tag names items: type: string example: - customer responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/alias: post: tags: - Aliases summary: Update aliases operationId: editAlias description: >- Changes the aliases in `items`; attributes that are left out keep their value, except the `goto_*` flags, which are 0 when left out. An alias that discards or learns its mail (`null@localhost`, `spam@localhost`, `ham@localhost`) therefore needs its `goto_*` flag in every edit: without it, the current destination fails the address check (`goto_invalid`), nothing is saved and the answer still contains `alias_modified`. Per alias: `alias_modified` on success; `alias_invalid` for an unknown alias or one of a domain the caller does not manage; `alias_group_managed` for the distribution address of a mailbox group (change it in the group). A new `address` is checked like in add/alias (`access_denied`, `alias_invalid`, `is_alias_or_mailbox`, `domain_not_found`, `is_spam_alias`); send-as grants of mailbox groups follow the new address. Destinations that are no e-mail address (`goto_invalid`), external while external destinations are disabled (`external_goto_denied`) or equal to the alias (`alias_goto_identical`) are dropped; if none remains, nothing is saved. Comments are not limited to 160 characters here. `expand_alias` does not change the alias: it creates the missing copies of it in the alias domains of its domain (same destinations, active, visible in SOGo, `sender_allowed` 0) and answers `alias_modified`, or warns `is_not_primary_alias` for an alias that is itself in an alias domain. requestBody: content: application/json: schema: type: object properties: items: description: IDs or addresses of the aliases, a single value or a list oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: address: type: string description: New alias address, `@example.org` for a catch-all goto: type: string description: >- New destination addresses, separated by spaces, commas, semicolons or line breaks; replaces the current ones. Ignored if a `goto_*` flag is 1. goto_null: type: [boolean, integer, string] description: 1 = discard the mail silently default: 0 goto_spam: type: [boolean, integer, string] description: 1 = learn the mail as spam default: 0 goto_ham: type: [boolean, integer, string] description: 1 = learn the mail as ham default: 0 active: type: [boolean, integer, string] description: 1 = active, 0 = inactive sogo_visible: type: [boolean, integer, string] description: 1 = selectable sender in SOGo internal: type: [boolean, integer, string] description: 1 = only reachable from its own domain and its alias domains sender_allowed: type: [boolean, integer, string] description: 1 = the destination mailboxes may send as this alias public_comment: type: string description: Comment shown to the users private_comment: type: string description: Comment for administrators expand_alias: type: [boolean, integer] description: >- true or 1 = create the missing copies of the alias in the alias domains; all other attributes are ignored required: - items - attr example: items: - 12 attr: goto: anna@example.org,ben@example.org,carla@example.org active: "1" public_comment: Sales team responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/api-key: post: summary: Edit API keys description: >- Changes API keys; attributes that are not sent keep their value. `regen_key` gives a key a new random value: clients using the old value are locked out. Service keys from mailcow.conf (API_KEY, API_KEY_READ_ONLY) cannot be changed here. One message per key. Success: `api_key_modified`. Errors: `access_denied` (no key with this ID), `api_key_service_locked`, `api_key_access_invalid`, `api_key_description_invalid`, `api_key_network_invalid`, `ip_list_empty`. operationId: editApiKey tags: - API Keys requestBody: required: true content: application/json: schema: type: object properties: items: description: ID of the API key, or a list of IDs oneOf: - type: [integer, string] - type: array items: type: [integer, string] attr: allOf: - $ref: "#/components/schemas/ApiKeyAttributes" - type: object properties: regen_key: type: [integer, string, boolean] description: 1 = generate a new key value default: 0 required: - items - attr example: items: - 3 attr: allow_from: - 192.0.2.10 - 198.51.100.0/24 regen_key: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/domain: post: summary: Edit domains description: >- Changes domains. With an API key (admin) every attribute can be set; quotas are in MiB, attributes not sent keep their value (`aliases`, `maxquota` and `quota` keep it also for 0 or an empty value). `relay_all_recipients` = 1 sets `backupmx`, `relay_unknown_only` = 1 sets both. `tags` are added to the existing tags (remove tags with delete/domain/tag); a tag the domain has already ends the request without an answer after the other values were saved. A domain admin (UI session) can only change `description` (with the ACL `domain_desc`), `gal` and `tags`. Rate limits are set with edit/rl-domain. Errors per domain: `domain_invalid`, `mailbox_defquota_exceeds_mailbox_maxquota`, `defquota_empty`, `mailbox_quota_exceeds_domain_quota`, `maxquota_empty`, `max_quota_in_use`, `domain_quota_m_in_use`, `mailboxes_in_use`, `aliases_in_use`; success `domain_modified`. operationId: editDomain tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Domain name or list of domain names oneOf: - type: string - type: array items: type: string attr: type: object properties: description: type: string description: Description aliases: type: [integer, string] description: Max. number of aliases mailboxes: type: [integer, string] description: Max. number of mailboxes defquota: type: [integer, string] description: Default quota of new mailboxes in MiB maxquota: type: [integer, string] description: Max. quota per mailbox in MiB quota: type: [integer, string] description: Quota of the whole domain in MiB active: type: [integer, string] enum: [0, 1, "0", "1"] description: Domain active gal: type: [integer, string] enum: [0, 1, "0", "1"] description: Global address list in SOGo backupmx: type: [integer, string] enum: [0, 1, "0", "1"] description: Relay this domain (backup MX) relay_all_recipients: type: [integer, string] enum: [0, 1, "0", "1"] description: Relay all recipients relay_unknown_only: type: [integer, string] enum: [0, 1, "0", "1"] description: Relay non-existing mailboxes only relayhost: type: [integer, string] description: >- ID of the sender-dependent transport (get/relayhost/all) the domain sends through, 0 = none tags: type: array items: type: string description: Tags to add example: items: - example.org attr: description: Example Org aliases: 400 mailboxes: 20 defquota: 3072 maxquota: 10240 quota: 51200 active: 1 gal: 1 backupmx: 0 relay_all_recipients: 0 relay_unknown_only: 0 relayhost: 0 tags: - premium responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/domain/footer: post: summary: Set the domain-wide footer description: >- Sets the footer that is added to all outgoing mails of addresses of the domains. The footer is replaced as a whole: values not sent are emptied. The footer texts can use `{= auth_user =}`, `{= from_user =}`, `{= from_name =}`, `{= from_addr =}`, `{= from_domain =}` and custom mailbox attributes like `{= foo =}`. Entries of `exclude` that are no existing mailbox, alias or alias domain are reported with `username_invalid` and left out. Success `domain_footer_modified`, errors `domain_invalid`, `access_denied` (both stop the call). operationId: editDomainFooter tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Domain name or list of domain names oneOf: - type: string - type: array items: type: string attr: type: object properties: html: type: string default: "" description: HTML footer plain: type: string default: "" description: Plain text footer skip_replies: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: No footer in replies exclude: description: >- Mailboxes, aliases (addresses) and alias domains that get no footer oneOf: - type: string - type: array items: type: string example: items: - example.org attr: html: "

Example Org · {= from_name =}

" plain: "Example Org - {= from_name =}" skip_replies: 1 exclude: - noreply@example.org - example.com responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/fail2ban: post: summary: Edit Fail2Ban settings, bans and filters description: >- Changes Fail2Ban (admins only, an API key may). What happens depends on `attr.action`: - `unban`, `whitelist`, `blacklist` act on the networks in `items`: queue the unban; add to the allowlist (and unban); add to the denylist (not `0.0.0.0/0` or the mailcow networks). Answer `object_modified` per network; `network_host_invalid` (danger) for an invalid network with `whitelist`/`blacklist` (`unban` skips it silently). - `unban-all` queues all active bans for unbanning (the denylist stays). Answer `f2b_modified`. - `edit-regex` replaces the regex filters with the list in `attr.regex` (numbered from 1). Answer `f2b_modified`. - `reset-regex` restores the default filters and waits up to 15 seconds for netfilter (`reset_f2b_regex` (danger) when it takes longer). Send a non-empty `items` (the UI sends `["self"]`); the answer is `object_modified` per item. - Without `action` the settings are saved. Values that are not sent keep their value, except `ban_time_increment` and `manage_external`, which become 0, and `whitelist`/`blacklist` replace the lists. Values are clamped: `ban_time`, `max_ban_time` at least 60, `max_attempts`, `retry_window` at least 1, `netban_ipv4` 8-32, `netban_ipv6` 8-128. Answer `f2b_modified`, `network_host_invalid` (danger) for every entry of the lists that is left out. An `unban`, `whitelist`, `blacklist` or `reset-regex` without `items` saves the settings like a call without `action` (and so turns off `ban_time_increment` and `manage_external`); an unknown `action` with `items` answers `object_modified` without changing anything. `access_denied` for non-admins. operationId: editFail2Ban tags: - Fail2Ban requestBody: content: application/json: schema: type: object properties: items: description: Networks or IPs for `unban`, `whitelist`, `blacklist` (single value or list) oneOf: - type: array items: type: string - type: string attr: type: object properties: action: type: string enum: - unban - whitelist - blacklist - unban-all - edit-regex - reset-regex regex: type: array items: type: string description: "`edit-regex`: the regex filters; the first group must match the IP" ban_time: type: [integer, string] description: Ban time in seconds (min. 60) ban_time_increment: type: [boolean, integer, string] description: 1 = the ban time grows with every ban of a network; not sent = 0 max_ban_time: type: [integer, string] description: Maximum ban time in seconds (min. 60) max_attempts: type: [integer, string] description: Failed logins within `retry_window` that lead to a ban (min. 1) retry_window: type: [integer, string] description: Window in seconds for `max_attempts` (min. 1) netban_ipv4: type: [integer, string] description: IPv4 prefix length that is banned (8-32) netban_ipv6: type: [integer, string] description: IPv6 prefix length that is banned (8-128) whitelist: type: string description: Allowlist, networks or host names separated by space, comma, semicolon or newline; replaces the list blacklist: type: string description: Denylist, networks separated by space, comma, semicolon or newline; replaces the list manage_external: type: [boolean, integer, string] description: 1 = netfilter only keeps the banlist and sets no rules (block with the banlist URL externally); not sent = 0 required: - attr examples: settings: summary: Save the settings value: items: [] attr: ban_time: "1800" ban_time_increment: "1" max_ban_time: "86400" max_attempts: "10" retry_window: "600" netban_ipv4: "32" netban_ipv6: "128" whitelist: |- 192.0.2.0/24 mail.example.org blacklist: 198.51.100.66/32 manage_external: "0" unban: summary: Unban a network value: items: - 203.0.113.7/32 attr: action: unban regex: summary: Replace the regex filters value: items: - self attr: action: edit-regex regex: - "^mailcow UI: Invalid password for .+ by ([0-9a-f\\.:]+)$" - "^Rspamd UI: Invalid password by ([0-9a-f\\.:]+)$" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/mailbox: post: tags: - Mailboxes summary: Update mailboxes operationId: editMailbox description: >- Changes the given mailboxes. Fields that are not sent keep their value, except `attribute_hash`, which is cleared. Admins and domain admins (for their domains); a mailbox user gets `access_denied`. Some fields need an ACL of domain admins and are silently ignored without it: `sogo_access` (sogo_access), the protocol fields (protocol_access), `relayhost` (mailbox_relayhost), `extended_sender_acl` (extend_sender_acl, error `extended_sender_acl_denied`); API keys have all ACLs. Messages per mailbox: `mailbox_modified` or `username_invalid`, `access_denied`, `mailbox_quota_left_exceeded`, `mailbox_quota_exceeded`, `password_complexity`, `password_mismatch`, `sender_acl_invalid`, `extra_acl_invalid`, `extra_acl_invalid_domain`. Quota 0 (unlimited) for a mailbox with a quota needs the `unlimited_quota` ACL (`unlimited_quota_acl`, stops the call). requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: $ref: "#/components/schemas/MailboxEdit" example: items: - john.doe@example.org attr: name: John Doe quota: 5120 active: 1 protocol_access: - imap - smtp - sieve - dav sender_acl: - sales@example.org - "@example.com" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/mailbox/custom-attribute: post: tags: - Mailboxes summary: Set custom attributes of mailboxes operationId: editMailboxCustomAttributes description: >- Replaces the custom attributes (free key/value pairs, e.g. for identity provider mappings) of the given mailboxes: `attribute[i]` gets `value[i]`, attributes that are not sent are removed; spaces in attribute names are removed. Both lists must have the same length. Admins and domain admins (for their domains). Messages per mailbox: `mailbox_modified`, `username_invalid`, `access_denied`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object properties: attribute: description: Attribute names (a single string is one name) oneOf: - type: string - type: array items: type: string value: description: Values, in the order of `attribute` oneOf: - type: string - type: array items: type: string example: items: - john.doe@example.org attr: attribute: - department - employee_id value: - Sales - "4711" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/mailbox-group: post: summary: Update mailbox groups description: >- Applies `attr` to every group in `items`, attributes that are not sent keep their value. The domain of a group cannot be changed. `members` replaces the manual members the caller may access; members added by the identity provider sync (`source` idp) stay and cannot be removed here (`group_member_idp`). Admins (every read/write API key) may edit every group; domain admins need the `mailbox_groups` ACL, and groups with send-as addresses or grants outside their domains are read-only for them. Answers one message per id: `group_modified` (plus warnings as for `add`) or the error of that group (`access_denied` for unknown ids and ids that are no number, the errors listed for `add`). operationId: editMailboxGroups tags: - Groups requestBody: content: application/json: schema: type: object required: - items - attr properties: items: description: >- Group ids, a list or a single id. An `id` inside `attr` replaces `items`. oneOf: - type: array items: type: - integer - string - type: - integer - string attr: allOf: - $ref: "#/components/schemas/MailboxGroupAttributes" - type: object properties: reapply_template: type: - boolean - integer - string description: >- Admins only: apply the effective templates to all members again, also when nothing changed default: 0 example: items: - 7 attr: add_members: - carol@example.org remove_members: - bob@example.org sogo_grants: - owner: shared@example.org type: Calendar folder: personal access: write subscribe: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/identity-provider-sync: post: summary: Request an identity sync run description: >- Admins only (every read/write API key). Queues a sync run of the Identity Provider (Keycloak and LDAP); the identity sync cron starts it within a minute, see `GET /api/v1/get/identity-provider/sync-status`. An identical pending request is reused. `items` is not used. Answers `iam_sync_requested`, or `iam_sync_mode_invalid`, `iam_sync_unavailable` (no Keycloak or LDAP provider configured), `access_denied`. operationId: requestIdentitySync tags: - Identity Provider requestBody: content: application/json: schema: type: object required: - attr properties: items: description: Not used attr: type: object properties: mode: type: string description: >- `preview` lists the changes without applying them, `run` applies them, `force` applies them without the change limit (`offboarding_limit`) enum: - preview - run - force default: run example: attr: mode: preview responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/mailq: post: summary: Act on the mail queue description: >- Runs a Postfix queue command (admins only, an API key may): - `hold` (`postsuper -h`), `unhold` (`postsuper -H`) and `deliver` (`postqueue -i`) act on the queue IDs in `items`. - `flush` (`postqueue -f`) tries to deliver the whole queue, `super_delete` (`postsuper -d ALL`) deletes every queued message. Both ignore `items`. Messages: `queue_command_success`; `Error: command failed: ...` (danger) with the output of the command; `unknown` (danger) when the command gave no answer; `access_denied` for non-admins. An unknown `action`, or `hold`/`unhold`/`deliver` without `items`, does nothing and is answered with `Task completed`. Queue IDs that are not hexadecimal are skipped. operationId: editMailQueue tags: - Queue Manager requestBody: content: application/json: schema: type: object properties: items: description: Queue ID or list of queue IDs (for `hold`, `unhold`, `deliver`) oneOf: - type: array items: type: string - type: string attr: type: object properties: action: type: string enum: - hold - unhold - deliver - flush - super_delete required: - action required: - attr examples: hold: summary: Hold two messages value: items: - B98C6260CA1 - 4C1E8260D02 attr: action: hold flush: summary: Flush the whole queue value: items: [] attr: action: flush responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/pushover: post: tags: - Mailbox user settings summary: Set the Pushover notifications of mailboxes operationId: editPushover description: >- Saves the Pushover settings (push notification on new mail) of the mailboxes, or removes them with `delete: "true"`. Needs the user ACL `pushover` (domain admins: their own ACL `pushover`, off by default). API keys may change any mailbox, a mailbox user only its own (the UI sends its address as `items`). `key` and `token` must be sent the first time; later, empty fields keep their value. Messages per mailbox: `pushover_settings_edited` or `access_denied`, `pushover_key`, `pushover_token`, "Invalid regex". requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object properties: key: type: string description: Pushover user key, 30 letters and digits token: type: string description: Pushover application token, 30 letters and digits active: type: [integer, string] enum: [0, 1, "0", "1"] title: type: string description: Title of the notification (default "Mail") text: type: string description: Text of the notification senders: type: string description: Only notify for these senders (addresses separated by comma, space or semicolon; invalid ones are dropped) senders_regex: type: string description: Only notify for senders matching this regular expression evaluate_x_prio: type: [integer, string] enum: [0, 1, "0", "1"] description: 1 sends high-priority mails as high-priority notifications (0 when not sent) only_x_prio: type: [integer, string] enum: [0, 1, "0", "1"] description: 1 notifies for high-priority mails only (0 when not sent) sound: type: string description: Pushover sound name (empty when not sent) delete: type: string enum: - "true" description: '"true" removes the Pushover settings, other fields are ignored' example: items: - john.doe@example.org attr: key: uQiRzpo4DXghDmr9QzzfQu27cmVRsG token: azGDORePK8gMaC0QOYAMyEEuzJnyUi active: 1 title: New mail text: You've got mail senders: boss@example.org evaluate_x_prio: 1 only_x_prio: 0 sound: pushover responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/quarantine_notification: post: tags: - Quarantine summary: Set the quarantine notification interval of mailboxes operationId: editQuarantineNotification description: >- How often the mailbox user gets a summary of quarantined mail. Needs the user ACL `quarantine_notification`. API keys may change any mailbox, a mailbox user only its own. `quarantine_notification` must be sent: without it the call fails with an empty answer. Messages per mailbox: `mailbox_modified` or `access_denied` (also for an invalid value). requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object required: - quarantine_notification properties: quarantine_notification: type: string enum: - never - hourly - daily - weekly example: items: - john.doe@example.org attr: quarantine_notification: daily responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/syncjob: post: summary: Update sync jobs description: >- Applies `attr` to every job in `items`; attributes that are not sent keep their value, an empty `password1` or `user1` keeps the stored one. The local mailbox (`username`) cannot be changed. Needs the `syncjobs` ACL and access to the local mailbox of each job (admins and API keys: every job). The source is only checked again when `source_id` changes. Sending `last_run` or `success` (any value except null) resets that field. `prio` is set by admins only. Answers one message per id: `mailbox_modified` with the local mailbox, or the error of that job (`access_denied` for unknown jobs and ids that are no number, otherwise the errors of `add`). operationId: editSyncJobs tags: - Sync jobs requestBody: content: application/json: schema: type: object required: - items - attr properties: items: description: >- Job ids, a list or a single id. An `id` inside `attr` replaces `items`. oneOf: - type: array items: type: - integer - string - type: - integer - string attr: allOf: - $ref: "#/components/schemas/SyncJobAttributes" - type: object properties: last_run: description: Any value except null resets the last run time (the job is due again) success: description: Any value except null resets the result of the last run prio: type: - integer - string description: >- Admins only: queue priority, higher runs first (see `edit/syncjob/run_next`) example: items: - 12 - 13 attr: active: 0 mins_interval: 60 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/syncjob/run_next: post: summary: Run sync jobs next description: >- Admins only (every read/write API key): moves the jobs to the front of the queue. Each job gets a `prio` above the current maximum, `last_run` and `success` are cleared, so the runner starts them next. Values in `items` that are no number are ignored, `attr` is not used. Answers `imapsync_job_running` (danger) for each job that is running right now and `imapsync_run_next_done` with the number of moved jobs. When no job was moved and none is running (e.g. unknown ids) the answer is `{"type": "error", "msg": "Cannot edit item"}`; without valid ids `access_denied`. operationId: runSyncJobsNext tags: - Sync jobs requestBody: content: application/json: schema: type: object required: - items properties: items: description: Job ids type: array items: type: - integer - string attr: description: Not used type: object example: items: - 12 attr: {} responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/syncjob_source: post: summary: Update sync sources description: >- Applies `attr` to every source in `items` (ids that are no number are skipped); attributes that are not sent keep their value, an empty `oauth_client_secret` keeps the stored secret. Domains and users are only replaced when `scope` is sent. Needs the `syncjobs` ACL and edit rights on the source (admins and API keys: every source; others the sources they created, domain admins also those of their mailboxes). Changing the server or the OAuth2 client drops the stored tokens. Changing the server or the OAuth2 client, or turning `verify_cert` off, needs access to every mailbox whose jobs use the source (`imapsync_source_used_by_others`). A client_credentials source fetches a new token while it is saved. Answers one message per id: `imapsync_source_modified` (plus `imapsync_source_token_refresh_failed` when the new token could not be fetched), or the error of that source (`access_denied` for unknown ids, the errors of `add`). operationId: editSyncJobSources tags: - Sync jobs requestBody: content: application/json: schema: type: object required: - items - attr properties: items: description: >- Source ids, a list or a single id. An `id` inside `attr` replaces `items`. oneOf: - type: array items: type: - integer - string - type: - integer - string attr: $ref: "#/components/schemas/SyncJobSourceAttributes" example: items: - 3 attr: description: Migration finished in December scope: user users: - alice@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/syncjob_source/refresh_token: post: summary: Refresh the token of a sync source description: >- Fetches a new OAuth2 token for a XOAUTH2 client_credentials source now (the cron refreshes tokens before they expire). Only the first id of `items` is used, `attr` is not used. Needs the `syncjobs` ACL and edit rights on the source. Answers `imapsync_source_token_refreshed`, or `imapsync_source_refresh_unsupported` (other sources), `imapsync_source_token_refresh_failed` (with the provider error), `access_denied` (unknown id, id that is no number, no edit rights). operationId: refreshSyncJobSourceToken tags: - Sync jobs requestBody: content: application/json: schema: type: object required: - items properties: items: description: Source id (the first entry is used) type: array items: type: - integer - string attr: description: Not used type: object example: items: - 5 attr: {} responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/imapsync_settings: post: summary: Update global sync job settings description: >- Admins only (every read/write API key). Sets how many sync jobs run at the same time and the bandwidth limit of every sync job process (a lower per-job `maxbytespersecond` still applies). `items` is not used. Answers `max_parallel_saved`, or `imapsync_max_parallel_invalid`, `imapsync_max_kb_per_second_invalid`, `access_denied`. operationId: editSyncJobSettings tags: - Sync jobs requestBody: content: application/json: schema: type: object required: - attr properties: items: description: Not used attr: type: object required: - max_parallel properties: max_parallel: type: - integer - string description: Sync jobs running at the same time, 1-32 minimum: 1 maximum: 32 max_kb_per_second: type: - integer - string description: >- Bandwidth limit per sync job process in KB/s, 0-10485760 (stored as bytes/s); 0, empty or not sent = unlimited default: 0 minimum: 0 maximum: 10485760 example: attr: max_parallel: 4 max_kb_per_second: 2000 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/user-acl: post: tags: - Mailboxes summary: Set the ACL of mailboxes operationId: editUserAcl description: >- Sets the user ACL (what a mailbox user may change in the UI) of the given mailboxes: listed ACLs are switched on, all others off. Admins and domain admins (for their domains); a domain admin cannot change ACLs that it does not have itself. Mailbox users get `access_denied`. Success: `acl_saved` per mailbox. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object properties: user_acl: description: ACLs to switch on (a single string is one ACL); an empty list switches all off oneOf: - $ref: "#/components/schemas/UserAclName" - type: array items: $ref: "#/components/schemas/UserAclName" example: items: - john.doe@example.org attr: user_acl: - spam_alias - tls_policy - spam_score - spam_policy - delimiter_action - eas_reset - pushover - quarantine - quarantine_notification - app_passwds - pw_reset responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/alias/{id}: get: tags: - Aliases summary: Get aliases operationId: getAliases description: >- `all` lists the aliases of every domain and alias domain the caller manages (an API key: all), an ID or address returns that alias. Mailbox addresses (alias rows pointing to themselves) are not listed. `{}` if there is none. parameters: - name: id in: path required: true description: "`all`, or the ID or address of an alias" schema: type: string example: all responses: "200": description: The list of aliases (`all`) or one alias content: application/json: schema: anyOf: - type: array items: $ref: "#/components/schemas/Alias" - $ref: "#/components/schemas/Alias" examples: all: summary: get/alias/all value: - in_primary_domain: "" id: 12 domain: example.org public_comment: Sales team private_comment: null goto: anna@example.org,ben@example.org address: sales@example.org is_catch_all: 0 internal: 0 active: 1 active_int: 1 sogo_visible: 1 sogo_visible_int: 1 sender_allowed: 1 group_id: null group_name: null created: "2026-10-26 14:43:58" modified: "2026-11-08 20:38:01" - in_primary_domain: example.org id: 20 domain: example.net public_comment: "" private_comment: "" goto: null@localhost address: "@example.net" is_catch_all: 1 internal: 0 active: 1 active_int: 1 sogo_visible: 0 sogo_visible_int: 0 sender_allowed: 0 group_id: null group_name: null created: "2026-11-02 09:12:40" modified: null one: summary: get/alias/12 value: in_primary_domain: "" id: 12 domain: example.org public_comment: Sales team private_comment: null goto: anna@example.org,ben@example.org address: sales@example.org is_catch_all: 0 internal: 0 active: 1 active_int: 1 sogo_visible: 1 sogo_visible_int: 1 sender_allowed: 1 group_id: null group_name: null created: "2026-10-26 14:43:58" modified: "2026-11-08 20:38:01" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/alias/all/{domains}: get: tags: - Aliases summary: Get the aliases of domains operationId: getAliasesByDomain description: >- Lists the aliases of the named domains or alias domains. Domains the caller does not manage are skipped. `{}` if there is none. parameters: - name: domains in: path required: true description: Comma-separated domain names schema: type: string example: example.org,example.net responses: "200": description: The list of aliases content: application/json: schema: type: array items: $ref: "#/components/schemas/Alias" example: - in_primary_domain: "" id: 12 domain: example.org public_comment: Sales team private_comment: null goto: anna@example.org,ben@example.org address: sales@example.org is_catch_all: 0 internal: 0 active: 1 active_int: 1 sogo_visible: 1 sogo_visible_int: 1 sender_allowed: 1 group_id: null group_name: null created: "2026-10-26 14:43:58" modified: "2026-11-08 20:38:01" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/time_limited_aliases/{mailbox}: get: tags: - Mailbox user settings summary: Get the time-limited aliases of a mailbox operationId: getTimeLimitedAliases description: >- The time-limited aliases of the mailbox that are still valid or permanent. `{}` when there are none or the caller has no access (a mailbox user: other mailboxes). If `{mailbox}` is no address, the session user's aliases are returned (nothing for API keys). parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: List of aliases content: application/json: schema: type: array items: $ref: "#/components/schemas/TimeLimitedAlias" example: - address: kahd.osuwe@example.org goto: john.doe@example.org description: Online shop registration validity: 1795699200 created: "2026-11-10 09:00:00" modified: null permanent: 0 "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/time_limited_alias: post: tags: - Mailbox user settings summary: Update time-limited aliases operationId: editTimeLimitedAlias description: >- Sets a new expiry (`validity` hours from now) or makes the aliases permanent. Needs the user ACL `spam_alias` and access to the target mailbox (API keys: any, mailbox users: their own). Aliases are skipped silently when neither `validity` nor a true `permanent` is sent. Messages per alias: `mailbox_modified` (without the address) or `access_denied`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Alias address or list of addresses oneOf: - type: string - type: array items: type: string attr: type: object properties: validity: type: [integer, string] description: Hours from now until the alias expires (not range-checked); ends a permanent state permanent: type: [boolean, integer, string] description: true makes the alias permanent (wins over `validity`) example: items: - kahd.osuwe@example.org attr: validity: 24 permanent: 0 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/time_limited_alias: post: tags: - Mailbox user settings summary: Delete time-limited aliases operationId: deleteTimeLimitedAlias description: >- Deletes time-limited aliases. Needs the user ACL `spam_alias` and access to the target mailbox (API keys: any, mailbox users: their own). Messages per alias: `mailbox_modified` or `access_denied`. requestBody: required: true content: application/json: schema: type: array description: Alias addresses items: type: string example: - kahd.osuwe@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/pushover-test: post: tags: - Mailbox user settings summary: Verify the Pushover credentials of mailboxes operationId: testPushover description: >- Checks the stored Pushover key and token of each mailbox with the Pushover API; `attr` is not used. Same access rules as `edit/pushover`. Messages per mailbox: "Pushover API OK (200): ..." (success) or "Pushover API ERR (code): ..." (danger), `access_denied`; a mailbox without settings stops the call with `pushover_credentials_missing`. requestBody: required: true content: application/json: schema: type: object required: - items properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object description: Not used example: items: - john.doe@example.org attr: {} responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/delimiter_action: post: tags: - Mailbox user settings summary: Set the handling of tagged mail operationId: editDelimiterAction description: >- What happens to mail for `user+tag@example.org`: moved to the subfolder "tag" (`subfolder`), "[tag]" added to the subject (`subject`), or nothing (`none`, also for any other value). Needs the user ACL `delimiter_action`. API keys may change any mailbox, a mailbox user only its own. Messages per mailbox: `mailbox_modified` or `access_denied`, `redis_error`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object properties: tagged_mail_handler: type: string enum: - subfolder - subject - none example: items: - john.doe@example.org attr: tagged_mail_handler: subfolder responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/tls_policy: post: tags: - Mailbox user settings summary: Set the TLS policy of mailboxes operationId: editTlsPolicy description: >- Enforces TLS for incoming and/or outgoing mail of the mailboxes; a field that is not sent keeps its value. Needs the user ACL `tls_policy`. API keys may change any mailbox, a mailbox user only its own. Messages per mailbox: `mailbox_modified` or `access_denied`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object properties: tls_enforce_in: type: [integer, string] enum: [0, 1, "0", "1"] description: 1 accepts incoming mail only over TLS tls_enforce_out: type: [integer, string] enum: [0, 1, "0", "1"] description: 1 sends outgoing mail only over TLS example: items: - john.doe@example.org attr: tls_enforce_in: 1 tls_enforce_out: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/spam-score: get: tags: - Mailbox user settings summary: Get the default spam score operationId: getDefaultSpamScore description: >- The spam score thresholds of the session user; for API keys, which have no mailbox, the server default from the Rspamd actions ("add header" and "reject"; "5,15" if Rspamd cannot be asked). responses: "200": description: Thresholds content: application/json: schema: $ref: "#/components/schemas/SpamScore" example: score: "8,15" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/api-key/{id}: get: summary: Get API key description: One API key including its key value. A read-only API key gets `{}`. operationId: getApiKey tags: - API Keys parameters: - name: id in: path required: true description: ID of the API key schema: type: integer example: 3 responses: "200": description: The API key, `{}` if there is none with this ID or for a read-only API key content: application/json: schema: $ref: "#/components/schemas/ApiKey" example: id: 3 description: provisioning access: rw api_key: 1B7E2C-94F0A3-6D5B18-C2E7F4-03A9D6 allow_from: - 192.0.2.10 - 2001:db8::/64 skip_ip_check: 0 service: 0 active: 1 created: "2026-11-02 09:12:44" modified: "2026-11-05 16:40:02" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/api-key/all: get: summary: Get API keys description: >- Lists all API keys including their key values, service keys from mailcow.conf first. A read-only API key gets `{}`: it may not see the keys. operationId: getApiKeys tags: - API Keys responses: "200": description: The API keys, `{}` if there are none or for a read-only API key content: application/json: schema: type: array items: $ref: "#/components/schemas/ApiKey" example: - id: 1 description: API_KEY access: rw api_key: 8F2D41-0C9A7B-5E3F12-AB46C0-77D1E9 allow_from: - 192.0.2.0/24 skip_ip_check: 0 service: 1 active: 1 created: "2026-09-21 10:20:00" modified: null - id: 3 description: provisioning access: rw api_key: 1B7E2C-94F0A3-6D5B18-C2E7F4-03A9D6 allow_from: - 192.0.2.10 - 2001:db8::/64 skip_ip_check: 0 service: 0 active: 1 created: "2026-11-02 09:12:44" modified: "2026-11-05 16:40:02" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/app-passwd/all/{mailbox}: get: tags: - App Passwords summary: Get the app passwords of a mailbox operationId: getAppPasswords description: >- The app passwords of the mailbox, including the password hashes. `{}` if there are none or the caller has no access to the mailbox (a mailbox user: other mailboxes). parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: List of app passwords content: application/json: schema: type: array items: $ref: "#/components/schemas/AppPassword" example: - id: 23 name: Thunderbird mailbox: john.doe@example.org domain: example.org password: "{BLF-CRYPT}$2y$10$..." created: "2026-11-08 20:38:01" modified: null imap_access: 1 smtp_access: 1 dav_access: 0 eas_access: 0 pop3_access: 0 sieve_access: 1 active: 1 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/app-passwd/all: get: tags: - App Passwords summary: Get the app passwords of the logged-in mailbox operationId: getOwnAppPasswords description: >- The app passwords of the session user. Meant for a mailbox user session; an API key has no mailbox and gets `{}` (use `get/app-passwd/all/{mailbox}`). responses: "200": description: List of app passwords content: application/json: schema: type: array items: $ref: "#/components/schemas/AppPassword" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/app-passwd/{id}: get: tags: - App Passwords summary: Get an app password (broken) operationId: getAppPassword description: >- Does not work: json_api.php reads `$object['id']` from the path segment, which is a string, so the request ends with an error and an empty body. Use `get/app-passwd/all/{mailbox}`. parameters: - name: id in: path required: true description: App password ID schema: type: integer example: 23 responses: "200": description: Empty body "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/app-passwd: post: tags: - App Passwords summary: Update app passwords operationId: editAppPassword description: >- Changes app passwords by ID; fields that are not sent keep their value. Needs the user ACL `app_passwds`. The app passwords are assigned to `username`, which defaults to the session user: API keys must send the mailbox the app passwords belong to (without it the backend tries to assign them to "API", the database refuses that and the answer is empty); a mailbox user may leave it out. Messages per ID: `object_modified`, `app_passwd_id_invalid`, `password_complexity`, `password_mismatch`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: App password ID or list of IDs oneOf: - type: integer - type: array items: type: integer attr: type: object properties: username: type: string description: Mailbox the app passwords belong to (required for API keys) app_name: type: string app_passwd: type: string format: password description: New password; changed only together with `app_passwd2` app_passwd2: type: string format: password protocols: description: Replaces the allowed protocols type: array items: type: string enum: - imap_access - smtp_access - pop3_access - sieve_access - dav_access - eas_access active: type: [integer, string] enum: [0, 1, "0", "1"] example: items: - 23 attr: username: john.doe@example.org active: 0 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/bcc/{id}: get: tags: - Address Rewriting summary: Get BCC maps operationId: getBccMaps description: >- `all` lists the BCC maps of the domains the caller manages (an API key: all), an ID returns that map. `{}` if there is none. The answer for an ID carries an additional member `"0": null` (json_api.php appends an undefined variable). parameters: - name: id in: path required: true description: "`all` or the ID of a BCC map" schema: type: string example: all responses: "200": description: The list of BCC maps (`all`) or one BCC map content: application/json: schema: anyOf: - type: array items: $ref: "#/components/schemas/BccMap" - $ref: "#/components/schemas/BccMap" examples: all: summary: get/bcc/all value: - id: 3 local_dest: "@example.org" bcc_dest: archive@example.com active: 1 type: rcpt created: "2026-11-03 10:15:22" domain: example.org modified: null one: summary: get/bcc/3 value: id: 3 local_dest: "@example.org" bcc_dest: archive@example.com active: 1 type: rcpt created: "2026-11-03 10:15:22" domain: example.org modified: null "0": null "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/dkim/{domain}: get: summary: Get the DKIM key of a domain description: >- The DKIM public key of a domain or alias domain with the TXT record to publish at `._domainkey.`; `{}` if it has none. The private key is only answered when `SHOW_DKIM_PRIV_KEYS` is enabled in vars.local.inc.php, else `privkey` is empty. operationId: getDkimKey tags: - DKIM parameters: - name: domain in: path required: true description: Domain or alias domain name schema: type: string example: example.org responses: "200": description: The DKIM key content: application/json: schema: $ref: "#/components/schemas/DkimKey" example: pubkey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxV3...IDAQAB length: "2048" dkim_txt: v=DKIM1;k=rsa;t=s;s=email;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxV3...IDAQAB dkim_selector: dkim privkey: "" "401": $ref: "#/components/responses/Unauthorized" /api/v1/add/dkim_import: post: summary: Import a DKIM key description: >- Imports an RSA private key as the DKIM key of a domain; the public key is derived from it. Admins only. The domain name must be valid but is not checked against the domains of mailcow. The old key of the domain is removed first, so a successful import answers `dkim_removed` and `dkim_added`. Errors `access_denied`, `private_key_error`, `dkim_domain_or_sel_invalid`, `dkim_domain_or_sel_exists` (a key exists and `overwrite_existing` is not set). operationId: importDkimKey tags: - DKIM requestBody: required: true content: application/json: schema: type: object required: - domain - private_key_file properties: domain: type: string description: Domain name dkim_selector: type: string default: dkim description: Selector (letters, digits, `-`, `_`, `.`) private_key_file: type: string description: The private key in PEM format (the text, not a file upload) overwrite_existing: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: Replace an existing key of the domain example: domain: example.org dkim_selector: dkim2026 private_key_file: "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASC...\n-----END PRIVATE KEY-----" overwrite_existing: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/domain-admin/all: get: summary: Get domain administrators description: Lists all domain administrators with their details. operationId: getDomainAdmins tags: - Domain admin responses: "200": description: The domain administrators, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/DomainAdmin" example: - username: domainadmin1 tfa_active: 0 tfa_active_int: 0 active: 1 active_int: 1 created: "2026-09-21 10:24:48" attributes: force_tfa: "0" force_pw_update: "0" selected_domains: - example.org unselected_domains: - example.com "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/domain-admin/{username}: get: summary: Get domain administrator description: Details of one domain administrator. operationId: getDomainAdmin tags: - Domain admin parameters: - name: username in: path required: true description: Username of the domain administrator schema: type: string example: domainadmin1 responses: "200": description: The domain administrator, `{}` if there is none of this name content: application/json: schema: $ref: "#/components/schemas/DomainAdmin" example: username: domainadmin1 tfa_active: 0 tfa_active_int: 0 active: 1 active_int: 1 created: "2026-09-21 10:24:48" attributes: force_tfa: "0" force_pw_update: "0" selected_domains: - example.org - example.com unselected_domains: "" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/domain/{id}: get: summary: Get domains description: >- `all` answers the list of all domains, a domain name answers that domain. An alias domain name answers its target domain. An API key sees every domain, a domain admin (UI session) only the domains assigned to it. Quotas are in bytes. `{}` when nothing is found, also for `all` without domains. operationId: getDomains tags: - Domains parameters: - name: id in: path required: true description: "`all` or a domain name" schema: type: string example: all - name: tags in: query required: false description: >- Only with `all`: comma-separated tag names, answers only the domains with a tag that contains one of them schema: type: string example: customer,premium responses: "200": description: The list of domains (`all`) or one domain content: application/json: schema: oneOf: - type: array items: $ref: "#/components/schemas/Domain" - $ref: "#/components/schemas/Domain" example: - max_new_mailbox_quota: 10632560640 def_new_mailbox_quota: 3221225472 quota_used_in_domain: "104857600" bytes_total: "79324302" msgs_total: "132" mboxes_in_domain: 3 mboxes_left: 7 domain_name: example.org domain_h_name: example.org description: Example Org max_num_aliases_for_domain: 400 max_num_mboxes_for_domain: 10 def_quota_for_mbox: 3221225472 max_quota_for_mbox: 10737418240 max_quota_for_domain: 10737418240 relayhost: "0" backupmx: 0 backupmx_int: 0 gal: 1 gal_int: 1 rl: value: "100" frame: h active: 1 active_int: 1 relay_all_recipients: 0 relay_all_recipients_int: 0 relay_unknown_only: 0 relay_unknown_only_int: 0 created: "2026-09-21 10:24:34" modified: "2026-11-06 18:15:08" aliases_in_domain: 1 aliases_left: 399 domain_admins: anna.admin tags: - customer "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/domain/template/{id}: get: summary: Get domain templates description: >- `all` answers the list of domain templates, an ID that template (`{}` if there is none; a non-numeric ID is read as 0). Admins and domain admins. The template attributes hold the quotas in bytes. operationId: getDomainTemplates tags: - Domains parameters: - name: id in: path required: true description: "`all` or a template ID" schema: type: string example: all responses: "200": description: The list of templates (`all`) or one template content: application/json: schema: oneOf: - type: array items: $ref: "#/components/schemas/DomainTemplate" - $ref: "#/components/schemas/DomainTemplate" example: - id: 1 template: Default type: domain attributes: tags: [] max_num_aliases_for_domain: 400 max_num_mboxes_for_domain: 10 def_quota_for_mbox: 3221225472 max_quota_for_mbox: 10737418240 max_quota_for_domain: 10737418240 rl_frame: s rl_value: "" active: 1 gal: 1 backupmx: 0 relay_all_recipients: 0 relay_unknown_only: 0 dkim_selector: dkim key_size: 2048 created: "2026-09-21 10:23:08" modified: null "401": $ref: "#/components/responses/Unauthorized" /api/v1/add/domain/template: post: summary: Add a domain template description: >- Adds a domain template, the defaults for new domains (see `template` of add/domain). Admins only. Quotas are sent in MiB and stored in bytes; values not sent get the defaults shown (numbers and quotas also for 0). Success `template_added`, errors `access_denied`, `template_name_invalid`, `template_exists`. operationId: addDomainTemplate tags: - Domains requestBody: required: true content: application/json: schema: allOf: - $ref: "#/components/schemas/DomainTemplateInput" - required: - template example: template: Small business max_num_aliases_for_domain: 400 max_num_mboxes_for_domain: 25 def_quota_for_mbox: 2048 max_quota_for_mbox: 5120 max_quota_for_domain: 51200 rl_value: 500 rl_frame: h active: 1 gal: 1 backupmx: 0 relay_all_recipients: 0 relay_unknown_only: 0 dkim_selector: dkim key_size: 2048 tags: - small responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/domain/template: post: summary: Edit domain templates description: >- Replaces the attributes of domain templates. Admins only. Send all attributes: one that is not sent is reset (numbers and quotas to 0, `tags` to none, `rl_frame` to `s`, `active` and `gal` to 1, `dkim_selector` to `dkim`, `key_size` to 2048). The `Default` template keeps its name. Edit one template per call: with several IDs the templates after the first get the name of the first one. Unknown IDs answer `template_id_invalid`; the call always ends with `template_modified`. operationId: editDomainTemplate tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Template ID or list of template IDs oneOf: - type: [integer, string] - type: array items: type: [integer, string] attr: $ref: "#/components/schemas/DomainTemplateInput" example: items: - 2 attr: template: Small business max_num_aliases_for_domain: 400 max_num_mboxes_for_domain: 50 def_quota_for_mbox: 2048 max_quota_for_mbox: 5120 max_quota_for_domain: 102400 rl_value: 500 rl_frame: h active: 1 gal: 1 backupmx: 0 relay_all_recipients: 0 relay_unknown_only: 0 dkim_selector: dkim key_size: 2048 tags: - small responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/domain/template: post: summary: Delete a domain template description: >- Deletes a domain template. Admins only. Only the first ID of the list is deleted. The `Default` template is never deleted; the answer is `template_removed` in every case. operationId: deleteDomainTemplate tags: - Domains requestBody: required: true content: application/json: schema: type: array description: Template IDs (only the first is used) items: type: [integer, string] example: - 2 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/search/domain: post: summary: Search domains (table of the UI) description: >- Paging, sorting and searching of domains with the server-side processing parameters of DataTables, used by the domain table of the UI. Admins (also read-only API keys) search all domains, domain admins their domains; other logins get `403` with `Insufficient permissions`. The position of an entry in `columns` (and `order[].column`) selects the database column: 2 domain name, 3 max. aliases (sorted by the number of aliases), 4 max. mailboxes (sorted by the number of mailboxes), 5 domain quota (sorted by the quota of the mailboxes), 6 storage (sorting only), 7 default mailbox quota, 8 max. mailbox quota, 10 relay domain, 14 tags (global search only), 15 active; other positions mean the domain name. `search.value` is searched (`LIKE %value%`) in the columns with `searchable: true` (positions 2 and 14 in the UI), `columns[].search.value` filters one column: the one whose position is given in `data`, else the domain name. `orderable` and `searchable` must be set (`true` or `"true"`). `columns` is required when `search.value` is set. Send `Content-Type: application/json`; without it the body is ignored and all domains are answered. A database error answers `{"error": "An SQL error occurred: ..."}` (status 200), e.g. a search in position 6. operationId: searchDomains tags: - Domains requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DomainSearchRequest" example: draw: 1 start: 0 length: 10 search: value: example order: - column: 2 dir: asc columns: - data: "" searchable: false orderable: false - data: chkbox searchable: false orderable: false - data: domain_h_name searchable: true orderable: true search: value: "" responses: "200": description: One page of domains, with the counts DataTables needs content: application/json: schema: $ref: "#/components/schemas/DomainSearchResult" example: draw: 1 recordsTotal: 12 recordsFiltered: 1 data: - max_new_mailbox_quota: 10632560640 def_new_mailbox_quota: 3221225472 quota_used_in_domain: "104857600" bytes_total: "79324302" msgs_total: "132" mboxes_in_domain: 3 mboxes_left: 7 domain_name: example.org domain_h_name: example.org description: Example Org max_num_aliases_for_domain: 400 max_num_mboxes_for_domain: 10 def_quota_for_mbox: 3221225472 max_quota_for_mbox: 10737418240 max_quota_for_domain: 10737418240 relayhost: "0" backupmx: 0 backupmx_int: 0 gal: 1 gal_int: 1 rl: false active: 1 active_int: 1 relay_all_recipients: 0 relay_all_recipients_int: 0 relay_unknown_only: 0 relay_unknown_only_int: 0 created: "2026-09-21 10:24:34" modified: "2026-11-06 18:15:08" aliases_in_domain: 1 aliases_left: 399 domain_admins: "-" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/alias-domain/{id}: get: summary: Get alias domains description: >- `all` answers the list of all alias domains, an alias domain name answers that alias domain; `{}` when nothing is found. An API key sees every alias domain, a domain admin those of its domains. operationId: getAliasDomains tags: - Domains parameters: - name: id in: path required: true description: "`all` or an alias domain name" schema: type: string example: all responses: "200": description: The list of alias domains (`all`) or one alias domain content: application/json: schema: oneOf: - type: array items: $ref: "#/components/schemas/AliasDomain" - $ref: "#/components/schemas/AliasDomain" example: - alias_domain: example.net parent_is_backupmx: 0 target_domain: example.org active: 1 active_int: 1 rl: false created: "2026-10-02 08:12:45" modified: null "401": $ref: "#/components/responses/Unauthorized" /api/v1/add/alias-domain: post: summary: Add alias domains description: >- Adds alias domains: mail to an address of the alias domain goes to the same local part in the target domain. Needs the ACL `alias_domains` (API keys have it) and access to the target domain, which must be an existing domain and no relay domain. Like add/domain this can set the rate limit and create a DKIM key for each alias domain. Per alias domain: success `aliasd_added`, errors `alias_domain_invalid` (also when the name is taken by a domain or alias domain), `aliasd_targetd_identical`, `targetd_not_found`, `targetd_relay_domain`; for the call: `access_denied`, `target_domain_invalid`. An invalid alias domain name currently ends the request without an answer. operationId: addAliasDomain tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - alias_domain - target_domain properties: alias_domain: type: string description: >- Alias domain name; several are separated by spaces, commas, semicolons or line breaks target_domain: type: string description: The domain the alias domain points to active: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: Alias domain active (inactive when not sent) rl_value: type: [integer, string] description: Rate limit, messages per `rl_frame`; empty or 0 = none rl_frame: type: string enum: [s, m, h, d] description: Rate limit time frame dkim_selector: type: string description: Selector of the DKIM key that is created key_size: type: [integer, string] description: >- RSA key size in bits of the DKIM key that is created; a key is created only when `key_size` and `dkim_selector` are sent example: alias_domain: example.net target_domain: example.org active: 1 rl_value: 100 rl_frame: h dkim_selector: dkim key_size: 2048 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/alias-domain: post: summary: Edit alias domains description: >- Changes alias domains; attributes not sent keep their value. The new target domain must be an existing domain (no alias domain) the caller has access to. Success `aliasd_modified`, errors per alias domain `alias_domain_invalid` (unknown), `target_domain_invalid`, `access_denied`. Rate limits are set with edit/rl-domain. operationId: editAliasDomain tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Alias domain name or list of alias domain names oneOf: - type: string - type: array items: type: string attr: type: object properties: active: type: [integer, string] enum: [0, 1, "0", "1"] description: Alias domain active target_domain: type: string description: New target domain example: items: - example.net attr: active: 1 target_domain: example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/alias-domain: post: summary: Delete alias domains description: >- Deletes alias domains together with their aliases, spam aliases, BCC maps and mailbox group data. Success `alias_domain_removed`, errors `domain_invalid`, `access_denied`. The rate limit of the alias domain is not removed. operationId: deleteAliasDomain tags: - Domains requestBody: required: true content: application/json: schema: type: array description: Alias domain names items: type: string example: - example.net responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/mta-sts: post: summary: Add an MTA-STS policy description: >- Adds the MTA-STS policy of a domain, published (when active) at `https://mta-sts./.well-known/mta-sts.txt`. Alias domains always publish the policy of their target domain: a policy added for an alias domain is accepted but never published. The policy ID for the `_mta-sts` TXT record is the time of the change (`YYYYMMDDhhmmss`). There is no read route: the policy is shown in the UI only. Answers `Task completed` on success; errors `access_denied` (unknown domain or no access), `version_invalid`, `mode_invalid`, `max_age_invalid`, `mx_invalid`, and the database message when the domain has a policy already (use edit/mta-sts). operationId: addMtaSts tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - domain - version - mode - mx - max_age properties: domain: type: string description: Domain or alias domain version: type: string enum: [stsv1, STSv1] description: Policy version (case-insensitive) mode: type: string enum: [enforce, testing, none] description: >- `enforce` rejects delivery without valid TLS, `testing` only reports, `none` publishes without effect mx: type: string description: >- Comma-separated MX host names the senders may deliver to, wildcards like `*.example.org` allowed max_age: type: [integer, string] description: Seconds senders may cache the policy, 1 to 31536000 active: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: Policy published (inactive when not sent) example: domain: example.org version: stsv1 mode: enforce mx: mail.example.org,mx2.example.org max_age: 86400 active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/mta-sts: post: summary: Edit MTA-STS policies description: >- Changes the MTA-STS policies of domains; attributes not sent keep their value, but `version` must always be sent (else `version_invalid`). A change of `version`, `mode`, `mx` or `max_age` gives the policy a new ID. Success `object_modified`, errors per domain `access_denied` (also for a domain without policy), `version_invalid`, `mode_invalid`, `max_age_invalid`, `mx_invalid`. operationId: editMtaSts tags: - Domains requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Domain name or list of domain names oneOf: - type: string - type: array items: type: string attr: type: object required: - version properties: version: type: string enum: [stsv1, STSv1] description: Policy version mode: type: string enum: [enforce, testing, none] description: Policy mode mx: type: string description: Comma-separated MX host names max_age: type: [integer, string] description: Seconds senders may cache the policy, 1 to 31557600 active: type: [integer, string] enum: [0, 1, "0", "1"] description: Policy published example: items: - example.org attr: version: stsv1 mode: testing mx: mail.example.org max_age: 604800 active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/fail2ban: get: summary: Get Fail2Ban settings and bans description: >- Returns the Fail2Ban (netfilter) settings, the regex filters, the allow- and denylists and the current bans. Admins only (an API key may); other callers get `{}`. Any further path segment (e.g. `/api/v1/get/fail2ban/all`) gives the same answer. operationId: getFail2Ban tags: - Fail2Ban responses: "200": description: Settings and bans content: application/json: schema: $ref: "#/components/schemas/Fail2Ban" example: ban_time: 1800 ban_time_increment: true max_ban_time: 86400 netban_ipv4: 32 netban_ipv6: 128 max_attempts: 10 retry_window: 600 banlist_id: a2c11baf-5770-47de-9c5e-575bd7bc9218 manage_external: 0 regex: "1": "^mailcow UI: Invalid password for .+ by ([0-9a-f\\.:]+)$" "2": "^Rspamd UI: Invalid password by ([0-9a-f\\.:]+)$" whitelist: |- 192.0.2.0/24 mail.example.org blacklist: 198.51.100.66/32 perm_bans: - network: 198.51.100.66/32 ip: 198.51.100.66 active_bans: - queued_for_unban: 0 network: 203.0.113.7/32 ip: 203.0.113.7 banned_until: 00h 27m 08s "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/fail2ban/banlist: post: summary: Renew the Fail2Ban banlist URL description: >- Gives the banlist (`/f2b-banlist?id=`, the list of denied and banned networks for an external firewall) a new random ID, so the old URL stops working. `items` must hold the current `banlist_id` (from `GET /api/v1/get/fail2ban`); `attr` can be left out. Admins only (an API key may). Answers `f2b_banlist_refreshed`; a wrong ID is answered with status 404 and `Cannot edit item`, a Redis error with 500 and `redis_error`. operationId: refreshFail2BanBanlist tags: - Fail2Ban requestBody: content: application/json: schema: type: object properties: items: description: The current banlist ID (single value or a list with it as first element) oneOf: - type: array items: type: string - type: string attr: type: object description: Not used required: - items example: items: - a2c11baf-5770-47de-9c5e-575bd7bc9218 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" "404": description: "`items` does not hold the current banlist ID" content: application/json: schema: $ref: "#/components/schemas/TaskResult" example: type: error msg: Cannot edit item /api/v1/get/logs/acme/{count}: get: summary: Get ACME logs (count or range) description: Entries of the ACME log (Let's Encrypt certificates), newest first. operationId: getAcmeLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/AcmeLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/api/{count}: get: summary: Get API logs (count or range) description: >- Entries of the API log: every request to `/api/v1` with its form data (values of keys like passwords and secrets replaced by `*`). Newest first. operationId: getApiLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/ApiLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/autodiscover/{count}: get: summary: Get Autodiscover logs (count or range) description: >- Requests to Autodiscover (Outlook, ActiveSync) and the service that was offered, newest first. operationId: getAutodiscoverLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/AutodiscoverLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/dovecot/{count}: get: summary: Get Dovecot logs (count or range) description: Entries of the Dovecot log (IMAP, POP3, Sieve, LMTP), newest first. operationId: getDovecotLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/DovecotLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/netfilter/{count}: get: summary: Get netfilter logs (count or range) description: Entries of netfilter (failed logins, bans), newest first. operationId: getNetfilterLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/NetfilterLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/postfix/{count}: get: summary: Get Postfix logs (count or range) description: Entries of the Postfix log (SMTP), newest first. operationId: getPostfixLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/PostfixLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/ratelimited/{count}: get: summary: Get rate limit logs (count or range) description: >- Messages that were rejected because a rate limit was exceeded, newest first. operationId: getRatelimitLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/RatelimitLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/rspamd-history/{count}: get: summary: Get Rspamd history (count or range) description: >- Messages scanned by Rspamd with score, action and symbols, newest first. operationId: getRspamdHistoryByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N messages. `from-to` (from ≥ 1, to ≥ from) is passed to Rspamd, which counts from 0: `2-3` gives the third and fourth newest message. Characters other than digits and `-` are removed; `0` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/RspamdHistory" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/sogo/{count}: get: summary: Get SOGo logs (count or range) description: Entries of the SOGo log, newest first. operationId: getSogoLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/SogoLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/watchdog/{count}: get: summary: Get watchdog logs (count or range) description: Health checks of the watchdog container, newest first. operationId: getWatchdogLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/WatchdogLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/acme: get: summary: Get ACME logs description: >- The newest 1000 entries of the ACME log (Let's Encrypt certificates), newest first. operationId: getAcmeLogs tags: - Logs responses: "200": $ref: "#/components/responses/AcmeLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/api: get: summary: Get API logs description: >- The newest 1000 entries of the API log: every request to `/api/v1` with its form data (values of keys like passwords and secrets replaced by `*`). Requests with a JSON body show the body as `attr` or `items`. Newest first. operationId: getApiLogs tags: - Logs responses: "200": $ref: "#/components/responses/ApiLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/autodiscover: get: summary: Get Autodiscover logs description: >- The newest 1000 requests to Autodiscover (Outlook, ActiveSync) and the service that was offered, newest first. operationId: getAutodiscoverLogs tags: - Logs responses: "200": $ref: "#/components/responses/AutodiscoverLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/cron: get: summary: Get cron logs description: >- The newest 1000 entries of the scheduled tasks of mailcow (e.g. identity provider sync, group shares), newest first. operationId: getCronLogs tags: - Logs responses: "200": $ref: "#/components/responses/CronLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/cron/{count}: get: summary: Get cron logs (count or range) description: >- Entries of the scheduled tasks of mailcow (e.g. identity provider sync, group shares), newest first. operationId: getCronLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: the entries from position `from` to `to` (the newest entry is 1). Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/CronLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/dovecot: get: summary: Get Dovecot logs description: The newest 1000 entries of the Dovecot log (IMAP, POP3, Sieve, LMTP), newest first. operationId: getDovecotLogs tags: - Logs responses: "200": $ref: "#/components/responses/DovecotLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/netfilter: get: summary: Get netfilter logs description: >- The newest 1000 entries of netfilter (failed logins, bans), newest first. operationId: getNetfilterLogs tags: - Logs responses: "200": $ref: "#/components/responses/NetfilterLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/postfix: get: summary: Get Postfix logs description: The newest 1000 entries of the Postfix log (SMTP), newest first. operationId: getPostfixLogs tags: - Logs responses: "200": $ref: "#/components/responses/PostfixLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/ratelimited: get: summary: Get rate limit logs description: >- The newest 1000 messages that were rejected because a rate limit was exceeded, newest first. operationId: getRatelimitLogs tags: - Logs responses: "200": $ref: "#/components/responses/RatelimitLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/rspamd-history: get: summary: Get Rspamd history description: >- The newest 1000 messages scanned by Rspamd with score, action and symbols, newest first. operationId: getRspamdHistory tags: - Logs responses: "200": $ref: "#/components/responses/RspamdHistory" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/rspamd-stats: get: summary: Get Rspamd statistics description: >- The statistics of Rspamd (`/stat` of its controller): scanned and learned messages, actions, Bayes statistics. Further path segments are ignored. operationId: getRspamdStats tags: - Logs responses: "200": description: The statistics, `{}` if Rspamd cannot be reached content: application/json: schema: $ref: "#/components/schemas/RspamdStats" example: version: 3.12.1 config_id: ch8ha1o68oxb3xf9izngisz4wxuxgs86n9gg6yko7jtt4msu4bxhuunnoi4pk7an4y5htzjb1y9nwsxmkjhdu671ocb5b5ea1g38csb uptime: 25891 read_only: false scanned: 300 learned: 30 actions: reject: 38 soft reject: 0 rewrite subject: 0 add header: 6 greylist: 1 no action: 255 scan_times: - 0.340365 - 0.48114 spam_count: 44 ham_count: 256 connections: 24 control_connections: 700 total_learns: 29 statfiles: - revision: 6 users: 1 used: 0 total: 0 size: 0 symbol: BAYES_SPAM type: redis languages: 0 class: spam fuzzy_hashes: mailcow: 0 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/sasl: get: summary: Get SASL logs description: >- The newest 1000 successful logins (IMAP, POP3, SMTP, SOGo, ...), one entry per service, address and user with the time of the last login, newest first. operationId: getSaslLogs tags: - Logs responses: "200": $ref: "#/components/responses/SaslLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/sasl/{count}: get: summary: Get SASL logs (count or range) description: >- Successful logins (IMAP, POP3, SMTP, SOGo, ...), one entry per service, address and user with the time of the last login, newest first. operationId: getSaslLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: `to` entries starting at position `from` (the newest entry is 1), so `2-3` gives the entries 2 to 4. Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/SaslLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/sogo: get: summary: Get SOGo logs description: The newest 1000 entries of the SOGo log, newest first. operationId: getSogoLogs tags: - Logs responses: "200": $ref: "#/components/responses/SogoLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/ui: get: summary: Get mailcow UI logs description: >- The newest 1000 actions of the mailcow UI and API (the messages of their calls), newest first. operationId: getUiLogs tags: - Logs responses: "200": $ref: "#/components/responses/UiLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/ui/{count}: get: summary: Get mailcow UI logs (count or range) description: >- Actions of the mailcow UI and API (the messages of their calls), newest first. operationId: getUiLogsByCount tags: - Logs parameters: - name: count in: path required: true description: >- `N`: the newest N entries. `from-to`: `to` entries starting at position `from` (the newest entry is 1), so `2-3` gives the entries 2 to 4. Characters other than digits and `-` are removed; `0` or a range with `to` < `from` gives `{}`. schema: type: string pattern: ^[0-9]+(-[0-9]+)?$ example: "50" responses: "200": $ref: "#/components/responses/UiLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/logs/watchdog: get: summary: Get watchdog logs description: >- The newest 1000 health checks of the watchdog container, newest first. operationId: getWatchdogLogs tags: - Logs responses: "200": $ref: "#/components/responses/WatchdogLogs" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox/{id}: get: tags: - Mailboxes summary: Get a mailbox operationId: getMailbox description: >- Details of one mailbox, `{}` if it does not exist or the caller has no access to it. A mailbox user may read its own mailbox. With the query parameter `tags`, `{id}` is a search term instead: the answer is the list of mailboxes whose address contains `{id}` and that have a tag containing one of the tags, `[]` when none matches. parameters: - name: id in: path required: true description: "Mailbox address (with `tags`: part of the address, e.g. a domain)" schema: type: string example: john.doe@example.org - $ref: "#/components/parameters/MailboxTagsQuery" responses: "200": description: The mailbox; with `tags` a list of mailboxes content: application/json: schema: oneOf: - $ref: "#/components/schemas/Mailbox" - type: array items: $ref: "#/components/schemas/Mailbox" examples: mailbox: $ref: "#/components/examples/MailboxDetails" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox-group/{id}: get: summary: Get mailbox group description: >- One group by id, with the same visibility rules as `GET /api/v1/get/mailbox-group/all`. Answers `{}` when the id is not a number, the group does not exist or the caller may not see it. operationId: getMailboxGroup tags: - Groups parameters: - name: id in: path required: true description: Group id schema: type: integer example: 7 responses: "200": description: The group content: application/json: schema: $ref: "#/components/schemas/MailboxGroup" example: id: 7 domain: example.org name: Sales description: Sales team address: sales@example.org sender_allowed: 1 sogo_visible: 0 internal: 1 template_id: 3 template: Staff priority: 10 idp_ref: null members: - username: alice@example.org source: manual send_as: - "@example.com" imap_grants: [] sogo_grants: [] active: 1 created: "2026-10-25 12:00:00" modified: null can_edit: true "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox-group/member/{username}: get: summary: Get group memberships of a mailbox description: >- The groups of a mailbox, its effective group template (the template of the active group with the highest priority, then the lowest id) and the addresses it may send as through its active groups. Admins (every API key) get it for every existing mailbox, domain admins with the `mailbox_groups` ACL for their mailboxes (only the groups of their domains), a mailbox user for himself. Answers `{}` for unknown mailboxes or without access. operationId: getMailboxGroupMemberships tags: - Groups parameters: - name: username in: path required: true description: Mailbox (case-insensitive) schema: type: string example: alice@example.org responses: "200": description: The memberships content: application/json: schema: $ref: "#/components/schemas/MailboxGroupMemberships" example: groups: - id: 7 domain: example.org name: Sales address: sales@example.org sender_allowed: 1 active: 1 priority: 10 template: Staff source: manual template: Staff send_as: - orders@example.org - sales@example.org "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox-group/all: get: summary: Get mailbox groups description: >- All groups the caller may see, ordered by domain and name: admins (and every API key) all groups, domain admins with the `mailbox_groups` ACL the groups of their domains, others none. `can_edit` is false for read-only API keys. Answers `{}` when there is no group. operationId: getMailboxGroups tags: - Groups responses: "200": description: The groups content: application/json: schema: type: array items: $ref: "#/components/schemas/MailboxGroup" example: - id: 7 domain: example.org name: Sales description: Sales team address: sales@example.org sender_allowed: 1 sogo_visible: 0 internal: 0 template_id: 3 template: Staff priority: 10 idp_ref: null members: - username: alice@example.org source: manual - username: bob@example.org source: idp send_as: - orders@example.org imap_grants: - owner: shared@example.org rights: write subscribe: 1 state: applied last_error: null sogo_grants: - owner: shared@example.org type: Calendar folder: personal access: read subscribe: 0 state: pending last_error: null active: 1 created: "2026-10-25 12:00:00" modified: "2026-10-28 09:14:03" can_edit: true "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox-group/all/{domains}: get: summary: Get mailbox groups of domains description: >- Like `GET /api/v1/get/mailbox-group/all`, limited to the groups of the given domains. Answers `{}` when there is no group. operationId: getMailboxGroupsOfDomains tags: - Groups parameters: - name: domains in: path required: true description: One domain or a comma-separated list of domains schema: type: string example: example.org,example.com responses: "200": description: The groups of the domains content: application/json: schema: type: array items: $ref: "#/components/schemas/MailboxGroup" example: - id: 7 domain: example.org name: Sales description: "" address: sales@example.org sender_allowed: 1 sogo_visible: 1 internal: 0 template_id: null template: null priority: 0 idp_ref: null members: - username: alice@example.org source: manual send_as: [] imap_grants: [] sogo_grants: [] active: 1 created: "2026-10-25 12:00:00" modified: null can_edit: true "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/identity-provider/sync-status: get: summary: Get identity sync status description: >- Status of the Identity Provider sync (admins and API keys, also read-only; `{}` for others): the provider, whether it can be synced (Keycloak and LDAP) and a scheduled sync is enabled, counts of the mailboxes of the provider and their links, and the last 10 sync runs, newest first. Run status: `pending`, `running`, `ok`, `blocked` (the change limit `offboarding_limit` stopped the run, nothing was changed) or `error` (`summary.error`, nothing was changed). operationId: getIdentitySyncStatus tags: - Identity Provider responses: "200": description: The sync status content: application/json: schema: $ref: "#/components/schemas/IdentitySyncStatus" example: provider: keycloak can_sync: true sync_enabled: true counts: accounts: 120 linked: 118 idp_disabled: 2 missing: 1 runs: - id: 42 provider: keycloak mode: run status: ok requested_by: "" created: "2026-11-09 12:00:00" started: "2026-11-09 12:00:01" finished: "2026-11-09 12:00:03" summary: counts: links: 0 creates: 1 deactivate: 1 restore: 0 release: 0 missing: 1 found: 0 updates: 3 group_creates: 0 group_renames: 0 memberships: 2 orphaned_groups: 0 conflicts: 0 membership_removals: 0 skipped: 0 errors: 0 details: creates: - new.user@example.org deactivate: - left.user@example.org restore: [] missing: - left.user@example.org group_creates: [] conflicts: [] blocked: false errors: [] mode: run "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/identity-link/{username}: get: summary: Get identity link of a mailbox description: >- The Identity Provider user a mailbox is linked to (admins and API keys, domain admins for their mailboxes). Answers `{}` without a link or without access. `missing_since` is set while the sync does not find the mailbox at the provider; a mailbox that was never linked then has a row without `external_id`. operationId: getIdentityLink tags: - Identity Provider parameters: - name: username in: path required: true description: Mailbox (case-insensitive) schema: type: string example: alice@example.org responses: "200": description: The link content: application/json: schema: $ref: "#/components/schemas/IdentityLink" example: provider: keycloak external_id: 8d5da28d-7541-428b-8356-a9704f73e7f4 idp_disabled: 0 missing_since: null "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailq/all: get: summary: Get the mail queue description: >- Returns the Postfix mail queue (`postqueue -j`), at most 10000 entries. Admins only (an API key may); other callers and an empty queue get `[]`. operationId: getMailQueue tags: - Queue Manager responses: "200": description: List of queued messages content: application/json: schema: type: array items: $ref: "#/components/schemas/MailQueueItem" example: - queue_name: deferred queue_id: B98C6260CA1 arrival_time: 1762768034 message_size: 1848 forced_expire: false sender: sender@example.org recipients: - "rcpt@example.com (connect to mx.example.com[192.0.2.25]:25: Connection timed out)" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/postcat/{qid}: get: summary: Get a queued message description: >- Returns the output of `postcat -q ` for a message in the Postfix queue: envelope records, headers and body. Admins only (an API key may); other callers get an empty answer. The answer is sent with `Content-Type: application/json`, but the body is the plain postcat text, not JSON-encoded. An unknown or non-hexadecimal queue ID gives the error text of postcat. operationId: getQueuedMessage tags: - Queue Manager parameters: - name: qid in: path required: true description: Postfix queue ID schema: type: string example: B98C6260CA1 responses: "200": description: The postcat output as plain text (despite the JSON content type) content: application/json: schema: type: string example: | *** ENVELOPE RECORDS deferred/B/B98C6260CA1 *** message_size: 1848 612 1 0 1848 0 message_arrival_time: Mon Nov 10 09:47:14 2026 create_time: Mon Nov 10 09:47:14 2026 named_attribute: rewrite_context=local sender: sender@example.org *** MESSAGE CONTENTS deferred/B/B98C6260CA1 *** From: Sender To: rcpt@example.com Subject: Test Hello *** HEADER EXTRACTED deferred/B/B98C6260CA1 *** original_recipient: rcpt@example.com recipient: rcpt@example.com *** MESSAGE FILE END deferred/B/B98C6260CA1 *** "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/oauth2-client/{id}: get: summary: Get OAuth2 client description: One OAuth2 client including its secret. operationId: getOAuth2Client tags: - oAuth Clients parameters: - name: id in: path required: true description: ID of the client (`id`, not `client_id`) schema: type: integer example: 1 responses: "200": description: The client, `{}` if there is none with this ID content: application/json: schema: $ref: "#/components/schemas/OAuth2Client" example: id: 1 client_id: 17c76aaa88c0 client_secret: 73fc668a88147e32a31ff80c redirect_uri: https://app.example.org/oauth/callback grant_types: null scope: profile user_id: null "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/oauth2-client/all: get: summary: Get OAuth2 clients description: >- Lists the OAuth2 clients that may use mailcow as OAuth2 server, including their secrets. operationId: getOAuth2Clients tags: - oAuth Clients responses: "200": description: The clients, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/OAuth2Client" example: - id: 1 client_id: 17c76aaa88c0 client_secret: 73fc668a88147e32a31ff80c redirect_uri: https://app.example.org/oauth/callback grant_types: null scope: profile user_id: null "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/oauth2-client: post: summary: Edit OAuth2 clients description: >- Does one of three things per client, in this order of precedence: `revoke_tokens` deletes all access and refresh tokens of the client, `renew_secret` gives it a new random secret, otherwise `redirect_uri` is set. `revoke_tokens` and `renew_secret` act when they are present, whatever their value. Success: `object_modified` per client. Errors: `access_denied` (no client with this ID; stops the call), `Redirect/Callback URL cannot be empty`. operationId: editOAuth2Client tags: - oAuth Clients requestBody: required: true content: application/json: schema: type: object properties: items: description: ID of the client, or a list of IDs oneOf: - type: [integer, string] - type: array items: type: [integer, string] attr: type: object properties: redirect_uri: type: string description: New redirect URL; empty or missing keeps the current one revoke_tokens: type: [integer, string, boolean] description: Present = delete the access and refresh tokens of the client renew_secret: type: [integer, string, boolean] description: Present = generate a new client secret required: - items - attr example: items: - 1 attr: redirect_uri: https://app.example.org/oauth/callback2 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/policy_bl_domain/{domain}: get: summary: Get the sender deny list of a domain description: >- The deny list (blacklist) entries of the domain and of all its mailboxes (`object` tells which); `{}` if there are none or the domain is unknown. operationId: getDomainBlacklist tags: - Domain antispam policies parameters: - name: domain in: path required: true description: Domain name schema: type: string example: example.org responses: "200": description: The deny list entries content: application/json: schema: type: array items: $ref: "#/components/schemas/DomainPolicyEntry" example: - object: example.org value: "*@spam.example.com" prefid: 13 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/policy_wl_domain/{domain}: get: summary: Get the sender allow list of a domain description: >- The allow list (whitelist) entries of the domain and of all its mailboxes (`object` tells which); `{}` if there are none or the domain is unknown. operationId: getDomainWhitelist tags: - Domain antispam policies parameters: - name: domain in: path required: true description: Domain name schema: type: string example: example.org responses: "200": description: The allow list entries content: application/json: schema: type: array items: $ref: "#/components/schemas/DomainPolicyEntry" example: - object: example.org value: "*@partner.example.com" prefid: 12 - object: anna@example.org value: newsletter@example.net prefid: 15 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/quarantine/all: get: summary: Get quarantined messages description: >- Returns the quarantined messages without their content. An API key (admin) gets every item, a domain admin the items of their domains, a mailbox user their own items. `[]` when there are none. operationId: getQuarantine tags: - Quarantine responses: "200": description: List of quarantine items content: application/json: schema: type: array items: $ref: "#/components/schemas/QuarantineItem" example: - id: 33 qid: 8224615004C1 subject: You have won virus_flag: 0 score: 15.48 rcpt: john.doe@example.org sender: bounces@send.example.com action: reject created: 1762768034 notified: 1 "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/quarantine_category: post: tags: - Quarantine summary: Set the quarantine notification category of mailboxes operationId: editQuarantineCategory description: >- Which quarantined mail the notifications list: rejected mail (`reject`), mail sent to the junk folder (`add_header`) or both (`all`). Needs the user ACL `quarantine_category`. API keys may change any mailbox, a mailbox user only its own. `quarantine_category` must be sent: without it the call fails with an empty answer. Messages per mailbox: `mailbox_modified` or `access_denied` (also for an invalid value). requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object required: - quarantine_category properties: quarantine_category: type: string enum: - reject - add_header - all example: items: - john.doe@example.org attr: quarantine_category: all responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/quarantine/{id}: get: summary: Get a quarantined message description: >- Returns one quarantine item with the raw message and the Rspamd symbols. An API key (admin) may read every item; others only items of their mailboxes or domains. `[]` for an unknown or non-numeric ID or without access. operationId: getQuarantineItem tags: - Quarantine parameters: - name: id in: path required: true description: ID of the quarantine item schema: type: string example: "33" responses: "200": description: The quarantine item content: application/json: schema: $ref: "#/components/schemas/QuarantineItemDetails" example: id: 33 qid: 8224615004C1 subject: You have won score: 15.48 ip: 198.51.100.23 action: reject symbols: '[{"name":"BAYES_SPAM","score":5.1,"options":["99.99%"]},{"name":"RBL_SPAMHAUS_SBL","score":4.0}]' fuzzy_hashes: "[]" sender: bounces@send.example.com rcpt: john.doe@example.org msg: "Return-Path: \r\nFrom: Prize \r\nTo: john.doe@example.org\r\nSubject: You have won\r\n\r\nClick here\r\n" domain: null notified: 1 created: "2026-11-10 09:47:14" user: unknown qhash: 3f9d8c0b2a4e6f1d7c5b9a8e0f2d4c6b8a1e3f5d7c9b0a2e4f6d8c1b3a5e7f9d "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/quarantine: post: summary: Edit quarantine settings description: >- Saves the global quarantine settings (admins only, an API key may). `attr.action` must be `settings`; `items` is ignored. Every call writes all settings: an attribute that is not sent is reset (a missing `retention_size` or `max_size` becomes 0, which turns the quarantine off). The current values cannot be read through the API. Messages: `saved_settings`; `redis_error` (danger); `access_denied` for non-admins. Without `action` nothing happens and the answer is `Task completed`. operationId: editQuarantineSettings tags: - Quarantine requestBody: content: application/json: schema: type: object properties: items: description: Ignored attr: type: object properties: action: type: string const: settings retention_size: type: [integer, string] description: Items kept per mailbox; 0 disables the quarantine max_size: type: [integer, string] description: Maximum message size in MiB; larger messages are not quarantined (0 is not unlimited) max_score: type: [number, string] description: No notification for messages with a higher spam score; empty = no limit (9999.0) max_age: type: [integer, string] description: Days an item is kept; 0 or less becomes 365 release_format: type: string enum: - raw - attachment default: raw description: Deliver released messages as they are or as an attachment of a new message exclude_domains: type: array items: type: string description: Domains and alias domains whose mail is not quarantined sender: type: string description: Sender of the notifications (invalid address = empty) subject: type: string description: Subject of the notifications bcc: type: string description: Address that gets a copy of every notification (invalid address = empty) redirect: type: string description: Address that gets all notifications instead of the mailboxes (invalid address = empty) html_tmpl: type: string description: HTML template of the notifications (empty = the default template) required: - action required: - attr example: items: - self attr: action: settings retention_size: "20" max_size: "15" max_score: "" max_age: "365" release_format: raw exclude_domains: - example.com sender: quarantine@example.org subject: Spam Quarantine Notification bcc: "" redirect: "" html_tmpl: "" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/recipient_map/{id}: get: tags: - Address Rewriting summary: Get recipient maps operationId: getRecipientMaps description: >- `all` lists the recipient maps, an ID returns that map. `{}` if there is none or the caller is no administrator. The answer for an ID carries an additional member `"0": null` (json_api.php appends an undefined variable). parameters: - name: id in: path required: true description: "`all` or the ID of a recipient map" schema: type: string example: all responses: "200": description: The list of recipient maps (`all`) or one recipient map content: application/json: schema: anyOf: - type: array items: $ref: "#/components/schemas/RecipientMap" - $ref: "#/components/schemas/RecipientMap" examples: all: summary: get/recipient_map/all value: - id: 1 recipient_map_old: jane.doe@example.org recipient_map_new: jane.smith@example.org active: 1 created: "2026-11-04 08:30:00" modified: null - id: 2 recipient_map_old: "@example.net" recipient_map_new: "@example.org" active: 0 created: "2026-11-04 08:31:12" modified: "2026-11-05 17:02:45" one: summary: get/recipient_map/1 value: id: 1 recipient_map_old: jane.doe@example.org recipient_map_new: jane.smith@example.org active: 1 created: "2026-11-04 08:30:00" modified: null "0": null "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/bcc: post: tags: - Address Rewriting summary: Update BCC maps operationId: editBccMap description: >- Changes the BCC maps in `items`; attributes that are left out keep their value, `local_dest` cannot be changed. Same callers as add/bcc. Per map: `bcc_edited`, `access_denied` (unknown, no access or no ACL), `bcc_must_be_email`, `bcc_exists`. `type` is not checked by the backend: use `sender` or `rcpt`. requestBody: content: application/json: schema: type: object properties: items: description: IDs of the BCC maps, a single value or a list oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: bcc_dest: type: string description: E-mail address that gets the copies type: type: string enum: - sender - rcpt active: type: [boolean, integer, string] description: 1 = active, 0 = inactive required: - items - attr example: items: - 3 attr: bcc_dest: journal@example.com active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/recipient_map: post: tags: - Address Rewriting summary: Update recipient maps operationId: editRecipientMap description: >- Changes the recipient maps in `items`; attributes that are left out keep their value. Administrators and API keys only. Per map: `recipient_map_entry_saved`, `access_denied` (unknown ID), `invalid_recipient_map_old`, `invalid_recipient_map_new`; `recipient_map_entry_exists` stops the call. requestBody: content: application/json: schema: type: object properties: items: description: IDs of the recipient maps, a single value or a list oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: recipient_map_old: type: string description: Original recipient, an e-mail address or a domain recipient_map_new: type: string description: New recipient, an e-mail address or a domain active: type: [boolean, integer, string] description: 1 = active, 0 = inactive required: - items - attr example: items: - 1 attr: recipient_map_new: jane.smith@example.org active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/relayhost/{id}: get: tags: - Routing summary: Get sender-dependent transports operationId: getRelayhosts description: >- `all` lists the sender-dependent transports, an ID returns that entry, with the password in clear text and the domains and mailboxes that use it. `{}` if there is none or the caller is no administrator. parameters: - name: id in: path required: true description: "`all` or the ID of a sender-dependent transport" schema: type: string example: all responses: "200": description: The list of sender-dependent transports (`all`) or one entry content: application/json: schema: anyOf: - type: array items: $ref: "#/components/schemas/Relayhost" - $ref: "#/components/schemas/Relayhost" examples: all: summary: get/relayhost/all value: - id: 1 hostname: "[smtp.example.com]:587" username: relay-user@example.com password: Secret-Passphrase-42 active: 1 password_short: Sec... used_by_domains: example.org, example.net used_by_mailboxes: anna@example.com one: summary: get/relayhost/1 value: id: 1 hostname: "[smtp.example.com]:587" username: relay-user@example.com password: Secret-Passphrase-42 active: 1 password_short: Sec... used_by_domains: example.org, example.net used_by_mailboxes: anna@example.com "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/resource/all: get: tags: - Resources summary: Get all resources operationId: getResources description: >- All resources of the domains the caller manages, `{}` when there are none. responses: "200": description: List of resources content: application/json: schema: type: array items: $ref: "#/components/schemas/Resource" example: - name: MeetingRoom1@example.org kind: location multiple_bookings: 0 description: Meeting Room 1 active: 1 active_int: 1 domain: example.org local_part: MeetingRoom1 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/resource/{id}: get: tags: - Resources summary: Get a resource operationId: getResource description: >- One resource by its address, `{}` if it does not exist or the caller does not manage its domain. parameters: - name: id in: path required: true description: Address of the resource schema: type: string example: MeetingRoom1@example.org responses: "200": description: The resource content: application/json: schema: $ref: "#/components/schemas/Resource" "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/resource: post: tags: - Resources summary: Update resources operationId: editResource description: >- Changes resources; fields that are not sent keep their value (the address does not change with the description). Admins and domain admins (for their domains). Messages per resource: `resource_modified` or `resource_invalid`, `description_invalid`, `access_denied`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Resource address or list of addresses oneOf: - type: string - type: array items: type: string attr: type: object properties: description: type: string description: Name shown in SOGo kind: type: string enum: - location - group - thing multiple_bookings: type: [integer, string] description: 0 always free, -1 unlimited but busy when booked, n ≥ 1 at most n overlapping bookings active: type: [integer, string] enum: [0, 1, "0", "1"] example: items: - MeetingRoom1@example.org attr: description: Meeting Room 1 (2nd floor) multiple_bookings: -1 active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/rl-mbox/{mailbox}: get: tags: - Ratelimits summary: Get the rate limit of a mailbox operationId: getMailboxRatelimit description: >- The outgoing rate limit set on the mailbox, `{}` when it has none (the domain limit applies then) or the caller is no admin or domain admin of it. parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: The rate limit content: application/json: schema: $ref: "#/components/schemas/MailboxRatelimit" example: value: "100" frame: h "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/rl-domain/{domain}: get: summary: Get rate limits of domains description: >- The rate limit (outgoing messages per time frame) of a domain or alias domain, `{}` if it has none. `all` answers the list of all domains and alias domains that have a rate limit, each with its `domain`. operationId: getDomainRatelimits tags: - Ratelimits parameters: - name: domain in: path required: true description: "`all` or a domain or alias domain name" schema: type: string example: example.org responses: "200": description: The rate limit, or the list of rate limits (`all`) content: application/json: schema: oneOf: - $ref: "#/components/schemas/DomainRatelimit" - type: array items: allOf: - $ref: "#/components/schemas/DomainRatelimit" - type: object properties: domain: type: string description: Domain or alias domain example: value: "100" frame: h "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/rl-domain: post: summary: Set rate limits of domains description: >- Sets the rate limit (outgoing messages per time frame) of domains or alias domains. Needs the ACL `ratelimit` (API keys have it). `rl_value` empty or 0 removes the limit; `rl_frame` must be valid then too. Success `rl_saved`, errors `rl_timeframe`, `access_denied`. operationId: editDomainRatelimits tags: - Ratelimits requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Domain or alias domain name, or a list of them oneOf: - type: string - type: array items: type: string attr: type: object required: - rl_frame properties: rl_value: type: [integer, string] description: Messages per `rl_frame`; empty or 0 removes the limit rl_frame: type: string enum: [s, m, h, d] description: Time frame (second, minute, hour, day) example: items: - example.org attr: rl_value: 100 rl_frame: h responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/rl-mbox/all: get: tags: - Ratelimits summary: Get the rate limits of all mailboxes operationId: getMailboxRatelimits description: >- The outgoing rate limits set on mailboxes the caller manages; mailboxes without their own limit are left out. Admins and domain admins, `{}` when there is none. responses: "200": description: List of rate limits content: application/json: schema: type: array items: allOf: - $ref: "#/components/schemas/MailboxRatelimit" - type: object properties: mailbox: type: string example: - value: "100" frame: h mailbox: john.doe@example.org "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/rl-mbox: post: tags: - Ratelimits summary: Set the rate limit of mailboxes operationId: editMailboxRatelimit description: >- Sets or removes the outgoing rate limit of mailboxes: `rl_value` messages per `rl_frame`. A value of 0 or empty removes the limit; `rl_frame` must be valid in that case too. Needs the ACL `ratelimit` (admins, domain admins for their domains); mailbox users get `access_denied`. Messages per mailbox: `rl_saved` or `rl_timeframe`, `access_denied`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object required: - rl_frame properties: rl_value: type: [integer, string] description: Number of messages, 0 or empty removes the limit rl_frame: type: string enum: - s - m - h - d description: Per second, minute, hour or day example: items: - john.doe@example.org attr: rl_value: 100 rl_frame: h responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/rlhash: post: tags: - Ratelimits summary: Reset a rate limit counter operationId: deleteRatelimitHash description: >- Deletes the counter of a rate-limited sender (the `rl_hash` of an entry of `get/logs/ratelimited`) and its log lines, so the sender can send again. Admins only. The body is a list with one hash (several are concatenated into one). Unlike other write routes the answer is not JSON: `1` when the hash was deleted, an empty body when it was not found, is invalid or the caller is no admin. requestBody: required: true content: application/json: schema: type: array description: The hash items: type: string pattern: "^RL[0-9A-Za-z=]+$" example: - RLkfx5cv4sq1i3ehpkz5bgdhcq9u9gkzj responses: "200": description: "`1` if deleted, else an empty body" content: application/json: schema: type: integer const: 1 example: 1 "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/status/containers: get: summary: Get container status description: >- State, start time and image of every container of the mailcow installation, stopped containers included, keyed by service name. operationId: getContainerStatus tags: - Status responses: "200": description: The containers, `null` if dockerapi cannot be reached content: application/json: schema: type: object additionalProperties: $ref: "#/components/schemas/ContainerStatus" example: dovecot-mailcow: type: info container: dovecot-mailcow state: running started_at: "2026-11-10T11:00:51.541059812Z" image: ghcr.io/mailcow/dovecot:2.3.21.1-4 postfix-mailcow: type: info container: postfix-mailcow state: running started_at: "2026-11-10T11:00:51.292779901Z" image: ghcr.io/mailcow/postfix:1.80 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/status/vmail: get: summary: Get mail storage status description: Disk usage of the file system that holds the mail storage (`/var/vmail`). operationId: getVmailStatus tags: - Status responses: "200": description: The disk usage (`df -H` values) content: application/json: schema: type: object properties: type: type: string const: info disk: type: string description: Device used: type: string description: Used space total: type: string description: Size of the file system used_percent: type: string description: Used space in percent example: type: info disk: /dev/sda1 used: 65G total: 105G used_percent: 66% "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/status/version: get: summary: Get mailcow version description: >- The installed mailcow version: the release tag on the master branch, else the short commit ID. operationId: getVersion tags: - Status responses: "200": description: The version content: application/json: schema: type: object properties: version: type: [string, "null"] description: Release tag or commit ID example: version: "2026-11" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/status/container/{id}: get: summary: Get container statistics description: >- Resource statistics of one container: the last (up to three) Docker stats snapshots, taken about five seconds apart while this route is called. The container is named by its full Docker container ID (64 hex digits, e.g. from `docker ps --no-trunc`), which the API does not list. For an unknown ID the call waits until the dockerapi timeout (60 seconds) and answers with the curl error text as a JSON string. operationId: getContainerStats tags: - Status parameters: - name: id in: path required: true description: Full Docker container ID schema: type: string example: 5d0f3cbb2a6e4f6c8e0e1c3b9a7d2f41b6c5e8a9d0f1e2c3b4a5968778695a4b responses: "200": description: >- Docker stats objects (see the Docker Engine API, `ContainerStats`), oldest first; `false` without ID content: application/json: schema: type: array items: type: object description: One Docker stats snapshot properties: read: type: string description: Time of the snapshot name: type: string id: type: string cpu_stats: type: object precpu_stats: type: object memory_stats: type: object blkio_stats: type: object networks: type: object pids_stats: type: object additionalProperties: true example: - read: "2026-11-10T19:20:01.123456789Z" name: /mailcowdockerized-dovecot-mailcow-1 id: 5d0f3cbb2a6e4f6c8e0e1c3b9a7d2f41b6c5e8a9d0f1e2c3b4a5968778695a4b pids_stats: current: 38 memory_stats: usage: 104857600 limit: 12541669376 blkio_stats: io_service_bytes_recursive: - major: 8 minor: 0 op: read value: 1052672 - major: 8 minor: 0 op: write value: 40960 networks: eth0: rx_bytes: 2621440 tx_bytes: 1048576 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/status/host: get: summary: Get host statistics description: CPU, memory, uptime and architecture of the Docker host. operationId: getHostStatus tags: - Status responses: "200": description: The host statistics content: application/json: schema: $ref: "#/components/schemas/HostStats" example: cpu: cores: 4 usage: 6 memory: total: 12541669376 usage: 62.5 swap: - 1022357504 - 512716800 - 509640704 - 50.2 - 1392812032 - 3864260608 uptime: 703800.5492310524 system_time: "10.11.2026 19:13:07" architecture: x86_64 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/status/host/ip: get: summary: Get public IP addresses description: >- The public IPv4 and IPv6 address of the server, as seen by the third party services ipv4.mailcow.email and ipv6.mailcow.email (requested on every call, regardless of the IP check setting of `edit/ip_check`). Other path segments after `host` give an empty answer. operationId: getHostIps tags: - Status responses: "200": description: The addresses, `false` for an address that could not be determined content: application/json: schema: type: object properties: ipv4: type: [string, boolean] description: Public IPv4 address ipv6: type: [string, boolean] description: Public IPv6 address example: ipv4: 198.51.100.25 ipv6: 2001:db8::25 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/syncjobs/all/no_log: get: summary: Get all sync jobs without logs description: >- Like `GET /api/v1/get/syncjobs/all`, with an empty `log` (the job list of the UI). The last segment is a comma-separated option list, only `no_log` has a meaning; other values are ignored. operationId: getSyncJobsWithoutLog tags: - Sync jobs responses: "200": description: The sync jobs content: application/json: schema: type: array items: $ref: "#/components/schemas/SyncJob" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/syncjob_source/{id}: get: summary: Get sync source description: >- One sync source by id, with the visibility rules of `GET /api/v1/get/syncjob_source/all`. Answers `{}` when the id is not a whole number, the source does not exist or the caller may not see it. operationId: getSyncJobSource tags: - Sync jobs parameters: - name: id in: path required: true description: Source id schema: type: integer example: 4 responses: "200": description: The sync source content: application/json: schema: $ref: "#/components/schemas/SyncJobSource" example: id: 4 name: Google Workspace description: "" created_by: admin scope: all host1: imap.gmail.com port1: 993 enc1: SSL verify_cert: 1 auth_type: XOAUTH2 oauth_flow: authorization_code oauth_token_endpoint: https://oauth2.googleapis.com/token oauth_authorize_endpoint: https://accounts.google.com/o/oauth2/v2/auth oauth_userinfo_endpoint: null oauth_client_id: 1234567890-example.apps.googleusercontent.com oauth_scope: https://mail.google.com/ openid email oauth_extra_params: '{"access_type":"offline","prompt":"consent"}' oauth_token_expires: null oauth_last_refresh_error: null oauth_last_refresh_attempt: null created: "2026-11-01 10:00:00" modified: null active: 1 domains: [] users: [] can_edit: true host: imap.gmail.com port: 993 enc: SSL has_client_secret: true has_access_token: false oauth_redirect_uri: https://mail.example.org/syncjob-oauth "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/syncjob_options: get: summary: Get allowed custom imapsync options description: >- The imapsync options sync jobs may use in `custom_params` (`$IMAPSYNC_OPTIONS` of vars.local.inc.php, defaults from vars.inc.php; options that run code, use local files or print secrets are always removed). Option name (lowercase, without dashes) mapped to its rule: `true` needs a value, `false` is a flag without value, a string is a regular expression the whole value must match. Available to every logged-in caller. operationId: getSyncJobOptions tags: - Sync jobs responses: "200": description: Option name => rule content: application/json: schema: type: object additionalProperties: type: - boolean - string example: authmd51: false debugssl: true folder: true maxsize: true syncinternaldates: false sslargs1: "^SSL_verify_mode=[01]$" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/imapsync_settings: get: summary: Get global sync job settings description: >- Settings of the sync job runner (admins and API keys; `{}` for others). operationId: getSyncJobSettings tags: - Sync jobs responses: "200": description: The settings content: application/json: schema: $ref: "#/components/schemas/SyncJobSettings" example: max_parallel: 3 max_bytes_per_second: 2048000 max_kb_per_second: 2000 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/syncjobs/all: get: summary: Get all sync jobs description: >- All sync jobs in queue order (highest `prio` first, then jobs that never ran, then the least recently run), with their logs. Admins (every API key) get every job, also jobs whose local mailbox no longer exists; domain admins the jobs of the mailboxes of their domains; mailbox users none (see `GET /api/v1/get/syncjobs/{mailbox}`). Answers `{}` when there is no job. operationId: getSyncJobs tags: - Sync jobs responses: "200": description: The sync jobs content: application/json: schema: type: array items: $ref: "#/components/schemas/SyncJob" example: - id: 12 user2: alice@example.org regextrans2: "" authmd51: 0 domain2: "" subfolder2: Imported user1: alice@old-provider.example.com exclude: "(?i)spam|(?i)junk" maxage: 0 mins_interval: 20 maxbytespersecond: "0" delete2duplicates: 1 delete1: 0 delete2: 0 automap: 1 skipcrossduplicates: 0 custom_params: '[{"o":"folder","v":"INBOX"}]' timeout1: 600 timeout2: 600 subscribeall: 1 dry: 0 is_running: 0 last_run: "2026-11-09 08:20:01" success: 1 exit_status: EX_OK created: "2026-11-03 09:40:09" modified: "2026-11-03 09:42:21" active: 1 source_id: 3 custom_params_legacy: null prio: 0 source_name: Old provider source_description: Migration from the old provider source_host: imap.example.com source_port: 993 source_enc: SSL source_auth_type: PLAIN source_created_by: API source_scope: domain source_oauth_flow: client_credentials source_active: 1 source_oauth_token_expires: null source_oauth_last_refresh_error: null user_token_expires: null user_token_error: null user_token_exists: 0 log: "Transfer started at Mon Nov 9 08:20:02 2026\n..." "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/syncjobs/{mailbox}: get: summary: Get sync jobs of a mailbox description: >- The sync jobs of a local mailbox in queue order, with their logs. Admins (every API key) get the jobs of every existing mailbox, domain admins of their mailboxes, users of their own. A value that is no email address returns the caller's own jobs (none for an API key). Answers `{}` when there is no job or no access. operationId: getMailboxSyncJobs tags: - Sync jobs parameters: - name: mailbox in: path required: true description: Local mailbox (`user2` of the jobs) schema: type: string example: alice@example.org responses: "200": description: The sync jobs of the mailbox content: application/json: schema: type: array items: $ref: "#/components/schemas/SyncJob" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/syncjobs/{mailbox}/no_log: get: summary: Get sync jobs of a mailbox without logs description: >- Like `GET /api/v1/get/syncjobs/{mailbox}`, with an empty `log`. The last segment is a comma-separated option list, only `no_log` has a meaning. operationId: getMailboxSyncJobsWithoutLog tags: - Sync jobs parameters: - name: mailbox in: path required: true description: Local mailbox (`user2` of the jobs) schema: type: string example: alice@example.org responses: "200": description: The sync jobs of the mailbox content: application/json: schema: type: array items: $ref: "#/components/schemas/SyncJob" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/syncjob_source/all: get: summary: Get sync sources description: >- The sync sources the caller may see, ordered by name: admins (every API key) all; domain admins their own, `all` sources, sources for their domains or mailboxes and sources created by their mailboxes; users their own, `all` sources and sources for them or their domain. Secrets are never returned (`has_client_secret`, `has_access_token`), extra OAuth2 parameters only to callers who may edit the source (`can_edit`, false for read-only API keys). Answers `{}` when there is no source. operationId: getSyncJobSources tags: - Sync jobs responses: "200": description: The sync sources content: application/json: schema: type: array items: $ref: "#/components/schemas/SyncJobSource" example: - id: 3 name: Old provider description: Migration from the old provider created_by: API scope: domain host1: imap.example.com port1: 993 enc1: SSL verify_cert: 1 auth_type: PLAIN oauth_flow: client_credentials oauth_token_endpoint: null oauth_authorize_endpoint: null oauth_userinfo_endpoint: null oauth_client_id: null oauth_scope: null oauth_extra_params: null oauth_token_expires: null oauth_last_refresh_error: null oauth_last_refresh_attempt: null created: "2026-11-03 09:42:21" modified: null active: 1 domains: - example.org users: [] can_edit: true host: imap.example.com port: 993 enc: SSL has_client_secret: false has_access_token: false "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/tls-policy-map/{id}: get: tags: - Outgoing TLS Policy Map Overrides summary: Get TLS policy maps operationId: getTlsPolicyMaps description: >- `all` lists the TLS policy maps, an ID returns that map. `{}` if there is none or the caller is no administrator. The answer for an ID carries an additional member `"0": null` (json_api.php appends an undefined variable). parameters: - name: id in: path required: true description: "`all` or the ID of a TLS policy map" schema: type: string example: all responses: "200": description: The list of TLS policy maps (`all`) or one TLS policy map content: application/json: schema: anyOf: - type: array items: $ref: "#/components/schemas/TlsPolicyMap" - $ref: "#/components/schemas/TlsPolicyMap" examples: all: summary: get/tls-policy-map/all value: - id: 2 dest: example.com policy: encrypt parameters: protocols=>=TLSv1.2 active: 1 created: "2026-11-06 12:00:31" modified: null one: summary: get/tls-policy-map/2 value: id: 2 dest: example.com policy: encrypt parameters: protocols=>=TLSv1.2 active: 1 created: "2026-11-06 12:00:31" modified: null "0": null "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/tls-policy-map: post: tags: - Outgoing TLS Policy Map Overrides summary: Update TLS policy maps operationId: editTlsPolicyMap description: >- Changes the TLS policy maps in `items`; attributes that are left out keep their value (`parameters: ""` clears the parameters). Administrators and API keys only. Per map: `tls_policy_map_entry_saved` or `access_denied` (unknown ID); `tls_policy_map_dest_invalid`, `tls_policy_map_parameter_invalid` and an existing destination (answered with `recipient_map_entry_exists`) stop the call. requestBody: content: application/json: schema: type: object properties: items: description: IDs of the TLS policy maps, a single value or a list oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: dest: type: string description: Destination, e.g. `example.org` or `[mail.example.org]:25` policy: type: string enum: - none - may - encrypt - dane - dane-only - fingerprint - verify - secure parameters: type: string description: Empty or space-separated `name=value` attributes active: type: [boolean, integer, string] description: 1 = active, 0 = inactive required: - items - attr example: items: - 2 attr: policy: dane parameters: "" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/transport/{id}: get: tags: - Routing summary: Get transport maps operationId: getTransports description: >- `all` lists the transport maps, an ID returns that entry, with the password in clear text. `{}` if there is none or the caller is no administrator. parameters: - name: id in: path required: true description: "`all` or the ID of a transport map" schema: type: string example: all responses: "200": description: The list of transport maps (`all`) or one entry content: application/json: schema: anyOf: - type: array items: $ref: "#/components/schemas/Transport" - $ref: "#/components/schemas/Transport" examples: all: summary: get/transport/all value: - id: 4 is_mx_based: 0 destination: example.com nexthop: "[mx.example.com]:25" username: "" password: "" active: 1 password_short: "..." - id: 5 is_mx_based: 1 destination: .*\.google\.com nexthop: "[smtp.example.net]:587" username: relay-user@example.net password: Secret-Passphrase-42 active: 1 password_short: Sec... one: summary: get/transport/4 value: id: 4 is_mx_based: 0 destination: example.com nexthop: "[mx.example.com]:25" username: "" password: "" active: 1 password_short: "..." "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/relayhost: post: tags: - Routing summary: Update sender-dependent transports operationId: editRelayhost description: >- Changes the sender-dependent transports in `items`; attributes that are left out keep their value (an empty `hostname` too). Administrators and API keys only. Per entry: `object_modified` (without the host name), `relayhost_invalid` (unknown ID), `mysql_error`. requestBody: content: application/json: schema: type: object properties: items: description: IDs of the sender-dependent transports, a single value or a list oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: hostname: type: string description: Next hop, e.g. `[smtp.example.com]:587` username: type: string description: SMTP user name, empty for no authentication password: type: string description: SMTP password active: type: [boolean, integer, string] description: 1 = active, 0 = inactive required: - items - attr example: items: - 1 attr: password: New-Secret-Passphrase-43 active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/transport: post: tags: - Routing summary: Update transport maps operationId: editTransport description: >- Changes the transport maps in `items`; attributes that are left out keep their value (an empty `destination`, `nexthop`, `is_mx_based` or `active` too). An empty `username` also clears the password. The credentials are written to every entry with the same next hop. Administrators and API keys only. Per entry: `object_modified` (without names), `relayhost_invalid` (unknown ID), `mysql_error`; `transport_dest_exists`, `invalid_destination`, `next_hop_interferes` and `next_hop_interferes_any` stop the call. requestBody: content: application/json: schema: type: object properties: items: description: IDs of the transport maps, a single value or a list oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: destination: type: string description: One destination (see add/transport) nexthop: type: string description: Next hop, e.g. `[mx.example.com]:25` username: type: string description: SMTP user name; empty removes the credentials password: type: string description: SMTP password is_mx_based: type: [boolean, integer, string] description: 1 = `destination` is a regular expression for MX names active: type: [boolean, integer, string] description: 1 = active, 0 = inactive required: - items - attr example: items: - 4 attr: nexthop: "[mx2.example.com]:25" active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/cors: post: summary: Edit CORS settings description: >- Sets which origins (web pages of other hosts) may call the API from a browser and with which methods. A missing value is replaced by its default. `items` is not used. Success: `cors_headers_edited`. Errors: `cors_invalid_origin`, `cors_invalid_method`. operationId: editCors tags: - Cross-Origin Resource Sharing (CORS) requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: allowed_origins: description: >- Origins (`scheme://host[:port]`, http or https, no path) or `*` for any origin; a list or a string with one origin per line. Default: the URL of the mailcow host. oneOf: - type: array items: type: string - type: string allowed_methods: description: >- Allowed methods: `GET`, `POST`, `PUT`, `DELETE` (uppercase); a list or a string separated by single spaces, commas, semicolons or newlines (`GET,POST`, not `GET, POST`). Default: all four. oneOf: - type: array items: type: string enum: - GET - POST - PUT - DELETE - type: string required: - attr example: attr: allowed_origins: - https://app.example.org - https://admin.example.com:8443 allowed_methods: - GET - POST responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/spam-score/{mailbox}: get: tags: - Mailbox user settings summary: Get the spam score of a mailbox operationId: getSpamScore description: >- The spam score thresholds of the mailbox: its own values or the server default. `{}` if the caller has no access to the mailbox (a mailbox user: other mailboxes). parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: Thresholds content: application/json: schema: $ref: "#/components/schemas/SpamScore" example: score: "6,12" "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/spam-score: post: tags: - Mailbox user settings summary: Set the spam score of mailboxes operationId: editSpamScore description: >- Sets the spam thresholds of the mailboxes: from the first value on mail goes to the junk folder, from the second it is rejected (equal values: the second is raised by 0.1). `default` removes the own values. Needs the user ACL `spam_score`. API keys may change any mailbox, a mailbox user only its own. Messages per mailbox: `mailbox_modified` or `access_denied`, "Invalid spam score, format must be "1,2" where first is low and second is high spam value.". requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: $ref: "#/components/schemas/MailboxItems" attr: type: object required: - spam_score properties: spam_score: type: string description: '"low,high" (numbers) or "default"' examples: - "6,12" - default example: items: - john.doe@example.org attr: spam_score: "6,12" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/mailbox/template: post: tags: - Mailboxes summary: Create a mailbox template operationId: addMailboxTemplate description: >- Saves a named set of mailbox settings that `add/mailbox` (field `template`) and the UI use as defaults. Admins only (`access_denied` otherwise). Errors: `template_name_invalid` (empty name), `template_exists`, `quarantine_category_invalid`. Success: `template_added`. Fields that are not sent get the defaults named in the schema; without `acl` the template grants no user ACL. requestBody: required: true content: application/json: schema: allOf: - type: object required: - template properties: template: type: string description: Name of the template - $ref: "#/components/schemas/MailboxTemplateInput" example: template: Sales quota: 2048 tags: - sales quarantine_notification: daily quarantine_category: reject rl_value: 100 rl_frame: h active: 1 protocol_access: - imap - smtp - sieve acl: - spam_alias - spam_score - app_passwds responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/mailbox/all: get: tags: - Mailboxes summary: Get all mailboxes operationId: getMailboxes description: >- Details of every mailbox in the domains the caller manages (API keys and admins: all domains). Resources are not included. `{}` when there are none. parameters: - $ref: "#/components/parameters/MailboxTagsQuery" responses: "200": description: List of mailboxes content: application/json: schema: type: array items: $ref: "#/components/schemas/Mailbox" examples: mailboxes: $ref: "#/components/examples/MailboxList" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox/all/{domains}: get: tags: - Mailboxes summary: Get the mailboxes of domains operationId: getMailboxesOfDomains description: >- Like `get/mailbox/all`, limited to the given domains. Caution: a domain that does not exist or that the caller does not manage adds all mailboxes the caller manages (API keys: every mailbox) to the answer. `{}` when nothing is found. parameters: - name: domains in: path required: true description: Domain name or comma-separated list of domain names schema: type: string example: example.org - $ref: "#/components/parameters/MailboxTagsQuery" responses: "200": description: List of mailboxes content: application/json: schema: type: array items: $ref: "#/components/schemas/Mailbox" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox/reduced: get: tags: - Mailboxes summary: Get all mailboxes (reduced details) operationId: getMailboxesReduced description: >- Like `get/mailbox/all`, but each mailbox without the costly fields `max_new_quota`, `spam_aliases`, `pushover_active`, `rl`, `rl_scope`, `is_relayed` and `sender_acl`. Faster for large installations. parameters: - $ref: "#/components/parameters/MailboxTagsQuery" responses: "200": description: List of mailboxes content: application/json: schema: type: array items: $ref: "#/components/schemas/Mailbox" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox/reduced/{domains}: get: tags: - Mailboxes summary: Get the mailboxes of domains (reduced details) operationId: getMailboxesReducedOfDomains description: >- Like `get/mailbox/reduced`, limited to the given domains, with the same caution as `get/mailbox/all/{domains}`: an unknown domain adds all mailboxes the caller manages. parameters: - name: domains in: path required: true description: Domain name or comma-separated list of domain names schema: type: string example: example.org,example.com - $ref: "#/components/parameters/MailboxTagsQuery" responses: "200": description: List of mailboxes content: application/json: schema: type: array items: $ref: "#/components/schemas/Mailbox" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox/template/all: get: tags: - Mailboxes summary: Get all mailbox templates operationId: getMailboxTemplates description: >- All mailbox templates. Admins and domain admins; `{}` for others or when there is none. responses: "200": description: List of templates content: application/json: schema: type: array items: $ref: "#/components/schemas/MailboxTemplate" example: - id: 2 template: Default type: mailbox attributes: tags: [] quota: 0 quarantine_notification: hourly quarantine_category: reject rl_frame: s rl_value: "" force_pw_update: 0 force_tfa: 0 sogo_access: 1 active: 1 tls_enforce_in: 0 tls_enforce_out: 0 imap_access: 1 pop3_access: 1 smtp_access: 1 sieve_access: 1 eas_access: 1 dav_access: 1 acl_spam_alias: 1 acl_tls_policy: 1 acl_spam_score: 1 acl_spam_policy: 1 acl_delimiter_action: 1 acl_syncjobs: 0 acl_eas_reset: 1 acl_sogo_profile_reset: 0 acl_pushover: 1 acl_quarantine: 1 acl_quarantine_attachments: 1 acl_quarantine_notification: 1 acl_quarantine_category: 1 acl_app_passwds: 1 created: "2026-09-21 10:23:08" modified: null "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/mailbox/template/{id}: get: tags: - Mailboxes summary: Get a mailbox template operationId: getMailboxTemplate description: >- One mailbox template by its ID, `{}` if there is none. Admins and domain admins. parameters: - name: id in: path required: true description: Template ID schema: type: integer example: 2 responses: "200": description: The template content: application/json: schema: $ref: "#/components/schemas/MailboxTemplate" "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/mailbox/template: post: tags: - Mailboxes summary: Update mailbox templates operationId: editMailboxTemplate description: >- Changes mailbox templates and passes the change on to the mailboxes whose effective group template it is. Admins only (`access_denied`). The template "Default" keeps its name. Messages: `template_id_invalid`, `quarantine_category_invalid`, once at the end `template_modified`. Send the complete template as the UI does: values that are not sent are not taken over from the stored template. `quota` falls back to 0 (unlimited), `quarantine_category` must be sent (else `quarantine_category_invalid`), and without `protocol_access` or `acl` the protocol or ACL settings are lost. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Template ID or list of template IDs oneOf: - type: integer - type: array items: type: integer attr: allOf: - type: object properties: template: type: string description: New name of the template (not for "Default") - $ref: "#/components/schemas/MailboxTemplateInput" example: items: - 3 attr: template: Sales quota: 4096 tags: - sales tagged_mail_handler: subfolder quarantine_notification: daily quarantine_category: reject rl_value: 100 rl_frame: h force_pw_update: 0 force_tfa: 0 sogo_access: 1 active: 1 tls_enforce_in: 0 tls_enforce_out: 1 protocol_access: - imap - smtp - sieve - dav acl: - spam_alias - spam_score - delimiter_action - app_passwds responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/rename-mbox: post: tags: - Mailboxes summary: Rename a mailbox operationId: renameMailbox description: >- Renames `old_local_part@domain` to `new_local_part@domain`: the address in all tables (aliases, ACLs, filters, app passwords, BCC and recipient maps, sync jobs, groups, SOGo), the maildir and the SOGo account; IMAP ACLs are moved over. `items` is not used, the mailbox is named in `attr`. The mailbox must be active (1 or 2). Admins and domain admins (for their domains). Errors: `username_invalid`, `access_denied`. Success: `mailbox_renamed`. requestBody: required: true content: application/json: schema: type: object required: - attr properties: items: description: Not used attr: type: object required: - domain - old_local_part - new_local_part properties: domain: type: string description: Domain of the mailbox old_local_part: type: string description: Current local part new_local_part: type: string description: New local part create_alias: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 description: 1 creates an alias from the old to the new address example: items: [] attr: domain: example.org old_local_part: john.doe new_local_part: john create_alias: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/mailbox/template: post: tags: - Mailboxes summary: Delete mailbox templates operationId: deleteMailboxTemplate description: >- Deletes mailbox templates by ID; the template "Default" is kept silently. Admins only (`access_denied`). Answers `template_removed`. requestBody: required: true content: application/json: schema: type: array description: Template IDs items: type: integer example: - 3 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/last-login/{mailbox}: get: tags: - Mailboxes summary: Get the last logins of a mailbox operationId: getLastLogin description: >- Logins of the mailbox in the last 7 days (`sasl`: one entry per address, service and app password with the latest time) and its previous UI login (`ui`, the one before the latest). `sasl` is empty without access to the mailbox; `ui` is only given to admins (API keys) and the mailbox user itself (`false` when there is none), domain admins get an empty list. parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: Logins content: application/json: schema: $ref: "#/components/schemas/LastLogin" example: ui: remote: 198.51.100.23 time: 1793011200 sasl: - real_rip: 198.51.100.23 datetime: "2026-11-09 08:12:44" service: imap app_password: 23 app_password_name: Thunderbird location: DE - real_rip: Web/EAS/Internal (172.22.1.248) datetime: "2026-11-08 17:03:10" service: SOGO app_password: 0 app_password_name: null "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/last-login/{mailbox}/{days}: get: tags: - Mailboxes summary: Get the last logins of a mailbox for a period operationId: getLastLoginDays description: >- Like `get/last-login/{mailbox}` for the last `{days}` days; values below 1 mean 7. parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org - name: days in: path required: true description: Number of days schema: type: integer minimum: 1 example: 30 responses: "200": description: Logins content: application/json: schema: $ref: "#/components/schemas/LastLogin" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/reset-last-login/{mailbox}: get: tags: - Mailboxes summary: Clear the login history of a mailbox operationId: resetLastLogin description: >- Deletes the stored logins of the mailbox (the SASL log and, for admins and the mailbox user itself, the successful UI logins). A `get` route that changes data: read-only API keys can call it too. Always answers `true`. parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: Done content: application/json: schema: type: boolean example: true "401": $ref: "#/components/responses/Unauthorized" /api/v1/delete/eas_cache: post: tags: - Mailboxes summary: Reset the ActiveSync devices of mailboxes operationId: deleteEasCache description: >- Deletes the ActiveSync device cache of the mailboxes, so devices sync from scratch. Needs the user ACL `eas_reset`. API keys may reset any mailbox, a mailbox user only its own. Messages per mailbox: `eas_reset` or `access_denied`. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MailboxItems" example: - john.doe@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/sogo_profile: post: tags: - Mailboxes summary: Reset the SOGo profile of mailboxes operationId: deleteSogoProfile description: >- Deletes the SOGo data of the mailboxes: profile, settings, calendars, address books and their shares. Needs the user ACL `sogo_profile_reset`. API keys may reset any mailbox, a mailbox user only its own. Messages per mailbox: `sogo_profile_reset` or `access_denied`. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MailboxItems" example: - john.doe@example.org responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/bcc-destination-options: get: tags: - Mailboxes summary: Get the possible BCC map destinations operationId: getBccDestinationOptions description: >- What a BCC map can be bound to: the domains the caller manages, per mailbox the mailbox and its aliases (aliases of other domains only if the caller manages them), and the alias domains. Keys without entries are left out. responses: "200": description: Domains, mailboxes with their addresses, alias domains content: application/json: schema: type: object properties: domains: type: array items: type: string mailboxes: type: object description: Mailbox address → the mailbox and its aliases additionalProperties: type: array items: type: string alias_domains: type: array items: type: string example: domains: - example.org mailboxes: john.doe@example.org: - john.doe@example.org - sales@example.org jane.roe@example.org: - jane.roe@example.org alias_domains: - example.net "401": $ref: "#/components/responses/Unauthorized" /api/v1/search/mailbox: post: tags: - Mailboxes summary: Search mailboxes page by page operationId: searchMailboxes description: >- Server-side search behind the mailbox table of the UI, with the request and answer format of DataTables (server-side processing): paging, sorting and a search over the mailboxes the caller manages (API keys: all; resources are not included). Read-only API keys may use it. Mailbox users get `403` with `Insufficient permissions`. Columns are addressed by number, as in the UI table: 2 address (`username`), 3 quota, 4 last mail login, 5 last password change, 6 quota in use, 7 name, 20 messages, 23 tags, 24 active. Sorting works on all of them except 23; searching on 2, 3, 7, 23 and 24. Searching in 4, 5, 6 or 20 or sorting by 23 makes the database query fail, so send these columns as not searchable/orderable. Other numbers stand for the address. `columns` is needed for a search or a sort: `columns[n]` describes column n. Each entry of `data` holds the full details of a mailbox, like `get/mailbox/{id}`. `recordsTotal` counts the mailboxes after the search too (same as `recordsFiltered`). requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/MailboxSearchRequest" example: draw: 1 start: 0 length: 25 search: value: john order: - column: 2 dir: asc columns: - searchable: false orderable: false - searchable: false orderable: false - data: username searchable: true orderable: true - searchable: false orderable: true - searchable: false orderable: true - searchable: false orderable: true - searchable: false orderable: true - data: name searchable: true orderable: true responses: "200": description: >- One page of mailboxes. A failing database query answers `{"error": "An SQL error occurred: ..."}` instead. content: application/json: schema: oneOf: - $ref: "#/components/schemas/MailboxSearchResult" - type: object properties: error: type: string examples: page: $ref: "#/components/examples/MailboxSearchPage" "401": $ref: "#/components/responses/Unauthorized" "403": description: The caller is a mailbox user or not logged in content: application/json: schema: $ref: "#/components/schemas/Error" example: type: error msg: Insufficient permissions /api/v1/edit/identity-provider: post: summary: Configure the Identity Provider description: >- Admins only (every read/write API key). Saves the configuration of the external Identity Provider (Keycloak, generic OpenID Connect or LDAP). Send the complete configuration of the provider: every setting of the provider is written, settings that are not sent get their default (flags off, `login_provisioning` off, `group_autocreate` off), and the secrets (`client_secret`, `bindpass`) must be sent again. `redirect_url_extra`, `default_template` and the `mappers`/`templates` pair are only changed when sent. Settings of other providers stay stored. The cached Keycloak access token is cleared. Changing the LDAP `id_attribute` drops the stored LDAP ids (mailboxes are linked by email again). `items` is not used. Answers `object_modified`. Errors: `required_data_missing` (`authsource` or a required setting missing: Keycloak `server_url`, `realm`, `client_id`, `client_secret`, `redirect_url`, `version`; generic-oidc `authorize_url`, `token_url`, `userinfo_url`, `client_id`, `client_secret`, `redirect_url`; LDAP `basedn`, `binddn`, `bindpass`), `invalid_authsource`, `authsource_in_use` (mailboxes use another external provider), `access_denied`. Secrets are logged as `*`. operationId: editIdentityProvider tags: - Identity Provider requestBody: content: application/json: schema: type: object required: - attr properties: items: description: Not used attr: $ref: "#/components/schemas/IdentityProviderAttributes" examples: keycloak: value: attr: authsource: keycloak server_url: https://auth.example.org realm: mailcow client_id: mailcow client_secret: client-secret-value redirect_url: https://mail.example.org redirect_url_extra: - https://webmail.example.org version: 26.1.3 login_provisioning: 1 mailpassword_flow: 0 periodic_sync: 1 import_users: 1 sync_interval: 15 offboarding: 1 offboarding_limit: 10 group_sync: 1 group_autocreate: 1 group_paths: /mailcow/* group_default_domain: example.org default_template: Default mappers: - staff templates: - Staff ldap: value: attr: authsource: ldap host: ldap1.example.org,ldap2.example.org port: 636 use_ssl: 1 use_tls: 0 ignore_ssl_error: 0 basedn: dc=example,dc=org binddn: cn=mailcow,ou=services,dc=example,dc=org bindpass: bind-password username_field: mail filter: (objectClass=inetOrgPerson) attribute_field: departmentNumber login_provisioning: 1 periodic_sync: 1 import_users: 1 sync_interval: 30 group_sync: 1 group_filter: (objectClass=groupOfNames) group_member_mode: member group_member_attribute: member default_template: Default generic-oidc: value: attr: authsource: generic-oidc authorize_url: https://sso.example.org/application/o/authorize/ token_url: https://sso.example.org/application/o/token/ userinfo_url: https://sso.example.org/application/o/userinfo/ client_id: mailcow client_secret: client-secret-value redirect_url: https://mail.example.org client_scopes: openid profile email mailcow_template login_provisioning: 1 default_template: Default responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/identity-provider: get: summary: Get Identity Provider settings description: >- The stored settings of the external Identity Provider (admins and API keys; `{}` for others). Values are returned as stored (mostly strings), also settings of a provider that was configured before. Read-only API keys get `*` instead of `client_secret`, `bindpass` and `access_token`; read/write API keys get them in plain text. Without a configuration the answer is `{"login_provisioning": 1}`. Any further path segment other than `sync-status` returns the same. operationId: getIdentityProvider tags: - Identity Provider responses: "200": description: The settings content: application/json: schema: $ref: "#/components/schemas/IdentityProviderSettings" example: authsource: keycloak server_url: https://auth.example.org realm: mailcow client_id: mailcow client_secret: "*" redirect_url: https://mail.example.org redirect_url_extra: - https://webmail.example.org version: 26.1.3 mailpassword_flow: "0" periodic_sync: "1" import_users: "1" sync_interval: "15" ignore_ssl_error: false login_provisioning: true group_paths: /mailcow/* offboarding: "1" offboarding_limit: "10" group_sync: "1" group_autocreate: "1" group_default_domain: example.org default_template: Default mappers: - staff templates: - Staff access_token: "*" "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/identity-provider-test: post: summary: Test an Identity Provider connection description: >- Admins only (every read/write API key). Tests the connection data in `attr` (the stored settings are not used, send the secrets too): Keycloak requests a client_credentials token from `/realms//protocol/openid-connect/token`, generic-oidc sends an OPTIONS request to `token_url` (both succeed on HTTP 200, 7 s timeout), LDAP connects and binds with `host`, `port`, `basedn`, `binddn`, `bindpass` (all required), `use_ssl`, `use_tls`. Nothing is stored. `items` is not used. The answer carries no details: `{"type": "success", "msg": "Task completed"}` or `{"type": "error", "msg": "Cannot edit item"}` (`access_denied` for non-admins). A missing or unknown `authsource` answers success without testing anything. operationId: testIdentityProvider tags: - Identity Provider requestBody: content: application/json: schema: type: object required: - attr properties: items: description: Not used attr: type: object required: - authsource properties: authsource: type: string enum: - keycloak - generic-oidc - ldap server_url: type: string description: Keycloak base URL realm: type: string description: Keycloak realm client_id: type: string description: Keycloak client id client_secret: type: string description: Keycloak client secret token_url: type: string description: generic-oidc token endpoint ignore_ssl_error: type: - boolean - integer - string description: Do not verify the TLS certificate host: type: string description: LDAP host, or several separated by commas port: type: - integer - string description: LDAP port basedn: type: string binddn: type: string bindpass: type: string use_ssl: type: - boolean - integer - string description: LDAPS use_tls: type: - boolean - integer - string description: StartTLS, ignored with `use_ssl` example: attr: authsource: keycloak server_url: https://auth.example.org realm: mailcow client_id: mailcow client_secret: client-secret-value ignore_ssl_error: 0 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/identity-provider: post: summary: Delete the Identity Provider configuration description: >- Admins only (every read/write API key). Deletes all Identity Provider settings, all mailbox links (`identity-link`) and the sync run history. Refused with `authsource_in_use` while any mailbox uses an external provider. The body must be valid JSON, its content is not used (e.g. `[]`). Answers `item_deleted`, or `authsource_in_use`, `access_denied`. operationId: deleteIdentityProvider tags: - Identity Provider requestBody: content: application/json: schema: description: Not used, any JSON value example: [] responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/filter: post: tags: - Mailbox filters summary: Create a Sieve filter operationId: addFilter description: >- Adds a Sieve script to a mailbox. Needs the ACL `filters`, which admins (API keys) and domain admins have, mailbox users not. `username` is required for API keys and domain admins (`no_user_defined`). The script is checked with a Sieve parser (`sieve_error`). Activating a filter deactivates the other filters of the same type and mailbox. Errors: `access_denied`, `script_empty`, `value_missing`, `filter_type`. Success: `mailbox_modified`. requestBody: required: true content: application/json: schema: type: object required: - username - script_data - script_desc - filter_type properties: username: type: string description: Mailbox of the filter script_desc: type: string description: Description script_data: type: string description: The Sieve script filter_type: type: string enum: - prefilter - postfilter description: Runs before (prefilter) or after (postfilter) the user's own filters active: type: [integer, string] enum: [0, 1, "0", "1"] default: 0 example: username: john.doe@example.org script_desc: Move newsletters script_data: |- require "fileinto"; if header :contains "List-Id" "newsletter" { fileinto "Newsletters"; } filter_type: prefilter active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/filters/all: get: tags: - Mailbox filters summary: Get all Sieve filters operationId: getFilters description: >- The Sieve filters of all mailboxes the caller manages, `{}` when there are none. responses: "200": description: List of filters content: application/json: schema: type: array items: $ref: "#/components/schemas/SieveFilter" example: - active: 1 id: 7 username: john.doe@example.org filter_type: prefilter script_data: |- require "fileinto"; if header :contains "List-Id" "newsletter" { fileinto "Newsletters"; } script_desc: Move newsletters "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/filters/{mailbox}: get: tags: - Mailbox filters summary: Get the Sieve filters of a mailbox operationId: getMailboxFilters description: >- The Sieve filters of the mailbox, `{}` when there are none or the caller has no access. If `{mailbox}` is no address, the session user's filters are returned (nothing for API keys). parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: List of filters content: application/json: schema: type: array items: $ref: "#/components/schemas/SieveFilter" "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/filter: post: tags: - Mailbox filters summary: Update Sieve filters operationId: editFilter description: >- Changes filters by ID; fields that are not sent keep their value. Needs the ACL `filters` (admins, domain admins). Activating a filter deactivates the other filters of the same type and mailbox. Messages per ID: `mailbox_modified` or `access_denied` (unknown ID or no access), `sieve_error`, `filter_type`. requestBody: required: true content: application/json: schema: type: object required: - items - attr properties: items: description: Filter ID or list of IDs oneOf: - type: integer - type: array items: type: integer attr: type: object properties: script_desc: type: string script_data: type: string filter_type: type: string enum: - prefilter - postfilter active: type: [integer, string] enum: [0, 1, "0", "1"] example: items: - 7 attr: active: 0 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/filter: post: tags: - Mailbox filters summary: Delete Sieve filters operationId: deleteFilter description: >- Deletes filters by ID; IDs that are not numeric are skipped. Needs the ACL `filters` (admins, domain admins). Messages per ID: `delete_filter` or `access_denied`. requestBody: required: true content: application/json: schema: type: array description: Filter IDs items: type: integer example: - 7 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/active-user-sieve/{mailbox}: get: tags: - Mailbox filters summary: Get the active Sieve script of a mailbox operationId: getActiveUserSieve description: >- The Sieve script the user activated in its mail client or SOGo (read from Dovecot), as a list with one string. `{}` when there is none or the caller has no access. If `{mailbox}` is no address, the session user's script is read (nothing for API keys). parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: The script as the only element of a list content: application/json: schema: type: array maxItems: 1 items: type: string example: - |- require ["fileinto"]; if header :contains "subject" "invoice" { fileinto "Invoices"; } "401": $ref: "#/components/responses/Unauthorized" /api/v1/add/mailbox-policy: post: tags: - Mailbox antispam policies summary: Add a sender to the allow or deny list of a mailbox operationId: addMailboxPolicy description: >- Adds a sender address or pattern to the spam allow list (`wl`, mail is never treated as spam) or deny list (`bl`, mail is rejected) of a mailbox. Needs the user ACL `spam_policy`. `username` is required for all callers; a mailbox user may only name its own mailbox. Errors: `access_denied`, `policy_list_from_invalid` (characters other than letters, digits, `@ _ . - *`), `policy_list_from_exists`. Success: `mailbox_modified`. requestBody: required: true content: application/json: schema: type: object required: - username - object_list - object_from properties: username: type: string description: Mailbox object_list: type: string enum: - wl - bl description: wl = allow list, bl = deny list object_from: type: string description: Sender address, domain or pattern with `*` example: username: john.doe@example.org object_list: bl object_from: "*@spam.example.com" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/policy_wl_mailbox/{mailbox}: get: tags: - Mailbox antispam policies summary: Get the allow list of a mailbox operationId: getMailboxPolicyAllowList description: >- The allow list entries that apply to the mailbox: its own and those of its domain (`object` tells which). `{}` when there are none, the mailbox does not exist or the caller has no access. If `{mailbox}` is no address, the session user's list is returned (nothing for API keys). parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: List of entries content: application/json: schema: type: array items: $ref: "#/components/schemas/SpamPolicyEntry" example: - object: john.doe@example.org value: "*@partner.example.com" prefid: 12 - object: example.org value: newsletter@example.net prefid: 4 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/policy_bl_mailbox/{mailbox}: get: tags: - Mailbox antispam policies summary: Get the deny list of a mailbox operationId: getMailboxPolicyDenyList description: >- Like `get/policy_wl_mailbox/{mailbox}` for the deny list. parameters: - name: mailbox in: path required: true description: Mailbox address schema: type: string example: john.doe@example.org responses: "200": description: List of entries content: application/json: schema: type: array items: $ref: "#/components/schemas/SpamPolicyEntry" example: - object: john.doe@example.org value: "*@spam.example.com" prefid: 13 "401": $ref: "#/components/responses/Unauthorized" /api/v1/delete/mailbox-policy: post: tags: - Mailbox antispam policies summary: Delete allow or deny list entries of mailboxes operationId: deleteMailboxPolicy description: >- Deletes allow or deny list entries of mailboxes by their `prefid`. Needs the user ACL `spam_policy` and access to the mailbox of the entry (entries of a domain are refused, use `delete/domain-policy`). Messages per ID: `items_deleted` or `access_denied`. requestBody: required: true content: application/json: schema: type: array description: prefid values items: type: integer example: - 13 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/add/rsetting: post: summary: Add Rspamd settings rule description: >- Adds a rule to the Rspamd settings map (admins only, an API key may). `content` is the body of an Rspamd settings entry (`priority`, `rcpt`, `from`, `authenticated`, `apply` ...); active rules are handed to Rspamd as `additional_settings_`. The presets of `GET /api/v1/get/presets/rspamd` are examples. Messages: `settings_map_added`; `map_content_empty` (danger) when `content` is empty; `access_denied` for non-admins. `desc` is required by the database: without it the insert fails and the answer is empty. operationId: addRspamdSetting tags: - Rspamd requestBody: content: application/json: schema: $ref: "#/components/schemas/RspamdSettingAttributes" example: desc: Disable all but DKIM and rate limit for authenticated users content: |- priority = 10; authenticated = yes; apply "default" { symbols_enabled = ["DKIM_SIGNED", "RATELIMITED", "RATELIMIT_UPDATE", "RATELIMIT_CHECK"]; } active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/rsetting/{id}: get: summary: Get Rspamd settings rules description: >- Returns one rule of the Rspamd settings map, or all rules with `all` (a list of the same objects). Admins only (an API key may); other callers, unknown IDs and an empty map get `{}`. operationId: getRspamdSettings tags: - Rspamd parameters: - name: id in: path required: true description: "`all` or the ID of a rule" schema: type: string example: all responses: "200": description: The rule, or the list of rules for `all` content: application/json: schema: oneOf: - type: array items: $ref: "#/components/schemas/RspamdSetting" - $ref: "#/components/schemas/RspamdSetting" example: - id: 1 desc: Disable Rspamd for example.org content: |- priority = 10; rcpt = "/.*@example\.org$/"; from = "/.*/"; apply "default" { MAILCOW_WHITE = -9999.0; } symbols [ "MAILCOW_WHITE" ] active: 1 "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/rsetting: post: summary: Edit Rspamd settings rules description: >- Changes rules of the Rspamd settings map (admins only, an API key may). Only the attributes that are sent change; an empty `desc` or `content` keeps the old value. `content` is trimmed. Messages: `object_modified` (with all requested IDs) for every changed rule, `settings_map_invalid` (danger) for an unknown ID, `access_denied` for non-admins. operationId: editRspamdSetting tags: - Rspamd requestBody: content: application/json: schema: type: object properties: items: description: ID or list of IDs of the rules oneOf: - type: array items: type: [integer, string] - type: [integer, string] attr: type: object properties: desc: type: string description: Short description (empty = keep) content: type: string description: Rule content (empty = keep) active: type: [boolean, integer, string] description: 1 = the rule is handed to Rspamd, 0 = inactive (not sent = keep) required: - items - attr example: items: - 1 attr: desc: Disable Rspamd for example.org active: "0" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/rsetting: post: summary: Delete Rspamd settings rules description: >- Deletes rules of the Rspamd settings map (admins only, an API key may). Answers `settings_map_removed` for every ID, also for IDs that do not exist; `access_denied` for non-admins. operationId: deleteRspamdSettings tags: - Rspamd requestBody: content: application/json: schema: description: IDs of the rules (a single ID works too) oneOf: - type: array items: type: [integer, string] - type: [integer, string] example: - 1 - 2 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/rspamd-map: post: summary: Edit Rspamd global regex maps description: >- Replaces the content of global Rspamd regex maps (Configuration > Global filters in the UI; admins only, an API key may). Each line is a regular expression like `/^spam@example\.org$/i`, lines starting with `#` are comments. The UI checks the expressions before it saves, the API does not. Every map takes about a second (the file is touched again for Rspamd to reload it). Messages: `object_modified` per map, `global_map_invalid` (danger) for an unknown map name, `global_map_write_error` (danger) when the file cannot be written, `access_denied` for non-admins. A known map whose file does not exist is not written but still answered with `object_modified`. operationId: editRspamdMap tags: - Rspamd requestBody: content: application/json: schema: type: object properties: items: description: File name or list of file names of the maps; all of them get the same content oneOf: - type: array items: $ref: "#/components/schemas/RspamdRegexMap" - $ref: "#/components/schemas/RspamdRegexMap" attr: type: object properties: rspamd_map_data: type: string description: New content of the map (trimmed, replaces the whole file) required: - rspamd_map_data required: - items - attr example: items: - global_smtp_from_blacklist.map attr: rspamd_map_data: |- # Envelope senders to reject /^spammer@example\.com$/i /@spam\.example\.net$/i responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/rspamd/actions: get: summary: Get Rspamd action statistics description: >- Returns how many messages Rspamd handled with each action (from Rspamd's `/stat`), as `[action, count]` pairs. Greylisted messages are counted as `soft reject`. Admins only (an API key may); other callers, or an Rspamd that does not answer, get `{}`. operationId: getRspamdActions tags: - Rspamd responses: "200": description: List of `[action, count]` pairs content: application/json: schema: type: array items: type: array prefixItems: - type: string description: Action, e.g. `reject`, `soft reject`, `rewrite subject`, `add header`, `no action` - type: integer description: Number of messages minItems: 2 maxItems: 2 example: - - reject - 38 - - soft reject - 1 - - rewrite subject - 0 - - add header - 6 - - no action - 255 "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/presets/{kind}: get: summary: Get Rspamd or Sieve presets description: >- Returns the example presets the UI offers: `rspamd` for Rspamd settings rules (see `POST /api/v1/add/rsetting`), `sieve` for Sieve filters. `headline` is translated into the language of the session. Admins and domain admins (an API key may); other callers get `{}`. operationId: getPresets tags: - Rspamd - Global filters parameters: - name: kind in: path required: true description: Kind of presets schema: type: string enum: - rspamd - sieve example: rspamd responses: "200": description: List of presets content: application/json: schema: type: array items: $ref: "#/components/schemas/Preset" example: - name: preset_1 headline: Disable all but DKIM and rate limit for authenticated users content: |- priority = 10; authenticated = yes; apply "default" { symbols_enabled = ["BCC", "DKIM_SIGNED", "RATELIMITED", "RATELIMIT_UPDATE", "RATELIMIT_CHECK", "DYN_RL_CHECK", "HISTORY_SAVE", "MILTER_HEADERS", "ARC_SIGNED"]; } - name: preset_4 headline: Disable Rspamd for a domain content: |- priority = 10; rcpt = "/.*@example.org$/"; from = "/.*/"; apply "default" { MAILCOW_WHITE = -9999.0; } symbols [ "MAILCOW_WHITE" ] "401": $ref: "#/components/responses/Unauthorized" /api/v1/add/global-filter: post: summary: Save a global Sieve filter description: >- Replaces the global Sieve prefilter (runs before the filters of the mailboxes) or postfilter (runs after them) and restarts Dovecot. Admins only (an API key may). The script is parsed first and not saved when it has errors. Messages: `dovecot_restart_success`, or `dovecot_restart_failed` (warning), then `global_filter_written`; `sieve_error` (danger) with the parser error; `invalid_filter_type` (danger); `global_filter_write_error` (danger); `access_denied` for non-admins. operationId: addGlobalFilter tags: - Global filters requestBody: content: application/json: schema: type: object properties: filter_type: type: string enum: - prefilter - postfilter description: Which global filter to replace script_data: type: string description: The Sieve script (CRLF and CR line ends become LF) required: - filter_type - script_data example: filter_type: postfilter script_data: |- require "fileinto"; if header :contains "X-Spam-Flag" "YES" { fileinto "Junk"; } responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/global_filters/all: get: summary: Get the global Sieve filters description: >- Returns the global Sieve prefilter and postfilter. Admins only (an API key may); other callers get `{}`. operationId: getGlobalFilters tags: - Global filters responses: "200": description: Both scripts content: application/json: schema: $ref: "#/components/schemas/GlobalFilters" example: prefilter: | # global_sieve_before script # global_sieve_before -> user sieve_before (mailcow UI) -> user sieve_after (mailcow UI) -> global_sieve_after postfilter: |- # global_sieve_after script require "fileinto"; if header :contains "X-Spam-Flag" "YES" { fileinto "Junk"; } "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/global_filters/{filter_type}: get: summary: Get one global Sieve filter description: >- Returns the global Sieve prefilter or postfilter as a JSON string. Admins only (an API key may); other callers and an empty script get `{}`. operationId: getGlobalFilter tags: - Global filters parameters: - name: filter_type in: path required: true description: Which global filter schema: type: string enum: - prefilter - postfilter example: prefilter responses: "200": description: The Sieve script content: application/json: schema: type: string example: | # global_sieve_before script # global_sieve_before -> user sieve_before (mailcow UI) -> user sieve_after (mailcow UI) -> global_sieve_after "401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/quota_notification: post: summary: Edit quota notification settings description: >- Saves sender, subject and template of the mails users get when their mailbox crosses 80 % and 95 % of its quota (admins only, an API key may). `items` is ignored. Every call writes all three values: one that is not sent becomes empty. The current values cannot be read through the API. Messages: `saved_settings`; `redis_error` (danger); `access_denied` for non-admins. operationId: editQuotaNotification tags: - Quota notifications requestBody: content: application/json: schema: type: object properties: items: description: Ignored attr: type: object properties: sender: type: string description: Sender address (control characters and non-ASCII bytes are removed; an invalid address becomes empty) subject: type: string description: Subject html_tmpl: type: string description: HTML template (empty = the default template) required: - attr example: items: - quota_notification attr: sender: quota-warning@example.org subject: Quota warning html_tmpl: "" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/quota_notification_bcc: post: summary: Edit quota notification BCC of a domain description: >- Sets the addresses that get a copy of every quota warning of a domain (the subject gets the username appended). Admins and domain admins of the domain (an API key may; the domain or alias domain must exist). `items` is ignored. The list replaces the old one; the current list cannot be read through the API. Messages: `saved_settings`; `goto_invalid` (danger) for an invalid address, which is left out while the others are saved; `redis_error` (danger); `access_denied` for an unknown or foreign domain. operationId: editQuotaNotificationBcc tags: - Quota notifications requestBody: content: application/json: schema: type: object properties: items: description: Ignored attr: type: object properties: domain: type: string description: Domain or alias domain bcc_rcpt: type: string description: Addresses, separated by space, comma, semicolon or newline active: type: [boolean, integer, string] description: 1 = send the copies; becomes 0 when no valid address is left required: - domain required: - attr example: items: - quota_bcc attr: domain: example.org bcc_rcpt: |- postmaster@example.org helpdesk@example.org active: "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/admin/all: get: summary: Get administrators description: >- Lists all administrators with their details. Domain administrators are not part of this list (see `get/domain-admin/all`). The API key itself (account `API`) is no administrator account and is not listed. operationId: getAdmins tags: - Administrators responses: "200": description: The administrators, `{}` if there are none content: application/json: schema: type: array items: $ref: "#/components/schemas/Admin" example: - username: admin tfa_active: 1 tfa_active_int: 1 active: 1 active_int: 1 created: "2026-09-21 10:23:08" attributes: force_tfa: "0" force_pw_update: "0" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/admin/{username}: get: summary: Get administrator description: Details of one administrator. operationId: getAdmin tags: - Administrators parameters: - name: username in: path required: true description: Username of the administrator schema: type: string example: admin responses: "200": description: The administrator, `{}` if there is no administrator of this name content: application/json: schema: $ref: "#/components/schemas/Admin" example: username: admin tfa_active: 0 tfa_active_int: 0 active: 1 active_int: 1 created: "2026-09-21 10:23:08" attributes: force_tfa: "0" force_pw_update: "0" "401": $ref: "#/components/responses/Unauthorized" /api/v1/add/admin: post: summary: Create administrator description: >- Creates an administrator account with full access to mailcow. The username is converted to lowercase; it may only contain letters, digits, `_`, `.` and `-` and must not be used by another administrator or domain administrator. The password must match `password2` and the password policy. Success: `admin_added`. Errors: `username_invalid`, `object_exists`, `password_complexity`, `password_mismatch`. operationId: addAdmin tags: - Administrators requestBody: required: true content: application/json: schema: type: object properties: username: type: string description: Username (letters, digits, `_`, `.`, `-`; stored in lowercase) password: type: string description: Password, must match the password policy password2: type: string description: The password again active: type: [integer, string, boolean] description: >- 1 = the account can log in, 0 = disabled. Missing counts as 0: send `1` for a usable account. default: 0 required: - username - password - password2 example: username: admin2 password: "Rand0m-Passw0rd!" password2: "Rand0m-Passw0rd!" active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/admin: post: summary: Edit administrators description: >- Changes administrator accounts. Attributes that are not sent keep their value, except `force_tfa` and `force_pw_update`, which are set to 0 when they are missing. The password is only changed when `password` is not empty. Renaming an account moves its two-factor authentication and the sync job sources it created to the new name (FIDO2 keys stay with the old name). One message per account. Success: `admin_modified`. Errors: `access_denied` (no administrator of this name), `username_invalid` (invalid `username_new` or the name of another administrator), warning `no_active_admin` (the last active administrator cannot be deactivated). A password that does not match `password2` or the policy (`password_mismatch`, `password_complexity`) stops the call. operationId: editAdmin tags: - Administrators requestBody: required: true content: application/json: schema: type: object properties: items: description: Username of the administrator, or a list of usernames oneOf: - type: string - type: array items: type: string attr: type: object properties: active: type: [integer, string, boolean] description: 1 = active, 0 = disabled username_new: type: string description: New username (letters, digits, `_`, `.`, `-`) password: type: string description: New password; empty or missing keeps the password password2: type: string description: The new password again force_tfa: type: [integer, string, boolean] description: 1 = the administrator must set up two-factor authentication at the next login default: 0 force_pw_update: type: [integer, string, boolean] description: 1 = the administrator must change the password at the next login default: 0 disable_tfa: type: [integer, string, boolean] description: >- Present (with any value) = deactivate the two-factor authentication of the account required: - items - attr example: items: - admin2 attr: active: 1 password: "N3w-Passw0rd!" password2: "N3w-Passw0rd!" force_pw_update: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/delete/admin: post: summary: Delete administrators description: >- Deletes administrator accounts with their two-factor authentication and FIDO2 keys; sync job sources they created are released. One message per account. Success: `admin_removed`. Errors: `username_invalid` (no administrator of this name), warning `cannot_delete_self` (the account of the current login, never the case for an API key). operationId: deleteAdmin tags: - Administrators requestBody: required: true content: application/json: schema: type: array description: Usernames of the administrators items: type: string example: - admin2 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/get/passwordpolicy: get: summary: Get password policy description: >- The password policy for mailboxes, administrators and domain administrators. Any further path segment except `html` gives the same answer. operationId: getPasswordPolicy tags: - Password policy responses: "200": description: The policy content: application/json: schema: $ref: "#/components/schemas/PasswordPolicy" example: length: "8" chars: "1" special_chars: "0" lowerupper: "1" numbers: "1" "401": $ref: "#/components/responses/Unauthorized" /api/v1/get/passwordpolicy/html: get: summary: Get password policy as HTML description: >- The password policy as an HTML snippet in the default language, as the mailcow UI shows it below password fields. operationId: getPasswordPolicyHtml tags: - Password policy responses: "200": description: The HTML snippet as a JSON string content: application/json: schema: type: string example:

- Minimum password length is 8
- Must contain at least one alphabetic character
- Must contain lowercase and uppercase characters
- Must contain at least one number

"401": $ref: "#/components/responses/Unauthorized" /api/v1/edit/passwordpolicy: post: summary: Edit password policy description: >- Changes the password policy; attributes that are not sent keep their value. The policy is checked when a password is set, existing passwords are not affected. `items` is not used. Success: `password_policy_saved`. operationId: editPasswordPolicy tags: - Password policy requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: length: type: [integer, string] description: Minimum length; values below 3 keep the current length chars: type: [integer, string, boolean] description: 1 = at least one letter lowerupper: type: [integer, string, boolean] description: 1 = lowercase and uppercase letters special_chars: type: [integer, string, boolean] description: 1 = at least one character that is no letter or digit numbers: type: [integer, string, boolean] description: 1 = at least one digit required: - attr example: attr: length: 12 chars: 1 lowerupper: 1 special_chars: 0 numbers: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/reset-password-notification: post: summary: Edit password reset e-mail description: >- Sets sender, subject and templates of the e-mail a mailbox user gets to reset a forgotten password (sent to the recovery address of the mailbox). All four values are replaced: a missing value is saved as empty; without both templates the default templates are used. Password reset is only offered while sender and subject are set. Subject and templates may use the placeholders `{{username}}`, `{{username2}}` (recovery address), `{{date}}`, `{{token_lifetime}}` (minutes), `{{hostname}}` and `{{link}}`. `items` is not used. Success: `saved_settings`. operationId: editPasswordResetNotification tags: - Password policy requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: from: type: string description: Sender address; an invalid address is saved as empty subject: type: string description: Subject text_tmpl: type: string description: Plain text template html_tmpl: type: string description: HTML template (the text template becomes its alternative part) required: - attr example: attr: from: noreply@example.org subject: Reset your password text_tmpl: "Hello {{username}}, open {{link}} within {{token_lifetime}} minutes to set a new password." html_tmpl: "

Hello {{username}},

Set a new password within {{token_lifetime}} minutes.

" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/app_links: post: summary: Edit app links description: >- Replaces the custom links of the "Apps" menu. The links are sent as four lists of equal length, one entry per link; if the lengths differ, nothing is saved but `app_links` is reported anyway. An empty list removes all custom links. The links of mailcow itself (SOGo, ...) are set in vars.local.inc.php and are not affected. `items` is not used. Success: `app_links`. Error: `demo_mode_enabled`. operationId: editAppLinks tags: - UI customization requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: app: type: array description: Names of the links items: type: string href: type: array description: URLs shown on the login page and to users without `user_href` items: type: string user_href: type: array description: >- URLs for logged-in accounts, `%u` is replaced with the username; empty = `href` items: type: string hide: type: array description: >- Per link: `"0"` or `0` shows it on the login page, any other value (also `false`) hides it there items: type: [string, integer, boolean] required: - attr example: attr: app: - Wiki - Webmail (Roundcube) href: - https://wiki.example.org - https://mail.example.org/rc/ user_href: - https://wiki.example.org - https://mail.example.org/rc/?_user=%u hide: - "0" - "1" responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/custom_login: post: summary: Edit login page options description: >- Sets the options of the login pages. All four options are replaced: a missing option counts as 0. `items` is not used. Success: `custom_login_modified`. Error: `demo_mode_enabled`. operationId: editCustomLogin tags: - UI customization requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: hide_user_quicklink: type: [integer, string, boolean] description: 1 = hide the link to the user login page default: 0 hide_domainadmin_quicklink: type: [integer, string, boolean] description: 1 = hide the link to the domain administrator login page default: 0 hide_admin_quicklink: type: [integer, string, boolean] description: 1 = hide the link to the administrator login page default: 0 force_sso: type: [integer, string, boolean] description: >- 1 = with an OIDC identity provider configured, show only the Single Sign-On button instead of the mailcow login form default: 0 required: - attr example: attr: hide_user_quicklink: 0 hide_domainadmin_quicklink: 1 hide_admin_quicklink: 1 force_sso: 0 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/ip_check: post: summary: Edit IP check description: >- Opt-in for the IP check of the dashboard, which asks the third party services ipv4.mailcow.email and ipv6.mailcow.email for the public addresses of the server. `items` is not used. Success: `ip_check_opt_in_modified`. Error: `demo_mode_enabled`. operationId: editIpCheck tags: - UI customization requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: ip_check_opt_in: type: [integer, string, boolean] description: 1/true = enabled; anything else or missing = disabled default: 0 required: - attr example: attr: ip_check_opt_in: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/ui_texts: post: summary: Edit UI texts description: >- Sets the texts of the mailcow UI. All values are replaced: a missing value is saved as empty, which means the default for the three names. `items` is not used. Success: `ui_texts`. Error: `demo_mode_enabled`. operationId: editUiTexts tags: - UI customization requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: title_name: type: string description: Title of the browser window (plain text); empty = " - mail UI" main_name: type: string description: Name shown on the start page (plain text); empty = " - mail UI" apps_name: type: string description: Name of the "Apps" menu (plain text); empty = "Apps" help_text: type: string description: Help text below the login form (HTML) ui_footer: type: string description: Footer of every page (HTML) ui_announcement_text: type: string description: Announcement for all logged-in accounts and on the login page (HTML) ui_announcement_type: type: string enum: - info - warning - danger description: Style of the announcement; other values are saved as empty ui_announcement_active: type: [integer, string, boolean] description: Not empty and not 0 = show the announcement default: 0 required: - attr example: attr: title_name: Example Mail main_name: Example Mail apps_name: Apps help_text: "Questions? Write to support@example.org." ui_footer: "Imprint" ui_announcement_text: Maintenance on Saturday from 8 to 10 am. ui_announcement_type: warning ui_announcement_active: 1 responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" /api/v1/edit/ui_theme: post: summary: Edit UI theme description: >- Selects the theme of the mailcow UI. Themes are folders `data/web/css/themes//` with a `theme.css`. `items` is not used. Success: `ui_theme_modified`. Errors: `ui_theme_invalid` (no theme of this name), `demo_mode_enabled`. operationId: editUiTheme tags: - UI customization requestBody: required: true content: application/json: schema: type: object properties: attr: type: object properties: ui_theme: description: >- Folder name of the theme; empty or missing = the default theme. A list counts with its first entry. oneOf: - type: string - type: array items: type: string required: - attr example: attr: ui_theme: servercow responses: "200": $ref: "#/components/responses/WriteResult" "401": $ref: "#/components/responses/Unauthorized" "403": $ref: "#/components/responses/Forbidden" tags: - name: Domains description: >- Domains, alias domains, domain templates, MTA-STS policies and domain-wide footers - name: Domain antispam policies description: Sender allow lists (whitelists) and deny lists (blacklists) of domains - name: Mailboxes description: Mailboxes, their templates, tags, ACLs and logins - name: Aliases description: You can manage aliases - name: Sync jobs description: Using Syncjobs you can sync your mails with other email servers - name: Logs description: Logs of the mailcow containers and the UI - name: Queue Manager description: Manage the postfix mail queue - name: Quarantine description: Check what emails went to quarantine - name: Fail2Ban description: Manage the Netfilter fail2ban options - name: DKIM description: Generate, import, copy and delete DKIM keys - name: Domain admin description: Domain administrators, their domains and ACLs - name: Single Sign-On description: Login tokens for domain administrators - name: Address Rewriting description: BCC maps and recipient maps - name: Outgoing TLS Policy Map Overrides description: TLS policies for outgoing connections to single destinations - name: oAuth Clients description: Clients that use mailcow as OAuth2 server - name: API Keys description: API keys; service keys from mailcow.conf are read-only - name: Groups description: Mailbox groups (distribution lists, send-as rights, templates) - name: Routing description: Sender-dependent transports (relay hosts) and transport maps for outgoing mail - name: Resources description: Bookable resources (rooms, equipment, groups) for SOGo - name: App Passwords description: Separate passwords of a mailbox for single clients and protocols - name: Status description: Containers, mail storage, host and version - name: Ratelimits description: Edit domain ratelimits - name: Cross-Origin Resource Sharing (CORS) description: Origins and methods that may call the API from a browser - name: Identity Provider description: >- External Identity Provider (Keycloak, OpenID Connect, LDAP): settings, identity sync and links of mailboxes - name: Mailbox user settings description: >- Settings a mailbox user changes on its own page (with the matching user ACL): time-limited aliases, spam score, TLS policy, tagged mail, Pushover. API keys can change them for any mailbox and must name the mailbox; a mailbox user only for its own. - name: Mailbox filters description: Sieve filters of mailboxes, managed by admins and domain admins - name: Mailbox antispam policies description: Sender allow and deny lists of mailboxes - name: Forwarding Hosts description: >- Hosts whose incoming mail is accepted unconditionally, e.g. servers that forward mail to mailcow - name: Rspamd description: Rspamd settings rules, global regex maps, presets and statistics - name: Global filters description: Global Sieve filters that run before and after the filters of the mailboxes, Sieve presets - name: Quota notifications description: Mails to users whose mailbox is nearly full - name: Administrators description: Administrator accounts - name: Password policy description: Password rules and the password reset e-mail - name: UI customization description: Texts, links, login page and theme of the mailcow UI