API Reference
API Reference
GitHub ↗
Admin Panel

Platform Settings

Configure platform-wide settings

Administrators configure platform-wide settings from the dashboard under Admin -> Platform Settings, or through GET/PUT /api/v1/admin/settings.

These are the knobs that can change at runtime. Everything in mailyard.yaml (listeners, database, TLS, worker sizing) needs a restart and is not settable here.

Get Settings

GET /api/v1/admin/settings

Returns every key in the registry with its definition, its effective value, and whether that value is an administrator override or the built-in default:

{
    "settings": [
        {
            "key": "retention_days",
            "type": "int",
            "unit": "days",
            "default": "0",
            "value": "30",
            "overridden": true,
            "description": "Days to keep email log rows. 0 keeps them forever.",
            "updated_at": "2026-08-06T02:11:00Z",
            "updated_by": "admin@example.com"
        }
    ]
}

Update Settings

PUT /api/v1/admin/settings
{
    "settings": [
        {
            "key": "retention_days",
            "value": "60"
        },
        {
            "key": "maintenance_mode",
            "value": "true"
        }
    ]
}

Only the listed keys change. The whole batch is validated before anything is written, so a bad value in one entry rejects the request rather than half-applying it. Writing a value equal to the default removes the override instead of storing a redundant copy.

An unknown key is rejected. Settings exist only when something reads them, so the registry is the full list of what can be set.

Every int setting has a ceiling, reported as max in the listing: ten years (3650) for a day count, 100 for a percentage, and 1000000 for a message count. A larger value is refused. acme_hosts takes fully qualified host names only - no wildcard, no IP address - acme_email a bare address, and acme_directory_url an https URL.

Both routes require the platform admin role.

Available Settings

KeyTypeDefaultDescription
retention_daysint30Days to keep email log rows. 0 keeps them forever. The emails table is partitioned by day, so whole partitions past this window are dropped rather than deleted row by row.
email_body_retention_daysint0Days to keep rendered HTML and text on an email row. 0 follows retention_days.
email_attachment_retention_daysint0Days to keep attachment bytes, including the blobs in object storage. 0 follows retention_days. Received mail too: when shorter than inbound_retention_days its content is stripped and the envelope kept.
inbound_retention_daysint0Days to keep received mail. 0 follows retention_days.
sandbox_retention_daysint7Days to keep a captured sandbox message. 0 keeps it until the per-project cap pushes it out. A sender may ask for a shorter window per message, never a longer one.
sandbox_max_messagesint500How many sandbox messages one project keeps. The oldest are dropped past this. 0 is unlimited, which on a project wired into CI means the table grows until the disk says otherwise.
webhook_delivery_retention_daysint30Days to keep webhook delivery history. 0 keeps it forever.
audit_log_retention_daysint90Days to keep audit log entries. 0 keeps them forever.
tracking_event_retention_daysint0Days to keep open and click events. 0 keeps them forever. Per-message and per-link counters are aggregated and always kept.
maintenance_modeboolfalseRefuse write requests from everyone but platform admins.
tls_certificate_serverstring-Name of the managed certificate the HTTP listener serves. Empty falls through to ACME, then to the self-signed pair.
tls_certificate_submissionstring-Same, for the SMTP submission listener.
tls_certificate_inboundstring-Same, for the inbound MX listener.
acme_enabledboolfalseOrder certificates from Let’s Encrypt .
acme_hostslist-Hostnames to issue for. One per line in the console, a JSON array of strings over the API. A name that is not listed falls through to the self-signed pair rather than failing the handshake.
acme_emailstring-ACME account contact, where the CA sends expiry warnings. Used when the account is first registered.
acme_directory_urlstring-A different ACME directory. Empty is Let’s Encrypt production - point it at the staging one while working out why issuance fails.
platform_mail_fromstring-Address the platform’s own mail comes from - invitations, password resets, signup confirmations. Must be on a domain the platform mail project has verified. Empty turns platform mail off.
platform_mail_from_namestring-Display name beside that address.
platform_mail_projectstring-The project platform mail is sent as: its servers, relay nodes and DKIM key carry it as system mail, and only a platform administrator may change its servers, server groups and relay nodes. Picked by name in the console. Platform mail is off until this and the address are set.
notification_retention_daysint30Days to keep notifications that have been read. Unread ones are always kept.
bounce_alert_percentint10Bounce rate, among the messages accepted in the last hour that have finished, that raises a project alert. 0 turns the alert off.
bounce_alert_min_volumeint20How many of the messages accepted in the last hour must have finished before the bounce rate is judged.
relay_nodes_auto_approveboolfalseLet a relay node start delivering as soon as it enrols, without an admin approving it.
user_project_creationboolfalseLet any signed-in account create a project. Off, only platform administrators can, and everybody else joins by invitation . Administrators are never subject to it.

Leave auto-approve off unless nodes are created automatically

A relay node in the sending pool receives the content of real messages to deliver, and every node enrols with the same relay_nodes.auto_register_token. With approval in the way, a leaked token gets somebody a pending row an admin will not recognise. Without it, it gets them a copy of everybody’s mail. Turn it on only where nodes come and go on their own, such as an autoscaling group.

Nothing expires by default

Every retention window except the webhook delivery log starts at 0, which means keep forever. An install that never visits this page never loses data - and never reclaims space either. Set the windows deliberately.

Retention behavior

  • Content windows are clamped to retention_days. Keeping a body longer than the row that owns it is meaningless, so a body window longer than the metadata window is silently reduced to it.
  • Attachment blobs are deleted from object storage before the database rows that hold their keys. A blob that will not delete is logged and skipped rather than blocking the sweep - the result is an orphaned object, not a stuck job.
  • A template attachment deleted from its template keeps its bytes while any message references it. The sweep removes it, blob first, once it was deleted longer ago than the attachment window (or an hour ago when that window is 0) and no message references it any more - a message scheduled past the window still holds it.
  • A visual builder image that no template uses any more is removed once retention_days has passed since the last template stopped using it. The row goes first and the blob after it, so an image put back into a template meanwhile is never left pointing at missing bytes. With retention_days at 0 such images are kept.
  • Emails still in flight (queued, scheduled, processing) are never purged, however old. Deleting one would strand work the delivery queue is about to claim.
  • A failed email is not in flight, so its content is cleared like any other. Once it is older than the body or attachment window it can no longer be retried - the retry answers 409 rather than send an empty or incomplete message.

The sweep runs as the retention-cleanup scheduled job .

Maintenance Mode

With maintenance_mode on, every mutating request (POST, PUT, PATCH, DELETE) on both the console API and the machine API returns:

503 Service Unavailable
{ "error": "the platform is in maintenance mode, writes are temporarily disabled" }

Reads stay open so the console remains usable and an incident stays diagnosable, and platform admins are exempt entirely - somebody has to be able to switch it back off.

Signing in stays open for the same reason: password, passkey and SSO sign-in all work, as do sign-out, requesting a password reset and resending a verification mail. Registering an account, confirming a password reset and confirming an email address write account rows and answer the 503 above.

The SMTP submission listener defers new mail with a transient 451 4.3.2, so a client keeps the message and retries once the mode is off.

The delivery worker and campaign runner keep draining whatever is already queued. Maintenance mode stops new work arriving, it does not pause the pipeline.