Messages
Overview
The Messages API provides programmatic access to email messages ingested by Material Security. Retrieve a specific message by ID, run an asynchronous search across messages using MQL (Material Query Language) to find messages matching criteria such as sender, subject, or date range, and move messages back to the inbox when a remediation turns out to be a false positive.
These endpoints make up the Messages API:
Get Message (
GET /api/v1/messages/{id}): retrieves a single message by its encoded ID.Search Messages (
POST /api/v1/messages/search): starts an asynchronous search.Get Message Search Results (
GET /api/v1/messages/search/{id}): retrieves the status and results of a search.Move Messages to Inbox (
POST /api/v1/messages/move-to-inbox): queues a move of messages back to the inbox in every mailbox your token can reach.
Moving Messages Back to the Inbox
Move to Inbox is the one endpoint in this API that changes mailboxes, and it undoes a placement rather than creating one: it returns messages Material moved to spam, trash, or Surge Protection to the inbox. The work runs in the background across every mailbox your token can reach that holds the message, so a 200 means the request was accepted, not that any message has moved. Copies that are already in the inbox are skipped, so the mailboxCount in the response is an upper bound. In the audit log, the message_moved_to_inbox entries carrying a uAcctId are the authoritative record of what actually moved; a single further entry carrying numRequested is written when the request is queued and records the scope of the request rather than any completed move.
Response Structure
Message responses return an ID and a headers object containing date, subject, sender, recipients, and authentication results by default. The search endpoints follow the standard paginated two-part structure similar to the Issues endpoints:
meta: metadata about the result set, includingtotalCount,limit,hasMore, andnextCursorfor pagination.items: an array of message objects matching the search query.
Note that search is asynchronous. The Search Messages endpoint returns a search ID (format job.1.<base64>) and status immediately; results are only present in the Get Message Search Results response once status is "DONE".
The include Parameter
By default, message responses return the message ID and a headers object (date, subject, sender, recipients, authentication results). To keep responses lightweight, related data is excluded unless explicitly requested with the include parameter on the Get Message endpoint. Supported values are content, attachments, issue, mailboxes, and links. Pass multiple values as a comma-separated list (e.g. include=content,attachments,links).
Want a walkthrough?
Review Hello World.
Pagination and Rate Limiting
Material APIs use cursor-based pagination: you pass the
nextCursorvalue from one response as thecursorparameter in the next request.The
limitparameter controls page size on all paginated endpoints.Every paginated response includes a
metaobject withtotalCount,limit,hasMore, andnextCursorto help you know where you are in the result set.When
hasMoreis false andnextCursoris null, you've reached the last page.Move Messages to Inbox is rate limited per tenant by the number of messages a request targets, not by the number of requests. Over the limit, the response is a
429with aRetry-Afterheader giving the seconds to wait before retrying.
Retrieves the full details of a single email message by its encoded ID, including sender, recipients, subject, date, and attachment metadata.
Material Security API key
The encoded ID of the message to retrieve. Format: msg.1.<base64>.
msg.1.ZXhhbXBsZV9pZF8xMjMPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$A comma-separated list of additional fields to include in the response. Valid values: content, attachments, issue, mailboxes, links.
linksSuccessful response
An email message.
The encoded message ID. Format: msg.1.<base64>.
msg.1.ZXhhbXBsZV9pZF8xMjMBad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
GET /api/v1/messages/{id} HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Accept: */*
{
"id": "msg.1.ZXhhbXBsZV9pZF8xMjM",
"headers": {
"messageId": "text",
"date": "2024-01-15T12:00:00.000Z",
"subject": "text",
"from": "sender@example.com",
"to": [
"text"
],
"cc": [
"text"
],
"bcc": [
"text"
],
"authResults": {
"spf": "pass",
"dkim": "pass",
"dmarc": "pass"
}
},
"links": {
"meta": {
"totalCount": 1,
"limit": 1,
"hasMore": true,
"nextCursor": "text"
},
"items": [
{
"href": "https://example.com/login",
"text": "text",
"type": "a.href"
}
]
},
"issue": {
"id": "iss.1.ZXhhbXBsZV9pZF8xMjM"
},
"content": {
"parts": [
{
"mime": "text/html",
"body": "text"
}
]
},
"attachments": {
"meta": {
"totalCount": 1,
"limit": 1,
"hasMore": true,
"nextCursor": "text"
},
"items": [
{
"id": "text",
"filename": "text",
"mime": "application/pdf",
"size": 1,
"hashes": {
"sha256": "text",
"md5": "text"
}
}
]
},
"mailboxes": {
"meta": {
"totalCount": 1,
"limit": 1,
"hasMore": true,
"nextCursor": "text"
},
"items": [
{
"accountId": "acct.1.ZXhhbXBsZV9pZF8xMjM",
"email": "user@example.com",
"tags": [
"text"
]
}
]
}
}Starts an asynchronous search across ingested email messages using an MQL query and a date range. Returns a search ID (format job.1.<base64>) to use when polling for results.
Material Security API key
Parameters for starting a message search.
The MQL (Material Query Language) query used to filter messages. For example, message.from.addresses=="attacker@example.com" and message.subject:~"urgent payment" finds messages from that sender whose subject contains "urgent payment." The API accepts the same queries as message search in Material, so the easiest way to get valid syntax is to build your query there. Navigate to Explorer > Messages, then write your query in Query, which suggests field names and operators as you type. You can also set up your search in Quick or Prompt, then switch to Query to copy the query it creates. When you send the query in a JSON body, escape its double quotes.
message.from.addresses=="attacker@example.com" and message.subject:~"urgent payment"The start of the search time window in ISO 8601 format.
2024-01-01T00:00:00.000ZThe end of the search time window in ISO 8601 format.
2024-02-01T00:00:00.000ZA list of tenant domain IDs to filter results by. If omitted, searches across all tenants the caller has access to.
Successful response
The encoded ID of the search. Format: job.1.<base64>.
job.1.ZXhhbXBsZV9pZF8xMjMPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$The current status of the search. Valid values: CREATED (queued), STARTED (running), DONE (results ready).
Bad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
POST /api/v1/messages/search HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 186
{
"mqlSearchString": "message.from.addresses==\"attacker@example.com\" and message.subject:~\"urgent payment\"",
"startDate": "2026-01-01T00:00:00.000Z",
"endDate": "2026-01-02T00:00:00.000Z"
}{
"id": "job.1.ZXhhbXBsZV9pZF8xMjM",
"status": "CREATED"
}Moves up to 200 messages back to the inbox in every mailbox your token can reach that holds them, clearing the spam, trash, and Surge Protection placements Material applied. Use this to undo a remediation that turned out to be a false positive. The move runs in the background, so a 200 means the request was accepted and queued, not that any message has moved yet. It is best-effort and safe to repeat: each copy is re-checked against the mail provider at move time, and copies already in the inbox, in mailboxes Material can no longer reach, or on unsupported providers are skipped without failing the request. Because of that, mailboxCount is an upper bound rather than a result. The message_moved_to_inbox audit log entries carrying a uAcctId are the authoritative record of what actually moved; one further entry carrying numRequested is written when the request is queued and records its scope, not that anything moved. Message IDs that do not resolve to a message your token can reach come back in notFound and are simply not queued; if none of them resolve, the response is a 404 and nothing is queued. Requires the Modify Messages permission. An API token carries the permissions of the user who created it, so a token minted by a user without that permission receives a 403. Requests are rate limited by the number of messages they target, not by request count, against a single organization-wide budget shared by every token; over the limit the response is a 429 with a Retry-After header.
Material Security API key
The encoded IDs of the messages to move back to the inbox. Up to 200 per request.
msg.1.ZXhhbXBsZV9pZF8xMjMSuccessful response
The encoded ID of the queued move job. Format: job.1.<base64>. Quote this when contacting support; it is not pollable through this API.
job.1.ZXhhbXBsZV9pZF8xMjMPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$Always CREATED. The move runs in the background and this endpoint never reports it finishing.
How many of the supplied message IDs resolved to a message you can reach, and were queued for the move.
The supplied message IDs that did not resolve to a message you can reach. These were not queued. An ID appears here whether the message does not exist or is outside the tenants your token covers.
msg.1.ZXhhbXBsZV9pZF8xMjMUpper bound on the mailbox copies the move was queued against, summed across accepted messages. Copies already in the inbox are skipped at move time, so fewer may actually change.
Bad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
POST /api/v1/messages/move-to-inbox HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Content-Type: application/json
Accept: */*
Content-Length: 44
{
"messageIds": [
"msg.1.ZXhhbXBsZV9pZF8xMjM"
]
}{
"id": "job.1.ZXhhbXBsZV9pZF8xMjM",
"status": "text",
"accepted": 1,
"notFound": [
"msg.1.ZXhhbXBsZV9pZF8xMjM"
],
"mailboxCount": 1
}Retrieves the status and results of a message search. Poll this endpoint until status is DONE, at which point the response includes a paginated list of matching messages.
Material Security API key
The encoded ID of the search job to retrieve. Format: job.1.<base64>.
job.1.ZXhhbXBsZV9pZF8xMjMPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$The pagination cursor returned in a previous response. Pass this value to retrieve the next page of results.
eyJ2IjoxLCJvIjoyNSwibCI6MjV9The maximum number of items to return per page.
25Successful response
The encoded ID of the search. Format: job.1.<base64>.
job.1.ZXhhbXBsZV9pZF8xMjMPattern: ^[a-z]{1,6}\.[0-9]{1,2}\.[A-Za-z0-9_-]+$The current status of the search. Valid values: CREATED (queued), STARTED (running), DONE (results ready).
Bad request: invalid parameters or request body
Unauthorized: missing or invalid API key
Forbidden: the API key doesn't have permission to perform this action
Not found: the requested resource doesn't exist
Too many requests: the client has exceeded a rate limit and should retry later
Internal server error
GET /api/v1/messages/search/{id} HTTP/1.1
Host: your-instance.on.material.security
x-material-client-secret: YOUR_API_KEY
Accept: */*
{
"id": "job.1.ZXhhbXBsZV9pZF8xMjM",
"status": "CREATED",
"data": {
"meta": {
"totalCount": 1,
"limit": 1,
"hasMore": true,
"nextCursor": "text",
"truncated": true
},
"items": [
{
"id": "msg.1.ZXhhbXBsZV9pZF8xMjM",
"headers": {
"date": "2024-01-15T12:00:00.000Z",
"from": "text"
}
}
]
}
}Last updated
Was this helpful?

