Appearance
smtp
4 endpoints.
GET /api/v1/settings/smtp
Get Smtp
The site's SMTP settings (DB-over-env), password withheld.
200 — Successful Response
json
{}PUT /api/v1/settings/smtp
Update Smtp
Upsert the SMTP settings, then return the refreshed (masked) view with a deliverability report attached.
THE CHECK RUNS ON SAVE BECAUSE THAT IS THE ONLY MOMENT ANYONE IS LOOKING. A mailbox that connects and authenticates can still have every message filed as spam — SPF, DKIM and DMARC live in DNS, nowhere near this form, and nothing here ever looked at them. Found on 2026-09-08: nine sites were sending as lubb.co.za under p=quarantine with a DKIM signature whose selector had never been published, so every signature failed and only SPF stood between the mail and the spam folder.
It NEVER blocks the save. These settings are how a site sends anything at all, and refusing a mailbox over a missing DNS record would block the fix as surely as the fault — DNS is edited elsewhere, often minutes later.
Request body
json
{
"properties": {
"host": {
"anyOf": [
{
"type": "string",
"maxLength": 255
},
{
"type": "null"
}
],
"title": "Host"
},
"port": {
"anyOf": [
{
"type": "integer",
"maximum": 65535,
"minimum": 1
},
{
"type": "null"
}
],
"title": "Port"
},
"user": {
"anyOf": [
{
"type": "string",
"maxLength": 255
},
{
"type": "null"
}
],
"title": "User"
},
"password": {
"anyOf": [
{
"type": "string",
"maxLength": 512
},
{
"type": "null"
}
],
"title": "Password"
},
"security": {
"anyOf": [
{
"type": "string",
"pattern": "^(starttls|ssl|none)$"
},
{
"type": "null"
}
],
"title": "Security"
},
"from": {
"anyOf": [
{
"type": "string",
"maxLength": 255
},
{
"type": "null"
}
],
"title": "From"
},
"from_name": {
"anyOf": [
{
"type": "string",
"maxLength": 255
},
{
"type": "null"
}
],
"title": "From Name"
},
"timeout": {
"anyOf": [
{
"type": "number",
"maximum": 300,
"exclusiveMinimum": 0
},
{
"type": "null"
}
],
"title": "Timeout"
}
},
"type": "object",
"title": "SmtpIn",
"description": "Partial update for the site's SMTP settings. A field omitted (``None``) is\nleft untouched; an empty string clears that field's override (falls back to\n``.env``)."
}200 — Successful Response
json
{}422 — Validation Error
json
{
"properties": {
"detail": {
"items": {
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
}
]
},
"type": "array",
"title": "Location"
},
"msg": {
"type": "string",
"title": "Message"
},
"type": {
"type": "string",
"title": "Error Type"
}
},
"type": "object",
"required": [
"loc",
"msg",
"type"
],
"title": "ValidationError"
},
"type": "array",
"title": "Detail"
}
},
"type": "object",
"title": "HTTPValidationError"
}GET /api/v1/settings/smtp/deliverability
Smtp Deliverability
Re-run the SPF/DKIM/DMARC check against the currently-saved settings, without saving anything — for the "check again" the operator wants after editing DNS, and for the estate sweep script.
200 — Successful Response
json
{}POST /api/v1/settings/smtp/test
Test Smtp
Live connect + authenticate using the currently-saved credentials (DB-over-env). Sends nothing. Returns {ok, detail}.
200 — Successful Response
json
{}