All guides

Masked Email from the command line and the API

Fastmail exposes Masked Email over JMAP, so an address is one HTTP request away. This covers maskedemail-cli, which wraps that API, and the methods it calls.

Install the CLI

maskedemail-cli is an open source Go client for Masked Email. Pick whichever installer you already have:

npm install -g maskedemail-cli
mise use -g go:github.com/dvcrn/maskedemail-cli@latest
go install github.com/dvcrn/maskedemail-cli@latest

Give it a token

The CLI authenticates with a Fastmail API token, the same kind the iOS app uses. Create one in Fastmail with Masked Email as its only scope; the token guide has the screens. Then put it where the CLI looks:

export MASKEDEMAIL_TOKEN=fmu1-...
maskedemail-cli session

session prints the account and capabilities the token reaches. If it fails here, nothing else will work, and the usual cause is a token created without the Masked Email scope. Scopes cannot be added to an existing token, so you have to make a new one.

The commands

maskedemail-cli create [-domain "<domain>"] [-desc "<description>"] [-prefix "<prefix>"] [-enabled=true|false]
maskedemail-cli list [-show-deleted] [-all-fields]
maskedemail-cli enable <maskedemail>
maskedemail-cli disable <maskedemail>
maskedemail-cli delete <maskedemail>
maskedemail-cli update <maskedemail> [-domain "<domain>"] [-desc "<description>"]

Creating an address for a signup, then reading back what you have:

$ maskedemail-cli create -domain "example.com" -desc "Example newsletter"
$ maskedemail-cli list
Masked Email        For Domain     Description          State
123@mydomain.com    example.com    Example newsletter   enabled

-prefix puts a word you choose at the front of the generated address, which makes a list of them easier to read later. Leave it off and Fastmail picks its own word pair.

Pipe create into your clipboard and the address is ready to paste into the signup form: maskedemail-cli create -domain "example.com" | pbcopy on macOS, or | wl-copy under Wayland.

What it calls underneath

Masked Email is a Fastmail extension to JMAP, advertised by the capability https://www.fastmail.com/dev/maskedemail. Every JMAP client starts at the session endpoint, which returns the API URL to post to and the account ids to use:

curl -H "Authorization: Bearer $MASKEDEMAIL_TOKEN" \
  https://api.fastmail.com/jmap/session

Take apiUrl from that response, and the account id from primaryAccounts under the Masked Email capability. Listing every address is then one method call:

{
  "using": [
    "urn:ietf:params:jmap:core",
    "https://www.fastmail.com/dev/maskedemail"
  ],
  "methodCalls": [
    ["MaskedEmail/get", { "accountId": "u123abc", "ids": null }, "0"]
  ]
}

There are two methods, both standard JMAP shapes: MaskedEmail/get reads addresses, and MaskedEmail/set creates, updates and destroys them. Everything the CLI does is one of those two.

The MaskedEmail object

  • id and email: the address and its id, both set by the server and immutable.
  • state: pending, enabled, disabled or deleted.
  • forDomain: the site the address was made for, as a URL or a bare domain.
  • description: your own note about what it is.
  • emailPrefix: set only at creation, up to 64 characters of a-z, 0-9 and underscore.
  • createdAt, createdBy and lastMessageAt: all set by the server.
  • url: a link back to a record in your own system.

Set -appname on the CLI, or send your own client name, and it lands in createdBy. Fastmail's web interface shows that value, so you can tell which tool made an address.

The four states

  • pending is where a new address starts, and the only state you cannot return to. It lets a client show you an address before you commit to it.
  • enabled delivers to your inbox.
  • disabled sends mail to Trash. The sender gets no error.
  • deleted bounces mail. The sender is told the address does not exist.

So disable is the quiet option and delete is the loud one. A deleted address can be restored from Fastmail's own settings, but mail that bounced while it was deleted is gone. Blocking an address covers that choice in more detail.

The CLI passes -enabled=true by default, so create hands back an address that is already live rather than one sitting in pending.

Other clients

The same library backs maskedemail-js for Node, and Masked Email Manager on iOS. 1Password and Bitwarden call the same API from their signup flows, which the password manager guide covers.

Fastmail documents the extension under Integrating with Fastmail.

Masked Email Manager is the same library with a list you can search and swipe, for when you are signing up on a phone rather than at a terminal.

Keep reading

Updated