Files
mailcow-dockerized/data/web/api/openapi.yaml
T

15166 lines
511 KiB
YAML

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_<name> 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=<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/<mailbox>); 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://<MAILCOW_HOSTNAME>/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 <address>`)
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=<john.doe@example.org>, method=PLAIN, rip=198.51.100.7, lip=172.22.1.250, mpid=310, TLS, session=<c0kD4l8L2N7GZGMH>"
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\" <john.doe@example.org>"
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://<mailcow host>/domainadmin/mailbox?sso_token=<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 <qid>`; 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: "<p>Example Org &middot; {= from_name =}</p>"
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 `<selector>._domainkey.<domain>`; `{}` 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.<domain>/.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=<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 <qid>` 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 <sender@example.org>
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: <bounces@send.example.com>\r\nFrom: Prize <bounces@send.example.com>\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
`<server_url>/realms/<realm>/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_<id>`. 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: <p class="help-block small">- Minimum password length is 8<br>- Must contain at least one alphabetic character<br>- Must contain lowercase and uppercase characters<br>- Must contain at least one number</p>
"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: "<p>Hello {{username}},</p><p><a href=\"{{link}}\">Set a new password</a> within {{token_lifetime}} minutes.</p>"
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 = "<hostname> - mail UI"
main_name:
type: string
description: Name shown on the start page (plain text); empty = "<hostname> - 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 <a href=\"mailto:support@example.org\">support@example.org</a>."
ui_footer: "<a href=\"https://example.org/imprint\">Imprint</a>"
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/<name>/` 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