---
title: "Test an agent's forwarding"
method: POST
path: "/v1/agents/{id}/forwarding/verify"
tags: ["Agents"]
---

# Test an agent's forwarding

`POST /v1/agents/{id}/forwarding/verify`

## Request

- Base URL: `https://api.mails.ai`
- URL: `https://api.mails.ai/v1/agents/{id}/forwarding/verify`
- Auth: HTTP bearer

**Requires scope:** `manage`. For an agent in forwarding mode: sends one test email from mails.ai to the agent's own address. When your mail service forwards it to the agent's `forwarding_address`, it is dropped there (the agent never sees it) and `forwarding_status` becomes `verified`; if it has not arrived 15 minutes after it was sent, `failing`. Answers the agent at once: poll `GET /v1/agents/{id}`. While a test is on its way, calling again sends nothing more; at most 10 tests an hour per agent. A test key gets 403 `live_mode_required`, since this sends a real email. A test the email service does not take changes nothing and answers as a send does: 503 `upstream_unavailable` with `Retry-After` when it is not taking more mail, 422 `recipient_suppressed` when it refused the agent's address, else 502 `upstream_error`.

## Path parameters

- `id` string, required

## Response `200`

The agent, with forwarding_test_sent_at set.

- Agent
  - `id` string, required: Agent id (`agt_…`).
  - `name` string, required: The agent's name, the part of `email` before the `@`. The agent routes, and `agent` on sends and drafts, take it in place of the id; agents on different domains can share a name, so the id is the safer reference.
  - `email` string, email, required: The agent's address, `<name>@<domain>`, unique across mails.ai: the identity it sends as. It receives mail at `receiving_address`.
  - `domain` string, required: The domain part of `email`: your workspace's `<slug>.mails.ai`, or a custom domain that was verified when the agent was created.
  - `receiving_address` string, email, required: Where this agent receives mail. Every send's Reply-To is this address unless the send sets `reply_to`, so replies reach the agent. `<name>.<slug>@in.mails.ai` for an agent on your mails.ai address (`<name>@in.mails.ai` when the name is the slug); for an agent on your own domain, the name and the whole domain with its dots written as underscores, such as `billing.example_com@in.mails.ai`. Mail to any plus address of it reaches the agent too: add `+` and anything before the `@` (`qa.acme+github@in.mails.ai`, `qa.acme+run-1842@in.mails.ai`), so one agent can give every service or test run its own address. The address used is kept as the message's `delivered_to`, and the `to` filter on received mail matches it.
  - `workspace_id` string, required: Id of the workspace the agent belongs to (`wsk_…`).
  - `status` 'active' | 'paused' | 'archived', required: `active`; `paused`, when its send requests get 422 `agent_paused` and `pause_reason` says why; or `archived` after a DELETE, when it cannot send and the default list leaves it out.
  - `pause_reason` string: Why the agent is paused or archived: `manual` (paused with PATCH), `archived_via_api` (archived with DELETE), or a reason the automatic bounce, complaint and abuse checks set, such as `bounce_rate=0.0612`. Absent when there is none. The list leaves it out.
  - `daily_send_limit` integer: This agent's own cap on recipients in any 24 hours, on top of your plan's cap: each live send counts once per `to`, `cc` and `bcc` address. Past it, a send gets 429 `daily_limit_exceeded`. Absent when unset. The list leaves it out.
  - `hourly_send_limit` integer: This agent's own cap on recipients in any hour, on top of your plan's cap: each live send counts once per `to`, `cc` and `bcc` address. Past it, a send gets 429 `hourly_limit_exceeded`. Absent when unset. The list leaves it out.
  - `allowlist_domains` string[]: When present, this agent sends only to these domains and their subdomains; any other recipient in `to`, `cc` or `bcc` gets 422 `recipient_not_allowed`. The test address reply@test.mails.ai is always allowed. The list leaves it out.
  - `blocklist_domains` string[]: Domains (and their subdomains) this agent never sends to; a recipient on the list gets 422 `recipient_not_allowed`. The test address reply@test.mails.ai is never blocked. The list leaves it out.
  - `classify_inbound` boolean: Whether this agent's new mail is read for intent; a reply in a thread never is. The list leaves it out.
  - `unread_count` integer: Unread threads in the agent's inbox folder, on the list and on the single-agent GET and PATCH.
  - `forwarding` boolean: Forwarding mode: the agent's address is on your own domain, and the mail service that already hosts it forwards its mail to `forwarding_address`, so no MX record changes.
  - `forwarding_address` string, email, nullable: Where to forward the agent's mail: its `receiving_address`. Mail that arrives here is stored, threaded and sent to your webhooks like any received mail, with the agent's own address as the recipient. Null when forwarding is off.
  - `forwarding_status` 'not_verified' | 'verified' | 'failing' | 'null', nullable: `verified` once a test from `POST /v1/agents/{id}/forwarding/verify` arrived through the forward; `failing` when the last test had not arrived 15 minutes after it was sent; `not_verified` before that. Null when forwarding is off.
  - `forwarding_test_sent_at` string, date-time, nullable: When the last forwarding test was sent. Null when none was, or forwarding is off.
  - `forwarding_verified_at` string, date-time, nullable: When a forwarding test last arrived through the forward. Null when none has, or forwarding is off.
  - `created_at` string, date-time: When the agent was created.
  - `updated_at` string, date-time: When the agent last changed: an update, an archive, or an automatic pause or resume. The list leaves it out.

## Other responses

- `400`: Invalid request — malformed JSON or a field failed validation. Also `feature_not_enabled` when this server has a feature switched off (custom domains before they are available).
- `401`: Missing or invalid API key.
- `403`: The API key lacks the required scope (`insufficient_scope`), or the token belongs to an app connected by sign-in and this operation is closed to connected apps (`connected_app_not_allowed`).
- `404`: Resource not found in this workspace, or the agent a send names is archived (`agent_archived`).
- `409`: Conflict — duplicate resource, an in-progress idempotent request, a draft or scheduled message that started sending while it was being changed, or a thread deleted for good while one of its messages is being sent (`duplicate_resource`); or an agent name more than one agent has (`agent_name_conflict`: use the agent's id); or a status change on an archived agent, since archiving cannot be undone (`agent_archived`).
- `422`: Unprocessable — agent paused, recipient suppressed, a recipient at test.mails.ai other than reply@test.mails.ai (`unknown_test_address`), or abuse guard.
- `429`: Rate or quota limit exceeded. The body's `resets_at` and `retry_after_seconds` say when the request can succeed. A `Retry-After` header is sent only when that is 60 seconds or less; a longer wait (an hourly, daily or monthly cap) sends none, so retry at `resets_at` or raise the limit instead of sleeping.
- `500`: Internal server error.
- `502`: The email service the mail goes out through did not confirm it took it (`upstream_error`), so it is recorded as not sent. The message is a fixed sentence naming the request id to quote to support@mails.ai; the service's own error is never returned. When the service refuses every recipient as undeliverable the answer is 422 `recipient_suppressed` instead, and when it is only not taking more mail for now, 503 `upstream_unavailable`.
- `503`: Temporarily unavailable, and nothing was done. On a send: the email service is not taking more mail right now (`upstream_unavailable`), so nothing was sent; send it again after the seconds in `Retry-After`.

---

[API](https://skmtc.dev/mails/apis/mails-ai-api.md) · [All operations](https://skmtc.dev/mails/apis/mails-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mails/apis/mails-ai-api/revisions/4d4930a8c33b?raw)
