Sdks
TypeScript SDK
Official TypeScript/JavaScript SDK for daimon.email
Installation
npm install daimon-emailyarn add daimon-emailpnpm add daimon-emailInfo
The TypeScript SDK is automatically generated from our OpenAPI spec using Fern. It includes full TypeScript types and is updated with every API release.
Quick Start
Initialize the Client
import { DaimonClient } from 'daimon-email';
const client = new DaimonClient({
apiKey: process.env.DAIMON_API_KEY // Never hardcode your API key
});Warning
For free tier (no API key yet), omit the apiKey parameter. You'll receive an API key when creating your first inbox.
Create Your First Inbox
// No API key required for first inbox
const response = await client.inboxes.create({
username: 'my-agent',
client_id: 'deployment-prod-1' // Idempotency key
});
const inbox = response.result;
console.log('Email address:', inbox.address);
// my-agent@daimon.email
console.log('API key:', inbox.account_api_key);
// dm_free_abc123...
// Save this API key for future requests
process.env.DAIMON_API_KEY = inbox.account_api_key;List Messages
const messages = await client.inboxes.messages.list(inbox.id);
for (const message of messages.result) {
console.log('From:', message.from);
console.log('Subject:', message.subject);
console.log('Body:', message.body);
console.log('---');
}Send an Email
try {
const sent = await client.inboxes.send(inbox.id, {
to: 'customer@example.com',
subject: 'Welcome to our service',
body: 'Thanks for signing up!',
client_id: 'welcome-email-user-123' // Idempotency
});
console.log('Message sent:', sent.result.id);
} catch (error) {
if (error.code === 'SEND_REQUIRES_PAID') {
// Free tier can't send - guide agent to upgrade
console.log('Upgrade needed:', error.upgrade_context.operator_action_url);
}
}Core Features
Inboxes
const inbox = await client.inboxes.create({
username: 'support-bot',
client_id: 'deploy-123-inbox',
metadata: {
purpose: 'customer support',
deployment: 'production'
}
});const inbox = await client.inboxes.get('inbox_abc123');
console.log(inbox.result.address);const inboxes = await client.inboxes.list({
limit: 50,
offset: 0
});
for (const inbox of inboxes.result) {
console.log(inbox.address);
}const updated = await client.inboxes.update('inbox_abc123', {
metadata: {
status: 'active',
last_checked: new Date().toISOString()
}
});await client.inboxes.delete('inbox_abc123');
// Inbox and all messages deleted immediatelyMessages
const messages = await client.inboxes.messages.list('inbox_abc123', {
limit: 20,
unread_only: true
});const message = await client.inboxes.messages.get('inbox_abc123', 'msg_xyz789');
console.log('From:', message.result.from);
console.log('Subject:', message.result.subject);
console.log('Plain text:', message.result.body);
console.log('HTML:', message.result.body_html);
// Auto-detected CTA links (e.g., "Verify Email")
for (const cta of message.result.cta_links) {
console.log('CTA:', cta.text, '->', cta.url);
}const sent = await client.inboxes.send('inbox_abc123', {
to: 'user@example.com',
subject: 'Password Reset',
body: 'Click here to reset your password: https://app.example.com/reset/token123',
client_id: 'reset-user-456'
});const reply = await client.inboxes.messages.reply('inbox_abc123', 'msg_xyz789', {
body: 'Thanks for your message! We will get back to you soon.',
client_id: 'reply-msg-xyz789'
});
// Automatically includes In-Reply-To and References headersconst forwarded = await client.inboxes.messages.forward('inbox_abc123', 'msg_xyz789', {
to: 'team@example.com',
note: 'Please review this support request.',
client_id: 'forward-msg-xyz789'
});Threads
const threads = await client.threads.list('inbox_abc123', {
limit: 20
});
for (const thread of threads.result) {
console.log('Subject:', thread.subject);
console.log('Message count:', thread.message_count);
console.log('Participants:', thread.participants);
}const thread = await client.threads.get('thread_abc123');
// All messages in the thread, sorted chronologically
for (const message of thread.result.messages) {
console.log(message.from, ':', message.subject);
}// Mark as read, add labels, etc.
await client.threads.update('thread_abc123', {
read: true,
labels: ['support', 'urgent']
});Webhooks
const webhook = await client.webhooks.create({
url: 'https://your-server.com/webhooks/daimon',
events: ['message.received', 'message.sent'],
inbox_id: 'inbox_abc123', // Optional: filter to specific inbox
client_id: 'webhook-prod-messages'
});
console.log('Webhook secret:', webhook.result.secret);
// Save this to verify webhook signaturesconst webhooks = await client.webhooks.list();// Send a test event to verify your endpoint
await client.webhooks.test('webhook_abc123');await client.webhooks.delete('webhook_abc123');Account & Capabilities
const account = await client.account.get();
console.log('Tier:', account.result.tier);
console.log('Email:', account.result.email);const caps = await client.account.capabilities();
console.log('Can send:', caps.result.can.includes('send_messages'));
console.log('Send quota:', caps.result.limits.sends_per_day);
console.log('Used today:', caps.result.usage.sends_today);// Generate magic upgrade link for operator
const link = await client.account.getUpgradeLink({
desired_tier: 'developer',
return_url: 'https://your-app.com/settings'
});
console.log('Upgrade URL:', link.result.url);
// Send this to your operator// Trigger operator notification
await client.account.notifyOperator({
message: 'I need sending capabilities to complete my task.',
action_required: 'upgrade_to_developer',
context: {
reason: 'send_limit_exceeded',
current_tier: 'free'
}
});Advanced Usage
Webhook Signature Verification
Verify incoming webhooks are authentic:
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Express.js example
app.post('/webhooks/daimon', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-daimon-signature'] as string;
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
if (event.event === 'message.received') {
console.log('New message:', event.data.subject);
// Process message
}
res.sendStatus(200);
});Error Handling
import { DaimonError } from 'daimon-email';
try {
await client.inboxes.send(inbox_id, message);
} catch (error) {
if (error instanceof DaimonError) {
// Known API error
console.error('API error:', error.code);
console.error('Message:', error.message);
if (error.upgrade_context) {
// Tier upgrade required
console.log('Upgrade URL:', error.upgrade_context.operator_action_url);
}
if (error.retry_after) {
// Rate limited
console.log('Retry after:', error.retry_after, 'seconds');
}
} else {
// Network error, timeout, etc.
console.error('Unexpected error:', error);
}
}Retry Logic with Exponential Backoff
async function sendWithRetry(
client: DaimonClient,
inbox_id: string,
message: any,
maxRetries: number = 3
) {
let attempt = 0;
while (attempt < maxRetries) {
try {
return await client.inboxes.send(inbox_id, message);
} catch (error) {
if (error instanceof DaimonError && error.status === 429) {
// Rate limited - use provided retry_after
const delay = error.retry_after * 1000;
console.log(`Rate limited. Waiting ${delay}ms...`);
await sleep(delay);
attempt++;
} else {
// Other error - throw
throw error;
}
}
}
throw new Error('Max retries exceeded');
}
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}Polling with Adaptive Intervals
class MessagePoller {
private interval = 5000; // Start at 5 seconds
private readonly minInterval = 5000;
private readonly maxInterval = 60000;
async start(inbox_id: string, onMessage: (msg: any) => void) {
while (true) {
const messages = await client.inboxes.messages.list(inbox_id, {
unread_only: true
});
if (messages.result.length > 0) {
// New messages - reset to fast polling
this.interval = this.minInterval;
for (const msg of messages.result) {
onMessage(msg);
}
} else {
// No messages - slow down
this.interval = Math.min(this.interval * 1.5, this.maxInterval);
}
await sleep(this.interval);
}
}
}
// Usage
const poller = new MessagePoller();
poller.start('inbox_abc123', (message) => {
console.log('New message:', message.subject);
});TypeScript Types
The SDK includes full TypeScript definitions:
import type {
Inbox,
Message,
Thread,
Webhook,
Account,
Capabilities
} from 'daimon-email';
// All response types are fully typed
const inbox: Inbox = await client.inboxes.get('inbox_abc123').then(r => r.result);
const messages: Message[] = await client.inboxes.messages.list('inbox_abc123').then(r => r.result);Framework Examples
Next.js API Route
// pages/api/webhooks/daimon.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { DaimonClient } from 'daimon-email';
const client = new DaimonClient({
apiKey: process.env.DAIMON_API_KEY!
});
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
return res.status(405).end();
}
const event = req.body;
if (event.event === 'message.received') {
const message = event.data;
console.log('Received:', message.subject);
// Auto-reply
await client.inboxes.messages.reply(message.inbox_id, message.id, {
body: 'Thank you for your message. We will respond within 24 hours.',
client_id: `auto-reply-${message.id}`
});
}
res.status(200).json({ success: true });
}Express.js App
import express from 'express';
import { DaimonClient } from 'daimon-email';
const app = express();
const client = new DaimonClient({ apiKey: process.env.DAIMON_API_KEY! });
app.post('/webhooks/daimon', express.json(), async (req, res) => {
const event = req.body;
if (event.event === 'message.received') {
const message = event.data;
// Handle message
}
res.sendStatus(200);
});
app.listen(3000);Resources
- NPM Package: npmjs.com/package/daimon-email
- GitHub Repo: github.com/daimon-email/daimon-ts
- Type Definitions: Included in package (no
@types/*needed) - Generated via: Fern from OpenAPI spec