> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oneshotagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Send Email

> Send an email with automatic domain provisioning

## Authentication

Requires x402 payment proof in the `X-Payment-Proof` header.

## Headers

<ParamField header="X-Quote-ID" type="string" required>
  Quote ID from the `/email/quote` endpoint. Must match an unused quote
  owned by the same wallet.
</ParamField>

<ParamField header="X-Payment-Proof" type="string" required>
  x402 EIP-712 signature authorising the quoted `total_cost`.
</ParamField>

## Request Body

<ParamField body="from_address" type="string" optional>
  Sender email address.

  **Required** if the matching quote did NOT come from rotation (you
  passed `from_address` when calling `/email/quote`).

  **Optional** if rotation picked the address at quote time — the server
  replays the `locked_from_address` from the quote. You can pass the
  same value as a sanity check, but a **different** value returns
  `400 from_address_mismatch` (the agent paid for slot A and can't send
  from slot B).
</ParamField>

<ParamField body="from_name" type="string" optional>
  Display name for the sender. Rendered as `From: From Name <from_address>` —
  e.g. `Jane Doe <jane@yourdomain.com>`. Max 100 characters; may not contain
  newlines or angle brackets.
</ParamField>

<ParamField body="mailbox_mode" type="string" optional>
  How the domain sends when first set up: `relay` (default) or `mailbox`.

  * `relay` — header-only send: any from-address works with nothing provisioned
    per address (no mailbox fee).
  * `mailbox` — a real dedicated mailbox per address (better deliverability +
    per-address warmup). Each new from-address provisions a mailbox and adds a
    one-time `mailbox_provisioning_fee` to the quote.

  **Set this on [`/email/quote`](/api-reference/email/quote), not here.** The
  quote prices the fee and locks the mode; on `/email/send` the mode is replayed
  from the paid quote, so `mailbox_mode` should be **omitted**. Re-passing
  `mailbox` here is redundant and returns `400 mailbox_mode_conflict` if the
  domain is a live relay domain. Only applies while a domain is unprovisioned —
  use it on a new `from_domain`; an already-provisioned domain keeps the mode it
  was created with.
</ParamField>

<ParamField body="to_address" type="string | string[]" required>
  Recipient email address(es)
</ParamField>

<ParamField body="subject" type="string" required>
  Email subject line
</ParamField>

<ParamField body="body" type="string" required>
  Email body content (plain text)
</ParamField>

<ParamField body="attachments" type="array" optional>
  Array of attachment objects with `filename`, `content_type`, and `content`
  (base64)
</ParamField>

<Snippet file="audit-context-params.mdx" />

## Response

<ResponseField name="request_id" type="string">
  Job ID for tracking the email send status
</ResponseField>

<ResponseField name="status" type="string">
  Job status: `pending`, `processing`, `completed`, or `failed`
</ResponseField>

<ResponseField name="message" type="string">
  Status message
</ResponseField>

<ResponseField name="warning" type="string">
  Non-blocking deliverability advisory — the send still proceeds. Branch on
  this to defer rather than treating it as an error:

  * `pinned_domain_warming` — you pinned a `from_address` whose domain is still
    warming (low warmup score). Pinning bypasses the warmup gate, so this is the
    only signal you'll get.
  * `pinned_over_limit` — your pinned domain is over its `daily_send_limit` today.

  Pinned sends are still counted against the domain's `daily_sent_count`, but
  they are **not blocked** by the limit or warmup score (only rotation enforces
  those). See [List Domain Pool](/api-reference/email/domains).
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://win.oneshotagent.com/v1/tools/email/send \
    -H "Content-Type: application/json" \
    -H "X-Quote-ID: quote_abc123" \
    -H "X-Payment-Proof: <your_x402_signature>" \
    -d '{
      "from_address": "hello@yourdomain.com",
      "from_name": "OneShot Team",
      "to_address": "recipient@example.com",
      "subject": "Hello from OneShot",
      "body": "This is a test email"
    }'
  ```

  ```bash cURL — rotation, no from_address theme={null}
  # Quote was created without from_address → server-rotated, locked the pick.
  # Send replays the locked address automatically.
  curl -X POST https://win.oneshotagent.com/v1/tools/email/send \
    -H "Content-Type: application/json" \
    -H "X-Quote-ID: quote_abc123" \
    -H "X-Payment-Proof: <your_x402_signature>" \
    -d '{
      "to_address": "recipient@example.com",
      "subject": "Hello",
      "body": "..."
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    "https://win.oneshotagent.com/v1/tools/email/send",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Quote-ID": "quote_abc123",
        "X-Payment-Proof": paymentProof,
      },
      body: JSON.stringify({
        from_address: "hello@yourdomain.com",
        from_name: "OneShot Team",
        to_address: "recipient@example.com",
        subject: "Hello from OneShot",
        body: "This is a test email",
      }),
    },
  );
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "request_id": "req_xyz789",
    "status": "processing",
    "message": "Email job queued successfully"
  }
  ```
</ResponseExample>

## Bulk Sending

To send the same email content to multiple recipients, pass an array of email addresses in the `to_address` field. Each recipient will receive an individual email (not CC/BCC).

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://win.oneshotagent.com/v1/tools/email/send \
    -H "Content-Type: application/json" \
    -H "X-Quote-ID: quote_bulk_123" \
    -H "X-Payment-Proof: <your_x402_signature>" \
    -d '{
      "from_address": "hello@yourdomain.com",
      "to_address": ["alice@example.com", "bob@example.com"],
      "subject": "Hello Team",
      "body": "Weekly update..."
    }'
  ```
</RequestExample>

## Domain Provisioning

If the domain is new, OneShot will automatically:

1. Register the domain
2. Configure DNS records
3. Set up email sending infrastructure
4. Verify domain ownership

This process typically takes 2-5 minutes. The email will be sent once provisioning completes.

## Errors

| Code                                | When                                                                                                                                                                                                                                                          |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400 from_address_mismatch`         | `from_address` in the body doesn't match the address locked into the quote at rotation time. Omit it or pass the locked value.                                                                                                                                |
| `400 missing_from_address`          | The quote has no `locked_from_address` (you passed an explicit `from_address` at quote time) and you didn't pass one on send.                                                                                                                                 |
| `400 quote_already_used`            | The quote was already consumed by a prior send.                                                                                                                                                                                                               |
| `402 mailbox_provisioning_required` | `from_address` is a new address on a domain that provisions a mailbox per address, but the quote you paid didn't include the one-time `mailbox_provisioning_fee`. Re-quote with this exact `from_address` (the fee is itemized on the quote), pay, and retry. |
| `403 domain_not_owned`              | The domain in `from_address` belongs to a different agent.                                                                                                                                                                                                    |

## Notes

* Quote must be used within 1 hour
* Domain provisioning is automatic - no additional steps required
* Job status can be polled using the `request_id`
* When rotation picks for you, the from\_address is locked into the quote
  — see [`/email/quote`](/api-reference/email/quote#domain-rotation)
