Skip to content

Handles, scenarios, and secrets

The addressing model — what a handle is, why scenarios need no setup, and how sending and reading are deliberately different capabilities.

Every MailMatter address has the same anatomy:

bluepine.signup@mailmatter.dev
└──┬───┘ └──┬─┘
 handle   scenario

The handle names a namespace your tests own. The scenario names one inbox inside it. Incoming mail is lowercased and split on the first dot, so a handle can never contain a dot, while a scenario may (reset.attempt2 works; it lives under handle bluepine if addressed as bluepine.reset.attempt2@…).

Scenarios are implicit

There is no “create inbox” call. Addressing {handle}.checkout@mailmatter.dev is what brings the checkout scenario into existence — the first message to arrive creates it. Use a fresh scenario per test case (signup, signup.expired-token, run-42.otp) and your suite never needs setup or teardown to get an address.

Scenarios are namespaced per handle: your signup and another tenant’s signup are different inboxes that can never see each other’s mail.

Sending and reading are different capabilities

A namespace is a public handle plus a secret read capability:

  • Anyone who knows the handle can send to it. That’s the point — you paste the address into the system under test.
  • Reading, listing, or purging requires the secret — sent as Authorization: Bearer <secret> or carried by the browser cookie the mint set. Guessing a handle grants send only; the secret is stored server-side only as a SHA-256 hash.

Unknown handles return 404; a known handle with a missing or wrong credential returns 403.

Anonymous inboxes

POST /api/namespace with no body and no account mints an anonymous namespace: a generated qa-* handle plus its secret, also set as an httpOnly cookie so the dashboard at mailmatter.dev/app can read the same inbox. Anonymous namespaces are temporary — reading them keeps them alive — and the dashboard’s Recovery link restores access to the same inbox on another browser.

The anonymous secret authenticates the REST API directly — no account needed. It never works as an API key or for MCP; those require a (free) account.

Claiming and org inboxes

Signing in claims your anonymous namespace into your organization: the handle stays the same, so addresses already pasted into forms keep working, and from then on reads are authorized by your session or an org API key instead of the anonymous secret.

Signed-in orgs can hold multiple handles. Generated handles (qa-…) are available on every plan; choosing your own handle — bluepine instead of qa-d7aafa0b9c73, short enough to hardcode in a test suite — is a paid feature. Handle counts, retention windows, and message volumes vary by plan; see pricing for the current numbers.

Retention and quotas

  • Mail expires. Each message is stamped with your plan’s retention window when it arrives and is purged by an hourly job after that. Don’t store anything in MailMatter you need to keep.
  • Messages over your plan’s volume cap still arrive — they show up as a withheld envelope (sender, subject, timestamp, over_quota: true) rather than disappearing. Your test learns “arrived, content gated” instead of the lie “the email never came”. Content unlocks retroactively on upgrade. Over the API a withheld message is served with HTTP 402.
  • Rate limiting is a fair-use guardrail, identical on every tier — it is never sold as a differentiator.
  • Manage handles — creating, labelling, emptying, and releasing handles in the dashboard.
  • REST API — the exact endpoints, parameters, and status codes.