Send Email
Send an email from your inbox (requires paid tier)
Warning
Paid tier required - Free tier inboxes cannot send emails. When a free tier agent calls this endpoint, it receives a 402 Payment Required response with an upgrade flow that the agent can autonomously handle.
Authentication
Requires the inbox's API key in the Authorization header.
Path Parameters
idstringpathrequiredInbox ID (e.g., inb_abc123)
Request Body
tostring or arraybodyrequiredRecipient email address(es). Can be a single string or array of strings.
ccstring or arraybodyCC recipients (optional)
bccstring or arraybodyBCC recipients (optional)
subjectstringbodyrequiredEmail subject line
bodystringbodyrequiredPlain text body
body_htmlstringbodyHTML body (optional)
in_reply_tostringbodyMessage-ID this email is replying to (sets In-Reply-To header)
referencesarraybodyArray of Message-IDs for threading (sets References header)
thread_idstringbodyThread UUID to attach this message to
client_idstringbodyIdempotency key - prevents duplicate sends on retry
send_atstringbodyISO 8601 datetime - schedule for future delivery (optional)
draft_idstringbodyDraft UUID to send (optional)
Request
curl -X POST "https://api.daimon.email/v1/inboxes/inb_abc123/send" \
-H "Authorization: Bearer dm_live_7d8a9b0c1d2e3f4g5h6i7j8k9l0m1n2o" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"subject": "Hello from my agent",
"body": "This is my first email sent via daimon.email!",
"client_id": "send-001"
}'const client = new DaimonClient({ apiKey: 'dm_live_...' });
try {
const sent = await client.inboxes.send('inb_abc123', {
to: 'user@example.com',
subject: 'Hello from my agent',
body: 'This is my first email sent via daimon.email!',
clientId: 'send-001'
});
console.log(`Sent! Message ID: ${sent.message_id}`);
console.log(`Status: ${sent.status}`);
} catch (error) {
if (error.code === 'SEND_REQUIRES_PAID') {
// Agent autonomously handles upgrade flow
console.log('Agent script:', error.upgradeContext.agentScript);
// Outputs: "Tell your operator: I need sending access. Here's a direct upgrade link: https://..."
// Generate magic upgrade link
const upgradeLink = await client.account.createUpgradeLink();
console.log('Upgrade URL:', upgradeLink.url);
}
}client = DaimonClient(api_key='dm_live_...')
try:
sent = client.inboxes.send('inb_abc123', {
'to': 'user@example.com',
'subject': 'Hello from my agent',
'body': 'This is my first email sent via daimon.email!',
'client_id': 'send-001'
})
print(f"Sent! Message ID: {sent.message_id}")
print(f"Status: {sent.status}")
except DaimonError as e:
if e.code == 'SEND_REQUIRES_PAID':
# Agent autonomously handles upgrade flow
print('Agent script:', e.upgrade_context['agent_script'])
# Outputs: "Tell your operator: I need sending access. Here's a direct upgrade link: https://..."
# Generate magic upgrade link
upgrade_link = client.account.create_upgrade_link()
print('Upgrade URL:', upgrade_link.url)Response
Response
{
"result": {
"id": "msg_sent123",
"status": "sent",
"message_id": "<abc123@daimon.email>",
"sent_at": "2024-03-11T14:23:45Z",
"note": null
},
"next_steps": [
"Message delivered successfully",
"Check /v1/inboxes/{id}/messages for replies",
"Monitor webhooks for message.reply events"
]
}{
"result": {
"id": "msg_sched456",
"status": "scheduled",
"message_id": "<def456@daimon.email>",
"sent_at": null,
"scheduled_at": "2024-03-12T09:00:00Z",
"note": "Scheduled for delivery at 2024-03-12T09:00:00Z"
},
"next_steps": [
"Message scheduled for future delivery",
"Call DELETE /v1/inboxes/{id}/scheduled/{id} to cancel",
"Message will appear in /v1/inboxes/{id}/messages after delivery"
]
}{
"error": "SEND_REQUIRES_PAID",
"message": "Sending emails requires a paid tier subscription",
"upgrade_context": {
"operator_action_url": "https://daimon.email/upgrade?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"operator_action_label": "Add a payment method to enable sending",
"agent_script": "Tell your operator: I need sending access. Here's a direct upgrade link: https://daimon.email/upgrade?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
},
"next_steps": [
"Present the operator_action_url to your human operator",
"Or call POST /v1/upgrade-link to generate a new magic upgrade link",
"After upgrade, retry this request with the same client_id for idempotency"
]
}{
"error": "SEND_LIMIT_EXCEEDED",
"message": "Monthly send limit reached (100/100). Upgrade to increase limit.",
"upgrade_context": {
"operator_action_url": "https://daimon.email/upgrade?token=...",
"operator_action_label": "Upgrade to Pro for 10,000 sends/month",
"agent_script": "Tell your operator: I've hit the monthly send limit. Here's an upgrade link: https://daimon.email/upgrade?token=..."
},
"next_steps": [
"Present the upgrade link to your operator",
"Or wait until the next billing cycle (resets on the 1st of each month)"
]
}Response Fields
result.idstringSent message ID
result.statusstringDelivery status: queued, sent, or scheduled
result.message_idstringRFC 5322 Message-ID header (e.g., <abc123@daimon.email>)
result.sent_atstringISO 8601 timestamp of when the message was sent (null if scheduled)
result.scheduled_atstringISO 8601 timestamp of when the message will be sent (null if sent immediately)
result.notestringAdditional delivery information
next_stepsarrayAgent guidance for next actions
Error Response Fields
errorstringError code: SEND_REQUIRES_PAID, SEND_LIMIT_EXCEEDED, SMTP_REQUIRES_PAID
messagestringHuman-readable error description
upgrade_context.operator_action_urlstringMagic upgrade link - agent presents this to the human operator
upgrade_context.operator_action_labelstringButton text / CTA for the operator
upgrade_context.agent_scriptstringReady-to-use message the agent can send to the operator
Use Cases
Autonomous upgrade handling (free tier agent)
// Agent checks capabilities before sending
const capabilities = await client.account.getCapabilities();
const canSend = capabilities.can.find(c => c.action === 'send_messages');
if (!canSend) {
// Generate magic upgrade link
const upgradeLink = await client.account.createUpgradeLink();
// Agent autonomously messages operator
await notifyOperator({
message: "I need sending access to complete this task.",
action_url: upgradeLink.url,
action_label: "Enable sending (takes 60 seconds)"
});
// Wait for upgrade...
await pollForUpgrade();
// Retry send
const sent = await client.inboxes.send('inb_abc123', {
to: 'user@example.com',
subject: 'Hello',
body: 'Now I can send!',
clientId: 'send-001' // Same client_id ensures idempotency
});
}Schedule future delivery
const sent = await client.inboxes.send('inb_abc123', {
to: 'user@example.com',
subject: 'Scheduled follow-up',
body: 'This will be sent tomorrow at 9am',
sendAt: '2024-03-12T09:00:00Z',
clientId: 'followup-001'
});
console.log(`Scheduled for: ${sent.scheduled_at}`);Send with idempotency
// Agent can safely retry failed requests
const clientId = `send-${Date.now()}`;
try {
const sent = await client.inboxes.send('inb_abc123', {
to: 'user@example.com',
subject: 'Important',
body: 'This will only be sent once',
clientId: clientId
});
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
// Retry with same client_id - will not duplicate
const sent = await client.inboxes.send('inb_abc123', {
to: 'user@example.com',
subject: 'Important',
body: 'This will only be sent once',
clientId: clientId // Same ID = idempotent retry
});
}
}Send HTML email
const sent = await client.inboxes.send('inb_abc123', {
to: 'user@example.com',
subject: 'Rich HTML email',
body: 'This is the plain text version',
bodyHtml: '<h1>Hello!</h1><p>This is the <strong>HTML</strong> version</p>',
clientId: 'html-001'
});