> For the complete documentation index, see [llms.txt](https://docs.resifactory.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.resifactory.net/imap/capabilities.md).

# What the server supports

This page is for people writing their own client, or debugging one that behaves oddly. If you are just setting up a mail app, you do not need it — [Connection settings](/imap/connection-settings.md) is enough.

## Commands

### Fully supported

| Command                        | Notes                                                                      |
| ------------------------------ | -------------------------------------------------------------------------- |
| `LOGIN` / `AUTHENTICATE PLAIN` | Over TLS only. Username + [app password](/imap/app-passwords.md).          |
| `CAPABILITY`                   | Standard.                                                                  |
| `LIST` / `LSUB`                | Returns exactly one mailbox: `INBOX`.                                      |
| `SELECT` / `EXAMINE`           | On `INBOX`. Returns `EXISTS`, `UIDNEXT`, `UIDVALIDITY`, and the flag set.  |
| `STATUS`                       | Including `MESSAGES` and `UNSEEN`.                                         |
| `SEARCH` / `UID SEARCH`        | See [Search](#search) below.                                               |
| `FETCH` / `UID FETCH`          | Envelopes, flags, internal date, headers, body parts, `RFC822`, `BODY[]`.  |
| `STORE` / `UID STORE`          | Flag changes — see [Flags](#flags).                                        |
| `IDLE`                         | New mail is pushed the moment it is stored.                                |
| `NOOP`                         | Reports mailbox growth, so polling clients are told when new mail arrives. |
| `UNSELECT` / `CLOSE`           | Standard.                                                                  |
| `LOGOUT`                       | Standard.                                                                  |
| `SUBSCRIBE` / `UNSUBSCRIBE`    | Accepted for `INBOX`. `INBOX` is always listed.                            |

### Not supported

These return an error rather than pretending to work:

| Command                      | Why                                                                      |
| ---------------------------- | ------------------------------------------------------------------------ |
| `CREATE`, `DELETE`, `RENAME` | There is one mailbox and it is not yours to restructure.                 |
| `APPEND`                     | The server is read-only; you cannot upload mail into it.                 |
| `COPY` / `MOVE`              | Nowhere to copy to — there is only `INBOX`.                              |
| `EXPUNGE`                    | Accepted but does nothing. Nothing is ever expunged from under a client. |

There is **no SMTP** — nothing here sends mail.

## Search

`SEARCH` and `UID SEARCH` are supported, including compound `OR` and `NOT` expressions. All top-level criteria combine with `AND`.

### Supported criteria

| Criterion                                                                         | Matches against                                                |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| `SUBJECT <string>`                                                                | The subject, substring match.                                  |
| `FROM <string>`                                                                   | The sender address.                                            |
| `TO <string>`                                                                     | The recipient address — this is how you narrow to one mailbox. |
| `HEADER SUBJECT/FROM/TO <string>`                                                 | Same three fields.                                             |
| `BODY <string>`                                                                   | The message body only.                                         |
| `TEXT <string>`                                                                   | Headers **or** body — subject, from, to, and body.             |
| `SINCE` / `BEFORE`                                                                | The date the message was received.                             |
| `SEEN`, `UNSEEN`, `ANSWERED`, `FLAGGED`, `DELETED`, `DRAFT` and their `UN-` forms | Flags.                                                         |
| `KEYWORD` / `UNKEYWORD`                                                           | Flags.                                                         |
| `UID <set>`                                                                       | UID ranges, including `1:*`.                                   |
| Sequence sets                                                                     | Message positions.                                             |
| `NOT <criterion>`                                                                 | Negation, nestable.                                            |
| `OR <a> <b>`                                                                      | Alternation, nestable.                                         |

Multiple `BODY` or `TEXT` terms are each applied — they are ANDed, not collapsed to the last one.

### Refused criteria

These are **rejected** with a tagged `NO [CANNOT]` response:

| Criterion                                    | Why                                                                                             |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `SENTSINCE` / `SENTBEFORE`                   | The server indexes when a message *arrived*, not the `Date:` header it claims.                  |
| `LARGER` / `SMALLER`                         | Reported message size is computed at render time and would not match what a size filter tested. |
| `MODSEQ`                                     | `CONDSTORE` is not implemented.                                                                 |
| `HEADER <other field>`                       | Only Subject, From and To are indexed.                                                          |
| `OR` / `NOT` nested more than 16 levels deep | A bound against pathological queries.                                                           |

{% hint style="success" %}

### Why refusing matters

A search starts from "every message in this mailbox" and narrows with the terms it was given. If the server quietly **dropped** a term it did not understand, the result would not be narrower — it would be **everything**, returned with a success code.

A client that asked `OR FROM "shop.example" FROM "retail.example"` and got the entire mailbox back, tagged `OK`, has no way to tell that apart from a mailbox where every message really is from those two senders. So the server fails the command instead. A failure is recoverable — your client can retry with a simpler query or fall back to a full sync. A confidently wrong answer is not.
{% endhint %}

If you get `NO [CANNOT]`, rewrite the query using the supported criteria above.

## Flags

The advertised flags are:

```
\Seen  \Answered  \Flagged  \Deleted  \Draft
```

`STORE` works for all of them. `\Seen` is the one that matters in practice — marking mail read in your client marks it read everywhere, including the web console, and vice versa.

Note that `\Deleted` does **not** remove anything: `EXPUNGE` is a no-op, so the flag is just a flag.

## UIDs and UIDVALIDITY

* **UIDs are stable and never reused.** A message keeps its UID for as long as it is retained.
* **`UIDVALIDITY` changes when the set of mailboxes a credential can see changes** — for example when you buy more mailboxes on a unified credential, or a batch's membership changes.

When `UIDVALIDITY` changes, a correct client discards its cache and resyncs. That is the intended behaviour: the numbering it remembers genuinely no longer describes the same set of mail. Nothing is lost.

{% hint style="info" %}
If you are writing your own client: **address by UID, not by sequence number.** Sequence numbers are positions in the current view, and they renumber when messages leave the view — because of retention, or because they aged out of an [inbox window](/imap/inbox-window.md). UIDs never move.
{% endhint %}

## Message counts and `EXISTS`

`SELECT` reports the number of messages **that credential can currently see** — which is to say, after its [inbox window](/imap/inbox-window.md) and [scope](/imap/scopes.md) are applied. It is not the total size of your mail.

If `EXISTS` is much smaller than you expected, that is almost always the window working as configured, not missing mail. Check the window on the app password.

`NOOP` reports **growth** — new mail arriving. It does not announce shrinkage (retention, or messages ageing out of a window), because IMAP has no way to describe that without `EXPUNGE`, which this server does not emit. Clients that address by UID are unaffected either way.

## IDLE

`IDLE` is supported and is the right way to wait for mail. New messages are pushed as untagged `EXISTS` as soon as they are stored — you do not need to poll.

If your client cannot do IDLE, `NOOP` on a sensible interval will tell you when the mailbox has grown. Do not spin: polling every few hundred milliseconds gets you nothing that IDLE would not have given you sooner.

## Authentication limits

* TLS is mandatory. There is no plaintext or STARTTLS path.
* Repeated authentication failures temporarily block further attempts from that username and IP. Successful authentication clears it. Do not retry in a tight loop after a failure — see [Troubleshooting IMAP](/imap/troubleshooting.md).
* If the app password has an [IP allowlist](/imap/app-passwords.md), a connection from outside it is rejected exactly like a wrong password.
* Revoking an app password **terminates its live sessions immediately**.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.resifactory.net/imap/capabilities.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
