---
title: "Disposable inboxes"
description: "A working address for somebody who has not got one: no account, no key, and gone the same day."
url: "https://openemail.uk/docs/api/temp-mail"
area: "API"
category: "Disposable inboxes"
---

# Disposable inboxes

A working address for somebody who has not got one: no account, no key, and gone the same day.

## What a disposable inbox is

A caller asks for an address on a domain this install owns, watches it for a few minutes, reads whatever arrives, and abandons it. It exists for the confirmation code, the "what does this form actually send" question, and the signup you do not want attached to the address you will still be using in five years.

- It receives and nothing else. There is no send: an inbox has no identity to send as, and none of these nine calls will put a message on the wire.
- The lease is 60 minutes by default and can be pushed to 24 hours, an hour at a time.
- It holds 50 messages, counted as they arrive. Mail reaching a full inbox is dropped rather than queued, and deleting one does not buy room for another.
- At the end of the lease the mail is DELETED, not hidden and not archived. The row outlives it by a week so the address cannot be re-issued while a slow sender is still retrying to it.
- None of it touches a mailbox. A disposable message lives in its own table, and no query on this path can reach a real one.

> These are the same calls the free tool on this site makes, so anything the page can do your code can do. The API is here for the case the page is not: a test suite that wants a fresh address per run.

## The address is not the credential

A disposable address is typed into a signup form the moment it is issued. From there it travels in a `To:` header, through the sender’s logs, and into whatever CRM sits at the other end. If knowing the address were enough to read the mail, the tool would leak every inbox it issued, by design, and to precisely the party the caller was holding at arm’s length.

So creating an inbox returns a second value: a `token`, 32 random bytes as `oe_inbox_` plus 43 base64url characters. It appears on that one response and on no other. The row keeps only a keyed hash of it, so nothing recovers it, not a support request and not a database dump. Lose the token and you have lost the inbox, which is the correct outcome for a credential that reads somebody’s mail.

**The whole flow**

```
# 1. Mint one. This is the only response that carries a token.
curl -s -X POST "$OE/temp-mail/inboxes" -H "Content-Type: application/json" -d '{}'

# 2. Keep it, and read with it.
export INBOX="Authorization: Bearer oe_inbox_kQ8v…"
curl -s "$OE/temp-mail/inboxes/tinb_9c2f…/messages" -H "$INBOX"
```

> Send an `oe_live_` or `oe_test_` API key to one of these routes and it is refused as `invalid_credential_type` rather than as a flat 401. Two kinds of credential share one host and one header here, and "unauthorised" would leave you to guess which of yours was wrong.

## The lease, and extending it

An hour, rather than the ten minutes the genre is named after. Ten is enough for a confirmation code and not enough for the other half of what these are used for: a trial that emails you again the next morning, a form filled in twice because the first attempt timed out. `ttlMinutes` on create asks for something else, from 1 to 1440; a number outside that is refused with a 422 rather than quietly adjusted, because an expiry you did not ask for is one you have already planned around.

`POST /temp-mail/inboxes/{id}/extend` adds an hour to the expiry, not to now, so extending early does not waste the time you have left. It works 23 times, and the day from the moment the inbox was created is the harder of the two caps: a lease that already runs to it has nothing left to buy, however few extensions have been spent. `extensionsLeft` on every inbox response counts both, so a client can grey the button out; at zero the call answers 409 `extension_limit`.

> An expired inbox stops authenticating the instant it expires: its token answers 404 without waiting for the sweep. The sweep is what deletes the mail, and it runs on the hourly cron; `DELETE /temp-mail/inboxes/{id}` is the same deletion on demand.

## The ceilings

All of these are counted rows rather than a rate limiter. There is no limiter in this codebase to reach for, and saying so is more use than implying a defence that is not there. They are placed where the damage would be: minting, and storage.

| Ceiling | Value | What happens at it |
| --- | --- | --- |
| Lease | 60 minutes, extendable to 24 hours | 409 `conflict_error` / `extension_limit` |
| Messages per inbox | 50 | Further mail is dropped at the door. No bounce is written, nothing is queued, and deleting a message does not give the slot back. |
| Inboxes minted | 6 per hour, 30 per day, per caller | 429 `rate_limit_error` / `too_many_inboxes` |
| Stored body | 2 MB | `truncated: true` on the message; the rest of it is gone. |
| Attachment bytes | 8 MB each | `content` is null and the metadata is kept, which is not the same as an empty file. |

> The minting ceiling counts against a keyed hash of the client IP, and a destroyed inbox still counts, so throwing one away is not a way to buy another. Behind somebody else’s proxy the forwarded header can be spoofed, which is a known weakness of the ceiling rather than a hole in the credential: nothing here authorises on that value.

## What is not here (not built yet)

Finding out by trying is worse than being told:

- No sending, in any form. A disposable inbox has no connection to send as, and adding one would make an anonymous, unauthenticated endpoint into an open relay.
- No rename. Changing your address means creating a second inbox: renaming in place would release the old local-part the instant it was clicked, and a confirmation already in flight would then be delivered to whoever was issued it next.
- No rules, filters, forwarding, webhooks or AI. `spam` is a flag on the message and nothing acted on it. Nothing was filed away, and nothing here is summarised or embedded.
- No bounces. Mail to a pooled domain that names neither a live disposable inbox nor an address the operator created is dropped silently, on purpose: a public address generator attracts dictionary attacks, and writing a delivery report to whatever return path the attack claims would make the install a backscatter source.
- No pooled domain on the hosted service yet. Until an operator enrols one, `GET /temp-mail/domains` answers with an empty list and creating an inbox answers 503 `temp_mail_unavailable`. Inbound delivery to a pooled domain has not been observed end to end on a live domain.

## Enrolling a domain, if you run the install

The pool is data, not configuration: whatever is enrolled is what gets handed out. Nothing automates the DNS, so four of these five steps are a human at a registrar.

1. Register a domain for it. Use one you are willing to let strangers hand out. Every address on it shares its reputation, which is also why the picker spreads new inboxes across the pool at random rather than filling the first one.
2. Add it in the app under Settings → Domains. That mints the sending identity and prints the DNS records to publish.
3. Publish the MX, SPF, DKIM and `_openemail-challenge` TXT records at the registrar. Verification reads live DNS and is re-checked on the cron; only a verified domain is ever offered.
4. Enrol the verified domain in the disposable pool with the operator script. Until it is enrolled it is an ordinary domain on the workspace.
5. Turn catch-all OFF. It is on by default now, and the enrolment script refuses a domain until it is off. Disposable delivery is decided before the ordinary recipient lookup, so no address row is ever written for a pooled domain; leaving catch-all on would start filing throwaway mail into a real mailbox.

> Reserved local-parts (`postmaster`, `abuse`, `security` and the rest of RFC 2142) can never be disposable and fall through to the ordinary mailbox instead. A pooled domain that black-holes its own abuse reports is a domain that stops being able to deliver anywhere. An address you create on a pooled domain yourself, such as `legal@` or `privacy@`, behaves the same way: mail to it lands in your mailbox, and nobody can be issued it as a disposable address.

## In this section

- [List pooled domains](https://openemail.uk/docs/api/temp-mail/domains.md): What this install will issue an address on.
- [Create an inbox](https://openemail.uk/docs/api/temp-mail/create.md): The address, and the one response with the token on it.
- [Retrieve an inbox](https://openemail.uk/docs/api/temp-mail/get.md): The lease, the count, and how many extensions are left.
- [Extend the lease](https://openemail.uk/docs/api/temp-mail/extend.md): Another hour, up to the day it is capped at.
- [Destroy an inbox](https://openemail.uk/docs/api/temp-mail/destroy.md): The mail now, the address in a week.
- [List messages](https://openemail.uk/docs/api/temp-mail/messages.md): The call you poll. Metadata only.
- [Retrieve a message](https://openemail.uk/docs/api/temp-mail/message.md): One message with its body, and what to render.
- [Delete a message](https://openemail.uk/docs/api/temp-mail/message-delete.md): One message and its bytes, at your request.
- [List attachments](https://openemail.uk/docs/api/temp-mail/attachments.md): The bytes, base64, and when they are missing.
- [From the SDK](https://openemail.uk/docs/sdk/reference/methods.md): The same nine calls as `tempMail.*`, one row each.
