Skip to content

Mailboxes API

Mailboxes represent individual email accounts within a domain. Each mailbox has a local part (the part before @), a password hashed with Argon2id, a quota, and a Maildir on disk. Postfix and Dovecot pick up new mailboxes immediately via SQL lookups — no service reload is needed.

All endpoints require authentication. domain_admin users are scoped to their assigned domains.

GET /api/v1/mailboxes

Returns a paginated list of mailboxes.

Query parameters:

ParameterTypeRequiredDescription
domain_idstring (UUID)Yes (for domain_admin)Filter by domain.
activebooleanNoFilter by active status.
cursorstringNoPagination cursor.
limitintegerNoItems per page (default 50, max 200).

Example:

Terminal window
curl "https://mail.example.com/api/v1/mailboxes?domain_id=0192abc0-..." \
-H "Authorization: Bearer vectis_sk_abc123..."

Response:

{
"data": [
{
"id": "0192abc0-def1-7000-8000-000000000010",
"domain_id": "0192abc0-def1-7000-8000-000000000001",
"local_part": "alice",
"display_name": "Alice Johnson",
"quota_mb": 2048,
"active": true,
"created_at": "2026-03-20T14:30:00Z",
"updated_at": "2026-03-20T14:30:00Z"
},
{
"id": "0192abc0-def1-7000-8000-000000000011",
"domain_id": "0192abc0-def1-7000-8000-000000000001",
"local_part": "bob",
"display_name": "Bob Smith",
"quota_mb": 1024,
"active": true,
"created_at": "2026-03-21T09:15:00Z",
"updated_at": "2026-03-21T09:15:00Z"
}
],
"meta": {
"request_id": "...",
"timestamp": "...",
"pagination": { "next_cursor": "", "has_more": false }
}
}
POST /api/v1/mailboxes

Creates a new mailbox. The password is hashed with Argon2id before storage. A Maildir directory structure is created on disk at /var/vectis/mail/<domain>/<local_part>/Maildir/{cur,new,tmp}.

Request body:

FieldTypeRequiredDefaultDescription
domain_idstring (UUID)YesID of the parent domain.
local_partstringYesLocal part of the email address (before @).
passwordstringYesPlaintext password (hashed server-side with Argon2id).
display_namestringNonullFull name of the mailbox owner.
quota_mbintegerNo1024Mailbox quota in megabytes.
activebooleanNotrueWhether the mailbox can send and receive mail.

Example:

Terminal window
curl -X POST https://mail.example.com/api/v1/mailboxes \
-H "Authorization: Bearer vectis_sk_abc123..." \
-H "Content-Type: application/json" \
-d '{
"domain_id": "0192abc0-def1-7000-8000-000000000001",
"local_part": "alice",
"password": "a-strong-passphrase",
"display_name": "Alice Johnson",
"quota_mb": 2048
}'

Response (201 Created):

{
"data": {
"id": "0192abc0-def1-7000-8000-000000000010",
"domain_id": "0192abc0-def1-7000-8000-000000000001",
"local_part": "alice",
"display_name": "Alice Johnson",
"quota_mb": 2048,
"active": true,
"created_at": "2026-04-04T12:00:00Z",
"updated_at": "2026-04-04T12:00:00Z"
},
"meta": { "request_id": "...", "timestamp": "..." }
}

Validation rules:

  • The domain must exist and be active.
  • local_part must be unique within the domain.
  • If the domain has max_mailboxes set, creation fails if the limit is reached.

Errors:

CodeStatusDescription
MISSING_FIELDS400Required fields missing.
DOMAIN_NOT_FOUND404Domain ID does not exist.
DOMAIN_INACTIVE403Domain is deactivated.
CONFLICT409Mailbox already exists for this local_part and domain.
GET /api/v1/mailboxes/{id}

Returns full details for a single mailbox, including quota usage.

Terminal window
curl https://mail.example.com/api/v1/mailboxes/0192abc0-def1-7000-8000-000000000010 \
-H "Authorization: Bearer vectis_sk_abc123..."
PATCH /api/v1/mailboxes/{id}

Updates mailbox settings. Only include the fields you want to change.

Request body (all fields optional):

FieldTypeDescription
passwordstringNew password (re-hashed with Argon2id).
display_namestringUpdated display name.
quota_mbintegerNew quota in megabytes.
activebooleanEnable or disable the mailbox.

Example — update quota and display name:

Terminal window
curl -X PATCH https://mail.example.com/api/v1/mailboxes/0192abc0-... \
-H "Authorization: Bearer vectis_sk_abc123..." \
-H "Content-Type: application/json" \
-d '{"quota_mb": 4096, "display_name": "Alice M. Johnson"}'

Example — change password:

Terminal window
curl -X PATCH https://mail.example.com/api/v1/mailboxes/0192abc0-... \
-H "Authorization: Bearer vectis_sk_abc123..." \
-H "Content-Type: application/json" \
-d '{"password": "new-strong-passphrase"}'
DELETE /api/v1/mailboxes/{id}

Deletes a mailbox. Requires the X-Confirm-Delete: true header to prevent accidental deletion.

Optionally archive the mailbox’s mail data before deletion by including {"archive": true} in the request body. Archived mail is saved to /var/vectis/archives/<domain>/<local_part>-<timestamp>/.

Example:

Terminal window
curl -X DELETE https://mail.example.com/api/v1/mailboxes/0192abc0-... \
-H "Authorization: Bearer vectis_sk_abc123..." \
-H "X-Confirm-Delete: true" \
-H "Content-Type: application/json" \
-d '{"archive": true}'

Response:

{
"data": {
"message": "Mailbox deleted",
"archived": true,
"archive_path": "/var/vectis/archives/example.com/alice-20260404-120000/"
}
}

Without the confirmation header, the API returns:

{
"error": {
"code": "CONFIRMATION_REQUIRED",
"message": "Set X-Confirm-Delete: true header to confirm mailbox deletion"
}
}

Passwords are hashed server-side using Argon2id with the following parameters:

ParameterValue
Memory65,536 KB (64 MB)
Iterations3
Parallelism4
FormatPHC string ($argon2id$v=19$m=65536,t=3,p=4$salt$hash)

Passwords are never stored in plaintext or returned in API responses.