Skip to content

fable

Create a Fablemail account, sign in, search your mail, and send messages without leaving your terminal.

curl -fsSL http://app.fable.email/cli/install | sh -s -- signup

This installs the fable command and walks you through sign-up. Once it finishes, sign in on the web. In a new terminal, fable signup does it again.

fable is a single file with no dependencies. It needs Node.js 18 or newer and curl. The installer puts it at ~/.local/bin/fable and, if that directory is not already on your PATH, adds it to your shell startup file. Open a new terminal afterwards.

curl -fsSL http://app.fable.email/cli/install | sh

Running it again updates fable. You can also download the script from your browser.

Terminal window
fable signup

The CLI asks for:

  • Your first and last name.
  • The address you want (the part before @fable.email). It checks availability right away and asks again if the address is taken.
  • A password, entered twice. Nothing is shown while you type.

It then runs a quick proof-of-work check (the same one the sign-up page runs in your browser), creates the account and prints your new address.

Create a Fablemail account on http://app.fable.email

First name: Ada
Last name: Lovelace
Email address (the part before the @): ada
  [email protected] is available.
Password (at least 10 characters):
Confirm password:

Running a quick verification... done.
Creating your account...

Your account is ready: [email protected]
Sign in at http://app.fable.email/login

Pass every field as a flag to skip the prompts. The password is read from standard input so it doesn't end up in your shell history or the process list:

printf '%s' "$PASSWORD" | fable signup \
  --first-name Ada --last-name Lovelace --address ada --password-stdin

Without a terminal to prompt on, the CLI fails if a field is missing. It exits with status 0 on success and 1 on any error, with the reason on standard error.

Terminal window
fable login

This opens your browser on this server. Sign in if you need to, then allow the terminal to use your mailbox for 48 hours. The browser sends the session token only to 127.0.0.1 on your computer. The CLI stores it in ~/.config/fable/credentials.json (or $XDG_CONFIG_HOME/fable/credentials.json) and no one else can read the file.

fable whoami prints the address and when the session expires. fable logout deletes it here and on the server. Changing your password ends it too.

list shows your inbox, newest first. search matches words (the same keyword index as the website). It does not search by meaning. Spam is left out of that index, so a word search only includes spam when you pass --folder spam.

Terminal window
fable list --unread --limit 10
fable search lease renewal --from ada
fable read <message-id>

--folder is inbox, sent, spam, or all. --from, --to, and --subject keep messages whose sender, recipient, or subject contains that text. --after and --before take a date or time. Add --json for the API response.

Terminal window
fable send --to [email protected] --subject "Hello" --body "Hi Ada"
Terminal window
printf '%s\n' "Hi Ada" | fable send --to [email protected] --subject "Hello"

Repeat --to, --cc, or --bcc, or separate addresses with commas. The body is --body, --body-file, or standard input when it is not a terminal. Sending follows the same undo window as the website.

signup
Create an account. Anything not passed as a flag is asked for.
login
Sign in through the browser. The session lasts 48 hours.
logout
Forget the saved session and revoke it on the server.
whoami
Print the signed-in address and when the session expires.
list
List messages. Defaults to the inbox.
search
Keyword search. The words follow the command.
read
Print one message, including its text.
send
Send an email.
help
Print usage. Same as --help or -h.
--first-name <name>
Your first name.
--last-name <name>
Your last name.
--address <name>
The part before @fable.email.
--password-stdin
Read the password from standard input instead of prompting.
--server <url>
The Fablemail server to use. Defaults to the server that installed fable (http://app.fable.email). FABLE_SERVER does the same.
--folder <name>
inbox, sent, spam, or all.
--from / --to / --subject
On list and search, keep messages that contain this text.
--unread / --after / --before
More filters for list and search. Dates can be a calendar day or a full timestamp.
--limit / --offset
Page size (1–100, default 25) and how many messages to skip (up to 1000).
--json
Print the JSON the server returned.
--to / --cc / --bcc
On send, the recipients. Repeat the flag or separate addresses with commas.
--body / --body-file
The message. With neither, a piped standard input is the body.
--reply-to <message-id>
Send as a reply to that message.
  • Addresses are 3-30 characters: letters, numbers, dots, underscores and hyphens. Some are reserved.
  • Passwords need at least 10 characters, can't be a common password and can't contain your name or address.
  • Each network can create up to 5 accounts per hour. Attempts rejected for invalid fields don't count.
  • Add a recovery email in Settings after signing in. Without one, a forgotten password can't be reset.

Sign-up is public. Everything else under /api/v1 needs the session token from fable login:

Authorization: Bearer <token>

The token lasts 48 hours. These routes ignore the browser cookie.

Returns { address, firstName, lastName, expiresAt }.

Revokes the bearer token.

Lists messages, newest first. Query parameters: folder (inbox, sent, spam), from, to, subject, unread=1, after, before, limit (1–100, default 25), offset (0–1000). Returns { messages }. Each message has id, threadId, folder, subject, fromName, fromAddr, toAddrs, ccAddrs, snippet, date, isRead, and hasAttachments.

One message, plus text and attachments (id, filename, contentType, size). Unknown ids return 404.

Keyword search only. q is required and uses the same word matching as the website (stems and quoted phrases). It does not search by meaning. The same filters as the list apply. Spam is included only with folder=spam, and then the match is a plain text search. Returns { query, messages }.

Same JSON body as the website's send endpoint:

curl -X POST http://app.fable.email/api/v1/send \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"to":["[email protected]"],"subject":"Hello","text":"Hi"}'

Returns 201 with { ok, id, threadId, queueId, undoMsLeft }. 400 when the body is invalid, 401 when the token is missing or expired, 429 when you are sending too fast or over the daily recipient limit.

Sign-up uses three public endpoints.

Returns a proof-of-work challenge: { algorithm, challenge, salt, maxnumber, signature }. Find the integer number from 0 to maxnumber where sha256(salt + number) as hex equals challenge. The token is the base64-encoded JSON { algorithm, challenge, number, salt, signature }. Each token works once and expires after 10 minutes.

Returns { available, error, address }. This is only a hint; sign-up itself is the final check.

curl -X POST http://app.fable.email/api/v1/signup \
  -H 'Content-Type: application/json' \
  -d '{"firstName":"Ada","lastName":"Lovelace","local":"ada","password":"…","captcha":"<token>"}'

Returns 201 with { ok: true, address, loginUrl }. Errors return { error } plus:

  • 400 with fieldErrors (keyed by firstName, lastName, local, password) when a field is invalid, or captchaFailed: true when the token is wrong, used or expired.
  • 409 when the address is taken.
  • 429 when your network hit the sign-up limit.