A tiny PHP mailer that exposes a single HTTP endpoint for sending emails via SMTP. Authenticate with an API key, POST some JSON, get an email delivered.
- PHP 8.1+.
- Composer.
- An SMTP server.
git clone https://github.com/openauthenticator-app/simple-mailer.git
composer install
cp config.example.php config.php
# Edit config.php with your valuesAll configuration lives in config.php, which must return a PHP array. See config.example.php for a complete
reference.
| Key | Type | Description |
|---|---|---|
api_key |
string | Secret key required in every request (X-Api-Key header) |
from_email |
string | Sender address |
from_name |
string | Sender display name |
mode |
string | free (default) or template — see below |
smtp.host |
string | SMTP hostname |
smtp.port |
int | SMTP port (e.g. 465, 587) |
smtp.username |
string | SMTP username |
smtp.password |
string | SMTP password |
smtp.encryption |
string | ssl or tls |
smtp.timeout |
int | Connection timeout in seconds (default 10) |
The caller supplies the email body on every request. No extra config needed.
The HTML and plain-text bodies are defined once in config. The caller only sends the parameters needed to fill in the
{{ placeholders }}. All parameters are HTML-escaped before being inserted into the HTML template, preventing XSS.
Bodies are defined per locale under template.locales.<locale>. The caller sends a locale field in the POST body;
if omitted, template.default_locale is used as a fallback.
| Key | Type | Description |
|---|---|---|
template.default_locale |
string | Optional. Fallback locale when none is supplied in the request. |
template.locales.<locale>.subject |
string | Optional per locale. Subject rendered with params; omits subject from request. |
template.locales.<locale>.html |
string | HTML body for this locale, with {{ variable }} placeholders |
template.locales.<locale>.txt |
string | Plain-text body for this locale, with {{ variable }} placeholders |
template.params |
array | Parameter validation rules, shared across all locales (see below) |
Parameter rules (template.params) :
Each entry is keyed by the parameter name. Available rule options :
| Option | Type | Default | Description |
|---|---|---|---|
required |
bool | true |
Whether the parameter must be present in the request |
type |
string | string |
string, email, url, or integer |
max_length |
int | — | Maximum character length |
pattern |
string | — | Regex the value must match (e.g. '/^\d{6}$/') |
Filters :
Placeholders can include a pipe filter: {{ variable | filter }}.
| Filter | Alias | Description |
|---|---|---|
urlEscape |
urlEncode |
URL-encodes the value (urlencode) |
rawUrlEscape |
rawUrlEncode |
RFC 3986 URL-encodes the value (rawurlencode) |
Filters are applied before HTML escaping, so the result is always safe for use in HTML attributes and bodies.
Example :
'mode' => 'template',
'template' => [
'default_locale' => 'en',
'locales' => [
'en' => [
'subject' => 'Your verification code',
'html' => '<h1>Hello {{ name }}!</h1><p>Your code is <strong>{{ code }}</strong>.</p><a href="https://example.com/verify?token={{ token | rawUrlEscape }}">Verify</a>',
'txt' => "Hello {{ name }}!\n\nYour code is: {{ code }}",
],
'fr' => [
'subject' => 'Votre code de vérification',
'html' => '<h1>Bonjour {{ name }} !</h1><p>Votre code est <strong>{{ code }}</strong>.</p>',
'txt' => "Bonjour {{ name }} !\n\nVotre code est : {{ code }}",
],
],
'params' => [
'name' => ['type' => 'string', 'max_length' => 100],
'code' => ['type' => 'string', 'pattern' => '/^\d{6}$/'],
'token' => ['type' => 'string'],
],
],Headers
| Header | Value |
|---|---|
X-Api-Key |
Your configured API key |
Content-Type |
application/json |
{
"to": "recipient@example.com",
"subject": "Hello",
"html": "<b>Hello!</b>",
"text": "Hello!"
}| Field | Required | Description |
|---|---|---|
to |
yes | Recipient email address |
subject |
yes | Email subject |
html |
no* | HTML body |
text |
no* | Plain-text body |
*At least one of html or text must be provided.
{
"to": "recipient@example.com",
"locale": "en",
"params": {
"name": "Alice",
"code": "123456"
}
}| Field | Required | Description |
|---|---|---|
to |
yes | Recipient email address |
subject |
no* | Email subject |
locale |
no** | Locale key to select the template (locale variant only) |
params |
yes | Object containing the template parameter values |
* Required only if template.locales.<locale>.subject is not defined in the config.
** Required only if template.default_locale is not defined in the config.
Success — HTTP 200
{
"ok": true,
"message": "email_sent"
}Error responses
| Status | error |
Cause |
|---|---|---|
| 400 | invalid_json |
Request body is not valid JSON |
| 400 | invalid_params |
A template parameter failed validation |
| 401 | unauthorized |
Missing or incorrect X-Api-Key |
| 405 | method_not_allowed |
Request method is not POST |
| 500 | config_error |
config.php is missing or invalid |
| 500 | mail_failed |
SMTP delivery failed |
Error responses include a message field with details where applicable.
SimpleMailer is licensed under the GNU General Public License v3.0.
If you like this project, there are a lot of ways for you to contribute to it ! Please read the contribution guide before getting started.
You can report bugs or suggest new features in the issue tracker.
You can donate for this project using either PayPal, Ko-Fi or Github sponsors. If you don't want to donate, any kind message is also appreciated !