Managing Mailboxes
Create and manage email accounts, distribution lists, and groups via the Stalwart admin API — including authentication, PATCH format, and quota configuration.
Overview
All mailbox management in Stalwart — creating accounts, setting up distribution lists, and managing groups — is done through the /api/principal endpoint. The admin panel provides a UI for common operations, but the API is the authoritative interface and is required for any automated or programmatic provisioning.
Key Concepts
Principal — Stalwart's unified model for any entity that can send, receive, or route mail. The three principal types relevant to mailbox management are:
| Type | Description |
|---|---|
| individual | A standard mailbox with a single user. Has a password and can log in via IMAP/JMAP. |
| group | A named collection of individuals. Mail to the group address is delivered to all members. Equivalent to a distribution list when members are individual accounts. |
| list | A mailing list with subscribers. Supports moderated posting and public/private subscription. |
Authentication — All Stalwart admin API requests use HTTP Basic authentication. The Authorization header must contain Basic <base64(username:password)>. Bearer tokens are not valid for the admin API.
PATCH Format — Stalwart uses a specific array-of-operations format for all PATCH requests. Each operation has action, field, and value keys. This differs from JSON Patch (RFC 6902).
Creating an Account
POST /api/principal
Content-Type: application/json
Authorization: Basic <credentials>
{
"type": "individual",
"name": "alice",
"emails": ["alice@dramwell.ai"],
"secrets": ["initialPassword123!"],
"quota": 1073741824
}
Key fields:
| Field | Description |
|---|---|
type |
individual, group, or list |
name |
The login name (must be unique) |
emails |
Array of email addresses associated with this account |
secrets |
Array containing the initial plaintext password (hashed on storage) |
quota |
Storage quota in bytes. 0 means unlimited. 1073741824 = 1 GB |
A successful creation returns HTTP 200 with the principal name in the response body.
Updating an Account
All updates use PATCH with the array-of-operations format:
PATCH /api/principal/alice
Content-Type: application/json
Authorization: Basic <credentials>
[
{"action": "set", "field": "quota", "value": 2147483648},
{"action": "set", "field": "description", "value": "Alice Smith — Engineering"}
]
To add or remove email aliases:
[
{"action": "addItem", "field": "emails", "value": "a.smith@dramwell.ai"},
{"action": "removeItem", "field": "emails", "value": "oldAlias@dramwell.ai"}
]
Available actions: set, addItem, removeItem.
Creating a Distribution List (Group)
POST /api/principal
Content-Type: application/json
Authorization: Basic <credentials>
{
"type": "group",
"name": "team-engineering",
"emails": ["engineering@dramwell.ai"],
"members": ["alice", "bob", "carol"]
}
Mail sent to engineering@dramwell.ai is delivered to all members listed. Members are referenced by their principal name (login name), not their email address.
To add a member to an existing group:
PATCH /api/principal/team-engineering
[{"action": "addItem", "field": "members", "value": "dave"}]
Quota Management
Quota is set in bytes at the account level. Common values:
| Quota | Bytes |
|---|---|
| 500 MB | 524288000 |
| 1 GB | 1073741824 |
| 5 GB | 5368709120 |
| Unlimited | 0 |
To check current usage for an account, fetch the principal and read the usedQuota field:
GET /api/principal/alice
The response includes usedQuota (bytes currently used) alongside the configured quota ceiling.
Deleting an Account
DELETE /api/principal/alice
Authorization: Basic <credentials>
Deletion is permanent and immediate. All stored messages are removed. Before deleting a production account, verify no active services depend on it and consider exporting mail via JMAP first.
Tips
- Always use the principal
name(login name) as the identifier in API paths — not the email address. The name is the stable identifier; email addresses can change via PATCH. - When creating accounts programmatically (e.g., during customer org provisioning), generate a strong random initial password and immediately send the user a password reset link rather than transmitting the initial password.
- If the API returns a 401 despite correct credentials, verify you are encoding
username:passwordas a single base64 string — not encoding them separately.
Related Articles
Was this article helpful?