API Documentation
Public REST API only — create inboxes, read mail, manage messages. Prefer v1 + API token for new integrations. Domain list and create endpoints only use public domains.
Bearer API token (am_at_…). JSON success/data, rate limited.
Login with address + password → JWT. Hydra-style responses (Mailcow/MailTM-like).
API v1 /api/v1/*
Token-based API. Create an inbox (or generate a token from the web UI) and call endpoints with the API token.
Authentication
Send the API token as a Bearer token. Tokens look like am_at_…. You get one automatically from POST /api/v1/inbox, or create more from the inbox UI (API tab).
Authorization: Bearer am_at_xxxxxxxxxxxxxxxx
Rate limits
25 requests/second per inbox (API token). Over limit → 429 with Retry-After. Successful responses include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Response format
// Success
{ "success": true, "data": { ... } }
// List + pagination
{
"success": true,
"data": [ ... ],
"pagination": { "page": 1, "limit": 50, "total": 42, "totalPages": 1 }
}
// Error
{ "success": false, "error": "Error message" }Endpoints
/api/v1/inboxCreate a permanent inbox on a public domain (optional username/password). Returns email, password, web token, and apiToken.
/api/v1/inboxGet current inbox info (email, emailCount, storageUsed, createdAt)
/api/v1/inboxDelete inbox and all emails
/api/v1/emailsList non-expired emails. Query: ?page=1&limit=50 (limit max 100)
/api/v1/emails/:idGet full email (bodyHtml, bodyText, attachments). Marks as read.
/api/v1/emails/:idDelete one email
/api/v1/emails/:id/attachments/:attIdDownload attachment (raw file bytes)
Example: full flow (v1)
# 1. Create inbox (public) — optional username/password
curl -X POST https://amail.my/api/v1/inbox \
-H "Content-Type: application/json" \
-d '{"username":"myapp","password":"secret"}'
# Response 201:
# {
# "success": true,
# "data": {
# "email": "[email protected]",
# "token": "<web-inbox-token>",
# "password": "secret",
# "apiToken": "am_at_..."
# }
# }
# 2. List emails
curl "https://amail.my/api/v1/emails?page=1&limit=50" \
-H "Authorization: Bearer am_at_..."
# 3. Get one email
curl https://amail.my/api/v1/emails/<EMAIL_ID> \
-H "Authorization: Bearer am_at_..."
# 4. Download attachment
curl -OJ https://amail.my/api/v1/emails/<EMAIL_ID>/attachments/<ATT_ID> \
-H "Authorization: Bearer am_at_..."
# 5. Delete email
curl -X DELETE https://amail.my/api/v1/emails/<EMAIL_ID> \
-H "Authorization: Bearer am_at_..."
# 6. Inbox info / delete inbox
curl https://amail.my/api/v1/inbox \
-H "Authorization: Bearer am_at_..."
curl -X DELETE https://amail.my/api/v1/inbox \
-H "Authorization: Bearer am_at_..."Create inbox body
POST /api/v1/inbox
{
"username": "optional", // a-zA-Z0-9, max 64; default: random
"password": "optional" // default: auto-generated
}
// Uses the default public domain (e.g. amail.my).
// Private domains are not available via this API.JWT account API legacy-style
Address + password → short-lived JWT. Responses use a Hydra-like shape (compatible with some MailTM-style clients). Prefer API v1 for new apps.
Authentication
Create an account, then exchange credentials for a JWT (valid 24 hours). Use it on message endpoints:
# Login
curl -X POST https://amail.my/api/token \
-H "Content-Type: application/json" \
-d '{"address":"[email protected]","password":"secret"}'
# → { "token": "eyJ...", "id": "...", "address": "[email protected]" }
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...Response format (Hydra-style)
This API does not use success/data.
// Domain / message lists
{
"hydra:member": [ { "@id": "...", ... } ],
"hydra:totalItems": 42,
"page": 1,
"limit": 50,
"totalPages": 1
}
// Create account 201
{ "@id": "...", "address": "[email protected]", "createdAt": "..." }
// Token
{ "token": "eyJ...", "id": "...", "address": "[email protected]" }
// Single message
{
"@id": "...",
"from": { "address": "...", "name": "" },
"to": [{ "address": "..." }],
"subject": "...",
"html": ["..."],
"text": "...",
"hasAttachments": false,
"attachments": [],
"size": 1234,
"isRead": true,
"createdAt": "..."
}
// Errors
{ "detail": "Error message" }Rate limits
No per-second rate limit on JWT message routes (unlike v1). Still use reasonable request rates.
Endpoints
/api/domainsList public domains only (active + shared). Hydra collection.
/api/accountsCreate inbox on a public domain (address + password, permanent)
/api/tokenExchange address + password for JWT (24h)
/api/messagesList non-expired messages. Query: ?page=1 (limit fixed at 50)
/api/messages/:idGet single message with body + attachments
/api/messages/:idDelete a message
Example: full flow (JWT)
# 1. Public domains only
curl https://amail.my/api/domains
# 2. Create account (address must use a public domain from step 1)
curl -X POST https://amail.my/api/accounts \
-H "Content-Type: application/json" \
-d '{"address":"[email protected]","password":"secret"}'
# 3. JWT
curl -X POST https://amail.my/api/token \
-H "Content-Type: application/json" \
-d '{"address":"[email protected]","password":"secret"}'
# 4. List messages (?page only; limit is always 50)
curl "https://amail.my/api/messages?page=1" \
-H "Authorization: Bearer <JWT>"
# 5. Get / delete message
curl https://amail.my/api/messages/<ID> \
-H "Authorization: Bearer <JWT>"
curl -X DELETE https://amail.my/api/messages/<ID> \
-H "Authorization: Bearer <JWT>"HTTP status codes
200OK ·201Created401Unauthorized ·404Not found409Conflict (v1 username taken) ·422Validation (JWT accounts)429Rate limited (v1 only)
Base URL: https://amail.my
GET /api/domains returns only domains marked public. Messages expire per domain TTL (default 2 hours) and are omitted from list/get after expiry.