---
title: "Reply to a received email (the agent answers)"
method: POST
path: "/inbound/{uid}/reply"
tags: ["Inbound"]
---

# Reply to a received email (the agent answers)

`POST /inbound/{uid}/reply`

## Request

- Base URL: `https://api.oveyon.com/v1`
- URL: `https://api.oveyon.com/v1/inbound/{uid}/reply`
- Auth: HTTP bearer

Sends a plain-text reply FROM the mailbox that received the message TO its Reply-To/From, threaded (In-Reply-To/References) and recorded in the conversation. Counts one quota unit and consumes warm-up like any outbound mail. Loop guard: the reply carries `Auto-Submitted: auto-replied`; replying to automated mail (Auto-Submitted, Precedence bulk/list/junk, null sender) is refused with 422 auto_submitted; at most 5 replies per message and 10 automated replies per conversation per hour (429). A message held by the agent-safety verdict answers 409 message_quarantined until a human releases it — the reply is never the tool that releases. When the message went to several mailboxes and only some copies are held (`partial`), the reply goes out from the first mailbox whose copy was delivered (or the one in `from`). Sending policies apply to replies as to /v1/send for the two actions that decide WHETHER mail leaves: `recusar` answers 422 policy_refused, and `reter_para_aprovacao` turns the reply into a draft awaiting a person (status `held`, `requestedBy: policy:<name>`) — that is how a key marked «require human approval» holds every reply of an agent. A key in sandbox accepts and freezes the reply without delivering it (status `sandbox`), even when hold or an approval policy is requested; no approval link is created. Sandbox on this endpoint comes from the key profile, not a request field or header.

## Path parameters

- `uid` string, required

## Request body

- InboundReplyRequest
  - `text` string, required: Plain-text body of the reply. The From is always the mailbox that received the message; the To is the original Reply-To/From; In-Reply-To and References are set for you.
  - `subject` string: Optional. Defaults to `Re: <original subject>`. Line breaks are rejected (400 bad_subject).
  - `idempotencyKey` string: Optional task/order key (or the Idempotency-Key header). Within the account, a retained key returns the original reply id for 24 hours from its first claim. After expiry a retry can create another message. Namespaced apart from /v1/send keys.
  - `from` string: Optional: which mailbox of the message replies (one of `recipients[].to`). Default: the first mailbox whose copy was delivered. A mailbox whose copy is held answers 409 recipient_quarantined; an address that did not receive the message, 422 from_not_recipient; an address outside ASCII, 400 with `reason: address_not_ascii`.
  - `hold` boolean: Hold the REPLY for a human to approve (status `held`); the key that replies does not approve (approve:holds). Ignored when the key itself is in sandbox (status `sandbox`).
  - `holdTtl` integer: Seconds until the held reply expires (default 86400).
  - `holdNote` string: What the agent wants the approver to know.

## Response `200`

Duplicate idempotency key — the earlier reply id

- DuplicateResponse
  - `id` string, uuid, required: The id of the ORIGINAL message (the one the idempotencyKey created).
  - `status` 'duplicate', required
  - `message` string, required

## Other responses

- `202`: Accepted and queued (`accepted`), frozen in sandbox (`sandbox`), or held for approval (`held`).
- `400`: Invalid request — fix it before retrying. Includes `invalid_json`: with `Content-Type: application/json` the body must be valid JSON and cannot be empty; and `request_too_complex` (with `limit`): the body has a list of more than 10,000 items, more than 60,000 values in total, more than 64 levels of nesting or a number/literal longer than 32 characters — text does not count, so a large attachment is one value. An address outside ASCII (accents, a full-width @, a control character, an international domain not in xn-- form) is refused here with `reason: address_not_ascii` and the `field` it came in (`error` stays the family of that field: `bad_recipient` for to/cc/bcc, `invalid_email` for contacts, `bad_request` otherwise).
- `401`: Missing or invalid API key.
- `403`: sending_disabled, or the key lacks reply:inbound (`insufficient_scope`); or any of the common 403s (key_disabled, account_suspended…). 403 is not a limit: do not retry.
- `404`: Does not exist, belongs to another account, or the identifier is malformed — the response is identical in all three cases, on purpose.
- `409`: message_quarantined (every copy held; with agentSafety and release.panel), recipient_quarantined (the copy of the mailbox in `from` is held), message_blocked, or no_mailbox (with `reason: address_not_ascii`: the mailbox that received has an address outside ASCII — reply from another mailbox of the message)
- `410`: copy_expired — the message was held and its retention expired (policy holds follow the mailbox retention): the copy is gone, it can no longer be released or answered
- `413`: body_too_long (the text above 20000 characters, with `limit`), or payload_too_large (the request body above 25 MB).
- `415`: unsupported_media_type — send the body as `application/json` (form and plain-text bodies are not accepted).
- `422`: no_reply_address (with `reason: address_not_ascii` when the original sender has an address outside ASCII — the platform only sends to ASCII addresses), domain_not_registered, domain_not_allowed_for_credential (the key is limited to selected domains of the account and the mailbox domain is not one of them — adjust it under Credentials or reply with another key), managed_domain_requires_paid_plan (the mailbox is on your instant inbox and the account is on the free plan — there it only receives), auto_submitted, recipient_blocked, recipient_not_allowed, from_not_recipient, policy_refused (a sending policy of the account refuses it; with `policy` and `recipient`)
- `429`: rate_limited (the per-key reply limit, with `Retry-After: 60`; or the per-credential limit), reply_limit (per message), thread_reply_limit (per conversation), quota_exceeded or hold_limit. Read `error` before retrying: reply_limit and thread_reply_limit are the loop guard, not congestion.
- `500`: internal_error — an unexpected failure on our side. The detail stays in our logs, never in the body. Retry reads; retry a write only when it is idempotent or carries an idempotency key.
- `503`: profile_unavailable, injection_failed or record_failed — the sending profile could not be verified, or the reply could not be queued or recorded; retry with the same Idempotency-Key. Or ip_rules_unavailable.

---

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