Getting started
The objects every Emayler account is made of, and the storage and threading rules behind them. Read this once and the rest of the docs click into place.
Five objects make up an account. Everything else in the product is a view over these.
| Object | ID prefix | What it is |
|---|---|---|
| Organisation | org_ | The owning account. Every resource belongs to one, and the database keeps data strictly separated between accounts. |
| Domain | dom_ | A customer domain with managed email signing. Adding one is a single step. |
| Mailbox | mbx_ | A real inbox with storage. The billing unit. Created with INBOX, Sent, Drafts, Trash, Archive. |
| Identity | idt_ | An address you can send from, attached to a user: address, display name, signature. Verified before use. |
| User | usr_ | A person with a role (owner, admin, member) and per-mailbox access grants (read or full). |
Aliases hang off mailboxes (POST /v1/mailboxes/{id}/aliases) and are free and unlimited on every plan. Threads (thr_), messages (msg_), attachments (att_), API keys (key_), and imports (imp_) round out the ID scheme; the prefix on every ID tells you what you are holding.
When a message arrives, Emayler does the minimum: it writes the original email in full to object storage, enqueues a parse job, and returns. The database holds only derived data: metadata, threads, flags, and the search index. Every message is stored once in full, and everything else about it can be rebuilt from that copy. If the index is ever lost or corrupted, a reindex job rebuilds it from storage.
Two consequences fall out of this. First, message bodies never live in the database, which keeps it small enough to self-host and keeps stored previews deliberately short. Second, if storage or the queue is unavailable when mail arrives, the mail host asks the sender to retry rather than accepting the message and dropping it. Emayler does not accept mail it cannot store.
Threading headers (In-Reply-To, References, Re: handling) follow the same rules in the inbox and the API, so a reply sent from the inbox threads identically to one sent from a script. When you pass reply_to_message_id to the send endpoint, the API sets the headers for you.
Incoming messages join a thread in this order:
In-Reply-To match against a known message.References match, checked right to left.Every new organisation starts with sending disabled. Inbound works immediately, so you can verify domains, create mailboxes, and receive mail before anything else happens. Sending turns on when an operator grants it after review; there is no API call or plan tier that enables it yourself. Asking the API to turn sending on returns an error.
The gate exists because outbound is a reputation problem, not a technical one. Sending goes through our sending provider, and a spam rate over 0.30% can take down your deliverability across every major mail provider in a recovery window that no amount of engineering routes around.
| Tier | Daily send allowance |
|---|---|
| New | 0 sends (pre-review) |
| Standard | 500 sends/day |
| Trusted | Plan limits apply |
| Restricted | 0 sends |
Every send is signed with your domain's own DKIM key. Setting it up is a one-time step: you publish one CNAME record pointing at Emayler, and the key rotates afterwards without you touching DNS again. The full record set (MX, SPF, DKIM, DMARC, ownership TXT) is covered in Domains & DNS.
Suspect mail is quarantined, not thrown away. In the API the Spam view covers quarantined messages, and each message carries a spam score plus the sender's authentication results. A false positive on a customer's biggest client is worse than any amount of spam in a folder, so nothing is silently discarded.
Originals are stored verbatim. HTML is cleaned at display time, never when it arrives, and remote images are blocked by default. Attachments download from a separate origin, never from the app domain, so a hostile attachment cannot act in the context of your inbox session.
Why no per-domain configuration
Adding a domain is a single step, full stop. Setup never requires touching a config file, a deploy, or a vendor API for anything other than DNS itself. That is what makes unlimited customer domains possible from the first message.