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": "[email protected]",
"from_name": "OneShot Team",
"to_address": "[email protected]",
"subject": "Hello from OneShot",
"body": "This is a test email"
}'
# 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": "[email protected]",
"subject": "Hello",
"body": "..."
}'
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: "[email protected]",
from_name: "OneShot Team",
to_address: "[email protected]",
subject: "Hello from OneShot",
body: "This is a test email",
}),
},
);
{
"request_id": "req_xyz789",
"status": "processing",
"message": "Email job queued successfully"
}
Email
Send Email
Send an email with automatic domain provisioning
POST
/
v1
/
tools
/
email
/
send
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": "[email protected]",
"from_name": "OneShot Team",
"to_address": "[email protected]",
"subject": "Hello from OneShot",
"body": "This is a test email"
}'
# 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": "[email protected]",
"subject": "Hello",
"body": "..."
}'
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: "[email protected]",
from_name: "OneShot Team",
to_address: "[email protected]",
subject: "Hello from OneShot",
body: "This is a test email",
}),
},
);
{
"request_id": "req_xyz789",
"status": "processing",
"message": "Email job queued successfully"
}
Authentication
Requires x402 payment proof in theX-Payment-Proof header.
Headers
string
required
Quote ID from the
/email/quote endpoint. Must match an unused quote
owned by the same wallet.string
required
x402 EIP-712 signature authorising the quoted
total_cost.Request Body
string
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).string
Display name for the sender. Rendered as
From: From Name <from_address> —
e.g. Jane Doe <[email protected]>. Max 100 characters; may not contain
newlines or angle brackets.string
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-timemailbox_provisioning_feeto the quote.
/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.Because an already-provisioned domain keeps its own mode, never
passing
mailbox_mode does not mean your sends are relay sends. (A domain
you own but which is still unprovisioned is the exception — it does still
honor the parameter, and asking for mailbox there opts it in and adds the
fee.) A domain created
in mailbox mode bills the one-time mailbox_provisioning_fee for every
address that isn’t already active on it — the canonical agent@ included
— no matter what this parameter says. Check mailbox_mode per domain on
/email/domains to see which of yours
charge, and read mailbox_provisioning_fee on the quote for the amount.string | string[]
required
Recipient email address(es)
string
required
Email subject line
string
required
Email body content (plain text)
array
Array of attachment objects with
filename, content_type, and content
(base64)Response
string
Job ID for tracking the email send status
string
Job status:
pending, processing, completed, or failedstring
Status message
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 afrom_addresswhose 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 itsdaily_send_limittoday.
daily_sent_count, but
they are not blocked by the limit or warmup score (only rotation enforces
those). See List Domain Pool.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": "[email protected]",
"from_name": "OneShot Team",
"to_address": "[email protected]",
"subject": "Hello from OneShot",
"body": "This is a test email"
}'
# 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": "[email protected]",
"subject": "Hello",
"body": "..."
}'
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: "[email protected]",
from_name: "OneShot Team",
to_address: "[email protected]",
subject: "Hello from OneShot",
body: "This is a test email",
}),
},
);
{
"request_id": "req_xyz789",
"status": "processing",
"message": "Email job queued successfully"
}
Bulk Sending
To send the same email content to multiple recipients, pass an array of email addresses in theto_address field. Each recipient will receive an individual email (not CC/BCC).
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": "[email protected]",
"to_address": ["[email protected]", "[email protected]"],
"subject": "Hello Team",
"body": "Weekly update..."
}'
Domain Provisioning
If the domain is new, OneShot will automatically:- Register the domain
- Configure DNS records
- Set up email sending infrastructure
- Verify domain ownership
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