> [!WARNING]
> **Developer Preview:** Available as part of the [Google Workspace Developer Preview Program](https://developers.google.com/workspace/preview.md.txt), which grants early access to certain features.

When you need to act on many quarantined messages at once, use the batch
methods of the
[`customers.quarantinedMessages`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.quarantinedMessages.md.txt)
resource in the Mail Admin Quarantine API:

- The [`batchRelease`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.quarantinedMessages/batchRelease.md.txt) method releases up to 500 messages.
- The [`batchDeny`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.quarantinedMessages/batchDeny.md.txt) method denies up to 500 messages.

Both methods are asynchronous. The API accepts the request, returns a
[long-running operation](https://google.aip.dev/151), and processes the
messages in the background. You then poll the
[`get`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.operations/get.md.txt)
method of the
[`customers.operations`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.operations.md.txt)
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](https://developers.google.com/workspace/gmail/admin-quarantine/guides/quotas.md.txt) regardless of how
many messages they contain.

> [!WARNING]
> **Warning:** Releasing and denying messages are permanent. You can't return a message to the `PENDING_TRIAGE` [`state`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.quarantinedMessages.md.txt#State) after you release or deny it.

## Prerequisites

- You completed the steps in [Set up the API](https://developers.google.com/workspace/gmail/admin-quarantine/guides/setup.md.txt).
- An access token that impersonates an administrator with an admin quarantine privilege and includes the `https://www.googleapis.com/auth/gmail.admin.quarantine` scope. See [Authorize with domain-wide delegation](https://developers.google.com/workspace/gmail/admin-quarantine/guides/authorize.md.txt).
- The resource names of up to 500 quarantined messages in the `PENDING_TRIAGE` state, which you get from [List and search quarantined messages](https://developers.google.com/workspace/gmail/admin-quarantine/guides/list-search.md.txt).

## Start a batch operation

To release messages in a batch, send a `POST` request to the
[`:batchRelease`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.quarantinedMessages/batchRelease.md.txt)
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`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.quarantinedMessages/batchDeny.md.txt)
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`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.operations.md.txt#Operation)
resource:

```json
{
  "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`](https://developers.google.com/workspace/gmail/admin-quarantine/reference/rest/v1/customers.operations/get.md.txt)
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:

```json
{
  "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`](https://cloud.google.com/apis/design/errors#error_model). 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:

```python
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')}")
```

> [!NOTE]
> **Note:** Each `get` call counts as a read request against your quota. Poll every few seconds rather than in a tight loop. For details, see [Mail Admin Quarantine API usage quotas](https://developers.google.com/workspace/gmail/admin-quarantine/guides/quotas.md.txt).

## Next steps

- [Mail Admin Quarantine API usage quotas](https://developers.google.com/workspace/gmail/admin-quarantine/guides/quotas.md.txt)