When you need to act on many quarantined messages at once, use the batch
methods of the
customers.quarantinedMessages
resource in the Mail Admin Quarantine API:
- The
batchReleasemethod releases up to 500 messages. - The
batchDenymethod denies up to 500 messages.
Both methods are asynchronous. The API accepts the request, returns a
long-running operation, and processes the
messages in the background. You then poll the
get
method of the
customers.operations
resource until the operation is done and read the per-message results from the
response.
Compared to calling the single-message methods in a loop, batch methods count as one write request against your quota usage regardless of how many messages they contain.
Prerequisites
- You completed the steps in Set up the API.
- An access token that impersonates an administrator with an admin
quarantine privilege and includes the
https://www.googleapis.com/auth/gmail.admin.quarantinescope. See Authorize with domain-wide delegation. - The resource names of up to 500 quarantined messages in the
PENDING_TRIAGEstate, which you get from List and search quarantined messages.
Start a batch operation
To release messages in a batch, send a POST request to the
:batchRelease
custom method on the customers.quarantinedMessages resource. The requests
array contains one object per message, each with the message's resource name:
POST https://mailadminquarantine.googleapis.com/v1/customers/CUSTOMER_ID/quarantinedMessages:batchRelease
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
{
"requests": [
{"name": "customers/CUSTOMER_ID/quarantinedMessages/user1@example.com:18d3ef45a8b2c1d0"},
{"name": "customers/CUSTOMER_ID/quarantinedMessages/user2@example.com:18d3ef45a8b2c1d1"}
]
}
To deny messages in a batch, send the same request body to the
:batchDeny
custom method on the customers.quarantinedMessages resource instead:
POST https://mailadminquarantine.googleapis.com/v1/customers/CUSTOMER_ID/quarantinedMessages:batchDeny
Each request must contain between 1 and 500 entries. Every message must
belong to the customer in the request path. A request with more than 500
entries returns a 400 INVALID_ARGUMENT error.
The API responds immediately with an
Operation
resource:
{
"name": "customers/01abc23de/operations/7f1c2a0e-5b7d-4d3a-9c1e-2f4b6a8d0e13",
"metadata": {
"@type": "type.googleapis.com/google.gmail.mailadminquarantine.v1.BatchReleaseQuarantinedMessagesMetadata",
"state": "PROCESSING",
"processedCount": 0,
"totalCount": 2
},
"done": false
}
Store the name of the operation. You need it to check the operation's
progress.
Check the status of a batch operation
To check the progress of a batch operation, send a GET request to the
get
method on the operation's resource name:
GET https://mailadminquarantine.googleapis.com/v1/customers/CUSTOMER_ID/operations/OPERATION_ID
Authorization: Bearer ACCESS_TOKEN
The get method accepts either Admin Quarantine API scope, so a read-only
integration can monitor operations that another system started.
While the operation runs, the done field is set to false and the metadata
field reports progress:
| Metadata field | Description |
|---|---|
state |
PROCESSING while messages are being processed, then SUCCEEDED or FAILED. |
processedCount |
The number of messages processed so far. |
totalCount |
The total number of messages in the batch. |
When the operation finishes, the done field is set to true and the
response field contains a result for every message in the batch:
{
"name": "customers/01abc23de/operations/7f1c2a0e-5b7d-4d3a-9c1e-2f4b6a8d0e13",
"metadata": {
"@type": "type.googleapis.com/google.gmail.mailadminquarantine.v1.BatchReleaseQuarantinedMessagesMetadata",
"state": "SUCCEEDED",
"processedCount": 2,
"totalCount": 2
},
"done": true,
"response": {
"@type": "type.googleapis.com/google.gmail.mailadminquarantine.v1.BatchReleaseQuarantinedMessagesResponse",
"results": [
{
"name": "customers/01abc23de/quarantinedMessages/user1@example.com:18d3ef45a8b2c1d0",
"status": {},
"quarantinedMessage": {
"name": "customers/01abc23de/quarantinedMessages/user1@example.com:18d3ef45a8b2c1d0",
"messageId": "18d3ef45a8b2c1d0",
"messageOwner": "user1@example.com",
"state": "RELEASED"
}
},
{
"name": "customers/01abc23de/quarantinedMessages/user2@example.com:18d3ef45a8b2c1d1",
"status": {
"code": 5,
"message": "Quarantined message not found"
}
}
]
}
}
Each entry in results describes the outcome for one message:
| Result field | Description |
|---|---|
name |
The resource name of the message. |
status |
A google.rpc.Status. An empty object (code 0, OK) means the action succeeded. Otherwise, code and message explain why the message was skipped. |
quarantinedMessage |
The updated message with its new state. Present only when status is OK. |
A batch operation succeeds even if some individual messages fail. Always
inspect the status of each result rather than relying on the operation's
state alone.
If the whole operation fails, for example because the backend is unavailable,
the done field is set to true and the error field contains a
google.rpc.Status instead of response.
Example: Batch release and poll for completion
The following Python example starts a batch release and polls the operation every few seconds until it completes:
import time
import requests
BASE_URL = 'https://mailadminquarantine.googleapis.com/v1'
CUSTOMER_ID = '01abc23de' # Your customer ID without the leading 'C'.
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
}
# message_names is a list of up to 500 quarantined message resource names.
batch_url = f'{BASE_URL}/customers/{CUSTOMER_ID}/quarantinedMessages:batchRelease'
body = {'requests': [{'name': name} for name in message_names]}
operation = requests.post(batch_url, headers=headers, json=body)
operation.raise_for_status()
operation_name = operation.json()['name']
while True:
status = requests.get(f'{BASE_URL}/{operation_name}', headers=headers)
status.raise_for_status()
operation = status.json()
if operation.get('done'):
break
metadata = operation.get('metadata', {})
print(f"Processed {metadata.get('processedCount', 0)} of "
f"{metadata.get('totalCount', 0)} messages...")
time.sleep(5)
if 'error' in operation:
raise RuntimeError(f"Batch failed: {operation['error']}")
for result in operation['response'].get('results', []):
if result.get('status', {}).get('code', 0) == 0:
print(f"Released: {result['name']}")
else:
print(f"Skipped {result['name']}: {result['status'].get('message')}")