Quick Start
Send your first email with Mailyard in minutes
This guide walks you through sending your first email with Mailyard.
1. Start Mailyard
Follow the Installation guide to start Mailyard with Docker Compose.
2. Sign In
Sign in at http://localhost:3000/app with admin@example.com and the bootstrap password from the container log.
For the curl below, get a cookie jar. The login response carries no token — the session is an HttpOnly cookie and
nothing else:
curl -c cookies.txt -X POST http://localhost:3000/app/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"<from the log>"}'
3. Create a Project
Everything in Mailyard belongs to a project, and a new account is a member of none — nothing is created for you. The console shows a New project button on first sign-in, or:
curl -X POST http://localhost:3000/api/v1/projects \
-b cookies.txt -H "Content-Type: application/json" \
-d '{"name": "My App"}'
Keep the id from the response. It is a UUID, and every project-scoped call needs it in the X-Mailyard-Project-Id
header.
PROJECT=81af718e-f0ae-4780-a0d7-9f05b34dabcc
4. Create an API Key
curl -X POST http://localhost:3000/api/v1/api-keys \
-b cookies.txt \
-H "X-Mailyard-Project-Id: $PROJECT" \
-H "Content-Type: application/json" \
-d '{
"name": "my-app",
"permissions": ["emails:write", "emails:read"]
}'
The plaintext is the token field of the response, and it is returned once — only hex(sha256(token)) is stored,
so nobody can recover it afterwards, an operator with database access included. Keep it for the send below:
KEY=myk_...
A key with no permissions can do nothing
permissions is not optional in practice. Omit it and the key is minted holding nothing, and every call it makes
answers 403 — which is the safe reading of an unstated intent, not a bug. The catalogue is at
GET /api/v1/permissions, and ["*"] grants everything in the project.
5. Configure an SMTP Server
curl -X POST http://localhost:3000/api/v1/smtp-servers \
-b cookies.txt \
-H "X-Mailyard-Project-Id: $PROJECT" \
-H "Content-Type: application/json" \
-d '{
"name": "primary",
"host": "smtp.relay.example.net",
"port": 587,
"username": "acme-industrial",
"password": "'"$SMTP_PASSWORD"'",
"encryption": "starttls"
}'
6. Verify Your Sending Domain
Mailyard will not send from a domain this project has not verified, so this step comes before the first message rather than after it. Add the domain, publish the TXT record it gives you, and verify:
curl -X POST http://localhost:3000/api/v1/domains \
-b cookies.txt \
-H "X-Mailyard-Project-Id: $PROJECT" \
-H "Content-Type: application/json" \
-d '{"domain": "yourdomain.com"}'
# publish the returned mailyard-verification=... TXT record at the apex, then
curl -X POST http://localhost:3000/api/v1/domains/<id>/verify \
-b cookies.txt \
-H "X-Mailyard-Project-Id: $PROJECT"
See Domain Verification for the full flow. Verifying also mints the DKIM key, so mail starts being signed at the same time.
7. Send Your First Email
Now the API key, which needs no project header — the key carries its project already:
curl -X POST http://localhost:3000/api/v1/emails/send \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourdomain.com",
"to": ["you@yourdomain.com"],
"subject": "First message",
"text": "If you are reading this, the install works.",
"html": "<p>If you are reading this, the install works.</p>"
}'
Send it to yourself, at the domain you just verified. A first message to somebody else is how you find out that your SMTP route is wrong by way of their spam folder.
{
"email": { "id": "0198f6a1-3c7e-7b21-9f4d-2a5c8e0b1d33", "status": "queued" },
"suppressed_recipients": []
}
8. Check Delivery Status
queued means accepted, not delivered. Poll the id you got back:
curl http://localhost:3000/api/v1/emails/0198f6a1-3c7e-7b21-9f4d-2a5c8e0b1d33/status \
-H "Authorization: Bearer $KEY"
{
"id": "0198f6a1-3c7e-7b21-9f4d-2a5c8e0b1d33",
"status": "sent",
"attempts": 1,
"error_message": "",
"sent_at": "2026-01-01T00:00:01Z"
}
A failed status puts the reason in error_message, and it is almost always one of three things at this stage: the
SMTP credentials, the sending domain not being verified, or the server refusing the from address. Fix it and
POST /api/v1/emails/{id}/retry.
See Email Status
for what each state means, and why sent is not the same as
delivered.
Generating a client
There is a Go client at github.com/yousysadmin/mailyard/sdk/go. For other languages, run mailyard export-api-spec
and generate a typed client from the document it writes.
Next Steps
- Template Email — Send emails using templates
- Batch Email — Send to multiple recipients