> For the complete documentation index, see [llms.txt](https://docs.material.security/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.material.security/working-with-issues/explorer/message-search.md).

# Message Search

Use message search to find messages across every mailbox Material syncs, then take bulk actions on the results. You can also save a search as a detection.

To open message search, from the left navigation expand **Explorer**, then click **Messages**. You can also use ⌘K to open the quick action menu from anywhere in the Material UI and search messages from there.

<figure><img src="https://3411262179-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FksjM8NywYRSHu1IlfxdP%2Fuploads%2FPlKNlceA5ceUnw3459RJ%2Fimage.png?alt=media&amp;token=ee286ac2-bc34-45b9-8e9f-44f66b8c9985" alt=""><figcaption></figcaption></figure>

## Search modes

Message search has three modes, which you choose with **Search Mode** at the top of the search:

* **Quick**: a form where you search by **Date range**, **To**, **From**, **Subject**, **Message ID**, and **Has attachments**, without writing a query.
* **Prompt**: describe the messages you're looking for in plain English, and Material writes the query for you.
* **Query**: write the query yourself in MQL (Material Query Language), using the syntax and fields on this page.

{% hint style="info" %}
**Prompt** mode isn't available if you've opted out of Material's GenAI capabilities.
{% endhint %}

**Quick** and **Prompt** both create an MQL query. To see it, switch to **Query**. This is a good way to learn the syntax, or to get a valid query you can copy into a custom detection, a force unlock request, or the [Messages search API](https://docs.material.security/reference/api-v1).

***

## Write a query

In **Query** mode, Material suggests fields, operators, and values as you type, and checks the query as you go. **Search** is only available when the query is valid.

* Use the `DOWN` and `UP` arrow keys to move through the suggestions, then press `ENTER` or `TAB` to add the one you've selected.
* Press `ESCAPE` to close the suggestions, or `DOWN` to open them again.
* Your recent searches also show up in the suggestions, so you can rerun a query quickly.

Every query is made of fields, operators, and values. For example, this query finds unread messages from Gmail addresses that failed SPF:

```sql
message.from.addresses:~"@gmail.com" and message.status.is_unread and message.headers.spf == "fail"
```

### Fields

Fields are grouped under two namespaces:

| Namespace | What it searches                                                                                                             | Examples                                                                           |
| --------- | ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `message` | The message itself: sender, recipients, subject, headers, attachments, links, mailbox status, and how Material classified it | `message.subject`, `message.from.addresses`, `message.attachments.has_attachments` |
| `account` | The mailbox the message is in                                                                                                | `account.email`                                                                    |

Separate each level of a field with a period, for example `message.from.domains`.

### Values

* Put text values in double quotes, for example `message.subject:~"invoice"`.
* Write numbers without quotes, for example `message.recipients.count > 50`.
* Use true/false fields on their own, without an operator. For example, `message.status.is_inbox` finds messages in the inbox, and `not message.status.is_inbox` finds messages that aren't.

{% hint style="warning" %}
**Close every quote.** An unclosed quote makes the query invalid, and **Search** stays unavailable until you fix it.
{% endhint %}

### Operators

| Operator             | Meaning                                                                  | Example                                                                             |
| -------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| `:~`                 | Contains (not case-sensitive)                                            | `message.subject:~"invoice"`                                                        |
| `!:~`                | Doesn't contain (not case-sensitive)                                     | `message.from.domains!:~"example.com"`                                              |
| `:`                  | Contains (case-sensitive)                                                | `message.subject:"URGENT"`                                                          |
| `!:`                 | Doesn't contain (case-sensitive)                                         | `message.subject!:"RE:"`                                                            |
| `==`                 | Is exactly                                                               | `message.headers.dmarc == "fail"`                                                   |
| `!=`                 | Isn't exactly                                                            | `message.headers.spf != "pass"`                                                     |
| `<`, `<=`, `>`, `>=` | Less than, less than or equal to, greater than, greater than or equal to | `message.recipients.count >= 100`                                                   |
| `is null`            | Has no value                                                             | `message.subject is null`                                                           |
| `is not null`        | Has a value                                                              | `message.headers.in_reply_to is not null`                                           |
| `in`                 | Value is in a list field                                                 | `"<app-id>" in account.appsWithAccess`                                              |
| `and`                | Both conditions must be true                                             | `message.status.is_inbox and message.links.has_links`                               |
| `or`                 | Either condition must be true                                            | `message.status.is_spam or message.status.is_trash`                                 |
| `not`                | The condition must be false                                              | `not message.attachments.has_attachments`                                           |
| `( )`                | Group conditions                                                         | `message.links.has_links and (message.status.is_inbox or message.status.is_unread)` |

`in` only works with list fields like `account.appsWithAccess`. To match one of several values, join conditions with `or`, for example `message.subject:~"invoice" or message.subject:~"payment"`.

When you aren't sure which operator to use, use `:~`. It matches text anywhere in the field and ignores capitalization.

When a field holds more than one value, such as `message.from.addresses` or `message.attachments.name`, a positive operator like `:~` matches if **any** value matches. A negative operator like `!:~` matches only if **none** of the values match.

`and` is applied before `or`, so use parentheses whenever you combine them.

### Regular expressions

To match a pattern, use a regex function. Regular expressions only work inside these functions, not with the operators above.

| Function                            | Matches when                                               |
| ----------------------------------- | ---------------------------------------------------------- |
| `regex.contains(field, "pattern")`  | Part of the field matches the pattern (case-sensitive)     |
| `regex.icontains(field, "pattern")` | Part of the field matches the pattern (not case-sensitive) |
| `regex.match(field, "pattern")`     | The whole field matches the pattern (case-sensitive)       |
| `regex.imatch(field, "pattern")`    | The whole field matches the pattern (not case-sensitive)   |

You can add more than one pattern. The function matches if any of them match:

```
regex.icontains(message.subject, "invoice", "payment due", "wire transfer")
```

{% hint style="info" %}
Patterns use [RE2 syntax](https://github.com/google/re2/wiki/Syntax).
{% endhint %}

<details>

<summary><strong>Example queries</strong></summary>

**Zipped attachments in the inbox**

`message.attachments.mime_type:~"zip" and message.status.is_inbox`

**Messages from Gmail senders in spam or trash**

`message.from.addresses:~"@gmail.com" and (message.status.is_spam or message.status.is_trash)`

**Messages that failed DMARC and include links**

`message.headers.dmarc == "fail" and message.links.has_links`

**Messages sent to a personal email address with an attachment**

`message.recipients.has_personal_email and message.attachments.has_attachments`

**Messages with any sensitive category**

`message.context.category.any`

**Messages marked suspicious**

`message.riskType.suspicious`

**Messages sent to an address where the username starts with "g," ends with "a," and is 8 characters long**

`regex.icontains(message.to, "g.{6}a@")`

</details>

***

## Search fields and detection fields

Custom detections, custom sensitive categories, sensitive category exemptions, and force unlock requests use the same MQL, with two differences:

* **Some fields are search-only.** `message.context`, `message.riskType`, `account.appsWithAccess`, and `account.appsWithContentAccess` describe how Material has already classified a message, so they're only available in message search. A search that uses them can't be saved as a detection.
* **Message body fields need content access.** `message.body` fields are only available if you have permission to view message content.

The **Available in** column in the reference below shows where you can use each field.

## Daily search quota

Message search has a daily quota. Searches you run in the Material UI, through the API, and through the Material MCP server all count toward the same quota.

Each search uses one unit of quota for every UTC day its date range covers, not one per search. For example:

* A search over one week uses 7 units.
* Ten searches over the same week use 70 units.
* One search over a year uses 365 units.
* A search over one full day in U.S. Eastern Time covers parts of two UTC days, so it uses 2 units.

Running exactly the same search again within 24 hours returns the saved results and doesn't use any quota.

When you reach the quota, the Material UI shows an error with the time the quota resets, and API requests return `429 Too Many Requests`. A search that's rejected doesn't use any quota, so a search with a shorter date range may still run. The quota resets at midnight UTC.

If you have questions about your quota, contact Material support.

## Search efficiently

These tips help your searches run faster and use less of your quota:

* **Search the shortest date range you need.** A narrow date range is faster and uses less quota than a broad search you filter afterward.
* **Combine searches when possible.** How complex a search is doesn't affect how much quota it uses, so one combined search uses less than several separate ones. For example, to find messages from two addresses, run `message.from.addresses == "a@example.com" or message.from.addresses == "b@example.com"` instead of one search per address. To cover a long period, use a few wide date ranges rather than many short ones.
* **Use exact match or contains when you know the value.** Regex functions check every message in the date range, so they're slower than `==`, `:`, and `:~`.
* **Use a true/false field when one fits.** Fields like `message.attachments.has_attachments`, `message.links.has_links`, and `message.recipients.has_personal_email` are faster than a text or regex match that does the same thing.
* **Only search the message body when you need to.** Material already pulls details like links, attachments, and recipients out of each message, and searching those fields is faster. If you do need the body, `message.body.html`, `message.body.plain`, and `message.body.text` all take about the same time.
* **Leave out conditions you don't need.** The order of conditions in a query doesn't affect speed.
* **Reuse saved results.** Running exactly the same query over the same date range within 24 hours returns the saved results, whether you search in the Material UI, through the API, or through the MCP server. When the Material UI shows saved results, a banner tells you how old they are. Click **re-run the search** in the banner only if you need the latest results, because that runs the search again and uses quota. If you start the same search twice at once, both run, so wait for the first one to finish before you retry.

## Message Search Operator Reference

<table><thead><tr><th width="300">Property</th><th>Type</th><th>Description</th><th>Available in</th></tr></thead><tbody><tr><td><strong>message</strong></td><td>object</td><td>Details about the message itself</td><td>Search, detections</td></tr><tr><td><strong>message.subject</strong></td><td>string</td><td>The message subject</td><td>Search, detections</td></tr><tr><td><strong>message.from</strong></td><td>object</td><td>The sender of the message</td><td>Search, detections</td></tr><tr><td><strong>message.from.addresses</strong></td><td>array</td><td>The sender's email addresses, including addresses from the <code>Original-From</code> and <code>Original-Sender</code> headers</td><td>Search, detections</td></tr><tr><td><strong>message.from.display_names</strong></td><td>array</td><td>The sender's display names</td><td>Search, detections</td></tr><tr><td><strong>message.from.domains</strong></td><td>array</td><td>The domains of the sender's email addresses</td><td>Search, detections</td></tr><tr><td><strong>message.from.raw</strong></td><td>string</td><td>The <code>From</code> header exactly as it appears in the message</td><td>Search, detections</td></tr><tr><td><strong>message.to</strong></td><td>string</td><td>The email addresses in the <code>To</code> header, without display names</td><td>Search, detections</td></tr><tr><td><strong>message.cc</strong></td><td>string</td><td>The email addresses in the <code>Cc</code> header, without display names</td><td>Search, detections</td></tr><tr><td><strong>message.bcc</strong></td><td>string</td><td>The email addresses in the <code>Bcc</code> header, without display names. Only available for sent messages from inside your organization.</td><td>Search, detections</td></tr><tr><td><strong>message.recipients</strong></td><td>object</td><td>Everyone the message was sent to: the to, cc, and bcc fields combined</td><td>Search, detections</td></tr><tr><td><strong>message.recipients.addresses</strong></td><td>string</td><td>The recipients' email addresses</td><td>Search, detections</td></tr><tr><td><strong>message.recipients.count</strong></td><td>number</td><td>The number of different email addresses the message was sent to. An address that appears in more than one field counts once. The sender's copy in their sent folder includes bcc recipients. A recipient's copy only includes the addresses that recipient can see.</td><td>Search, detections</td></tr><tr><td><strong>message.recipients.has_disposable_email</strong></td><td>bool</td><td>Messages where any recipient's address is from a disposable email service</td><td>Search, detections</td></tr><tr><td><strong>message.recipients.has_personal_email</strong></td><td>bool</td><td>Messages where any recipient's address is at a personal email domain, such as gmail.com. This doesn't check whether the address belongs to a specific person.</td><td>Search, detections</td></tr><tr><td><strong>message.recipients.has_vip</strong></td><td>bool</td><td>Messages where any recipient is marked as a VIP</td><td>Search, detections</td></tr><tr><td><strong>message.participants</strong></td><td>object</td><td>Everyone on the message: the sender and all recipients combined</td><td>Search, detections</td></tr><tr><td><strong>message.participants.addresses</strong></td><td>string</td><td>The email addresses of the sender and all recipients</td><td>Search, detections</td></tr><tr><td><strong>message.participants.has_disposable_email</strong></td><td>bool</td><td>Messages where anyone on the message has an address from a disposable email service</td><td>Search, detections</td></tr><tr><td><strong>message.participants.has_personal_email</strong></td><td>bool</td><td>Messages where anyone on the message has an address at a personal email domain, such as gmail.com. This doesn't check whether the address belongs to a specific person.</td><td>Search, detections</td></tr><tr><td><strong>message.participants.has_vip</strong></td><td>bool</td><td>Messages where anyone on the message is marked as a VIP</td><td>Search, detections</td></tr><tr><td><strong>message.headers</strong></td><td>object</td><td>Details from the message headers</td><td>Search, detections</td></tr><tr><td><strong>message.headers.dkim</strong></td><td>string</td><td>The result of the DKIM check: <code>pass</code>, <code>fail</code>, <code>neutral</code>, <code>none</code>, <code>policy</code>, <code>temperror</code>, or <code>permerror</code>. In most searches, compare with <code>== "pass"</code> or <code>!= "fail"</code> rather than listing each result.</td><td>Search, detections</td></tr><tr><td><strong>message.headers.dmarc</strong></td><td>string</td><td>The result of the DMARC check: <code>pass</code>, <code>fail</code>, <code>none</code>, <code>temperror</code>, or <code>permerror</code>. In most searches, compare with <code>== "pass"</code> or <code>!= "fail"</code> rather than listing each result.</td><td>Search, detections</td></tr><tr><td><strong>message.headers.in_reply_to</strong></td><td>string</td><td>The <code>In-Reply-To</code> header: the message ID of the message this one replies to</td><td>Search, detections</td></tr><tr><td><strong>message.headers.message_id</strong></td><td>string</td><td>The message ID set by the sender's email client, usually in the format <code>&#x3C;...@...></code></td><td>Search, detections</td></tr><tr><td><strong>message.headers.original_from</strong></td><td>string</td><td>The <code>Original-From</code> header</td><td>Search, detections</td></tr><tr><td><strong>message.headers.original_sender</strong></td><td>string</td><td>The <code>Original-Sender</code> header</td><td>Search, detections</td></tr><tr><td><strong>message.headers.reply_to</strong></td><td>string</td><td>The email addresses in the <code>Reply-To</code> header, where replies to this message go</td><td>Search, detections</td></tr><tr><td><strong>message.headers.spf</strong></td><td>string</td><td>The result of the SPF check: <code>pass</code>, <code>fail</code>, <code>softfail</code>, <code>neutral</code>, <code>none</code>, <code>policy</code>, <code>temperror</code>, or <code>permerror</code>. In most searches, compare with <code>== "pass"</code> or <code>!= "fail"</code> rather than listing each result.</td><td>Search, detections</td></tr><tr><td><strong>message.attachments</strong></td><td>object</td><td>Details about the message's attachments</td><td>Search, detections</td></tr><tr><td><strong>message.attachments.has_archive</strong></td><td>bool</td><td>Messages with an archive attachment, such as a .zip file</td><td>Search, detections</td></tr><tr><td><strong>message.attachments.has_attachments</strong></td><td>bool</td><td>Messages with any attachments</td><td>Search, detections</td></tr><tr><td><strong>message.attachments.hash</strong></td><td>string</td><td>The MD5 or SHA-256 hashes of the attachments</td><td>Search, detections</td></tr><tr><td><strong>message.attachments.mime_type</strong></td><td>string</td><td>The MIME types of the attachments, such as <code>application/pdf</code></td><td>Search, detections</td></tr><tr><td><strong>message.attachments.name</strong></td><td>string</td><td>The file names of the attachments</td><td>Search, detections</td></tr><tr><td><strong>message.links</strong></td><td>object</td><td>Details about the links in the message</td><td>Search, detections</td></tr><tr><td><strong>message.links.has_links</strong></td><td>bool</td><td>Messages with any links</td><td>Search, detections</td></tr><tr><td><strong>message.links.text</strong></td><td>string</td><td>The display text of the links</td><td>Search, detections</td></tr><tr><td><strong>message.links.url</strong></td><td>string</td><td>The URLs of the links</td><td>Search, detections</td></tr><tr><td><strong>message.body</strong></td><td>object</td><td>The content of the message. Requires permission to view message content.</td><td>Search, detections</td></tr><tr><td><strong>message.body.html</strong></td><td>string</td><td>The HTML version of the message body</td><td>Search, detections</td></tr><tr><td><strong>message.body.plain</strong></td><td>string</td><td>The plain text version of the message body</td><td>Search, detections</td></tr><tr><td><strong>message.body.text</strong></td><td>string</td><td>All text content in the message body</td><td>Search, detections</td></tr><tr><td><strong>message.status</strong></td><td>object</td><td>Where the message is in the mailbox and whether it's been read</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_draft</strong></td><td>bool</td><td>Messages that are drafts</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_inbox</strong></td><td>bool</td><td>Messages in the inbox</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_retrieved</strong></td><td>bool</td><td>Microsoft 365 messages in the Retrieved folder</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_sent</strong></td><td>bool</td><td>Messages in the sent folder</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_spam</strong></td><td>bool</td><td>Messages in the spam folder</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_surge</strong></td><td>bool</td><td>Messages that <a href="/email-protections/email-bomb-attacks.md">Email Bomb Protection</a> moved to the surge protection label or folder</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_trash</strong></td><td>bool</td><td>Messages in the trash</td><td>Search, detections</td></tr><tr><td><strong>message.status.is_unread</strong></td><td>bool</td><td>Messages that haven't been read</td><td>Search, detections</td></tr><tr><td><strong>message.context</strong></td><td>object</td><td>How Material has classified the message</td><td>Search</td></tr><tr><td><strong>message.context.account_access</strong></td><td>bool</td><td>Messages about account access</td><td>Search</td></tr><tr><td><strong>message.context.account_activation</strong></td><td>bool</td><td>Messages about activating an account</td><td>Search</td></tr><tr><td><strong>message.context.account_reset</strong></td><td>bool</td><td>Messages about resetting a password or account</td><td>Search</td></tr><tr><td><strong>message.context.account_verification</strong></td><td>bool</td><td>Messages about verifying an account</td><td>Search</td></tr><tr><td><strong>message.context.appId</strong></td><td>array</td><td>Search for messages by application ID</td><td>Search</td></tr><tr><td><strong>message.context.case</strong></td><td>object</td><td>Search for messages by issue</td><td>Search</td></tr><tr><td><strong>message.context.case.id</strong></td><td>string</td><td>Search for messages in a specific issue, by issue ID. Use <code>==</code> or <code>!=</code>.</td><td>Search</td></tr><tr><td><strong>message.context.case.in_case</strong></td><td>bool</td><td>Messages that are currently in an issue</td><td>Search</td></tr><tr><td><strong>message.context.category</strong></td><td>object</td><td>Search for messages by the kind of sensitive data they contain</td><td>Search</td></tr><tr><td><strong>message.context.category.any</strong></td><td>bool</td><td>Messages that match any enabled sensitive category</td><td>Search</td></tr><tr><td><strong>message.context.category.id</strong></td><td>string</td><td>Search for messages that match a specific sensitive category. As you type, Material suggests your categories by name. Use <code>==</code> or <code>!=</code>.</td><td>Search</td></tr><tr><td><strong>message.context.exempt</strong></td><td>bool</td><td>Messages that are exempt</td><td>Search</td></tr><tr><td><strong>message.context.rule</strong></td><td>object</td><td>Search for messages by the detections they matched</td><td>Search</td></tr><tr><td><strong>message.context.rule.id</strong></td><td>array</td><td>Search for messages that matched a specific detection. As you type, Material suggests your detections by name. Use <code>==</code> or <code>!=</code>.</td><td>Search</td></tr><tr><td><strong>message.context.rule.matched</strong></td><td>bool</td><td>Messages that matched any detection</td><td>Search</td></tr><tr><td><strong>message.riskType</strong></td><td>object</td><td>The message's risk type: sensitive, suspicious, or none</td><td>Search</td></tr><tr><td><strong>message.riskType.none</strong></td><td>bool</td><td>Messages that aren't marked sensitive or suspicious</td><td>Search</td></tr><tr><td><strong>message.riskType.sensitive</strong></td><td>bool</td><td>Messages marked sensitive</td><td>Search</td></tr><tr><td><strong>message.riskType.suspicious</strong></td><td>bool</td><td>Messages marked suspicious</td><td>Search</td></tr><tr><td><strong>account</strong></td><td>object</td><td>Details about the account whose mailbox has the message</td><td>Search, detections</td></tr><tr><td><strong>account.appsWithAccess</strong></td><td>array</td><td>IDs of the OAuth apps that have access to this account. Use with <code>in</code>, for example <code>"&#x3C;app-id>" in account.appsWithAccess</code>.</td><td>Search</td></tr><tr><td><strong>account.appsWithContentAccess</strong></td><td>array</td><td>IDs of the OAuth apps that have access to this account and can read its message content. Use with <code>in</code>.</td><td>Search</td></tr><tr><td><strong>account.email</strong></td><td>string</td><td>The email address of the account whose mailbox has the message</td><td>Search, detections</td></tr></tbody></table>

***

## FAQ

**Why does my search say a field is unavailable?**

`message.body` fields need permission to view message content. If you don't have it, Material shows that the field is unavailable. Ask an admin to update your role.

**Why can't I save my search as a detection?**

Detections can't use `message.context` or `message.riskType`. These fields are only available in search. Remove them from the query, then save it again.

**Can I search the text inside attachments?**

No. Message search matches attachment names, MIME types, and hashes, and whether a message has attachments. It doesn't search the text inside an attachment, such as the text in a PDF.

**How do I search by date?**

Use the **Date range** picker next to the search. MQL doesn't have date fields.

**Can I use these queries with the API?**

Yes. The [Messages search API](https://docs.material.security/reference/api-v1) accepts the same queries. Build and test your query in **Query** mode first, then copy it into your request.

**Can I search for messages by Gmail label or Outlook folder?**

You can search by the standard mailbox locations with `message.status`, such as `message.status.is_inbox` or `message.status.is_spam`. Custom labels and folders aren't searchable.


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.material.security/working-with-issues/explorer/message-search.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
