daimon.email
Sdks

TypeScript SDK

Official TypeScript/JavaScript SDK for daimon.email

Installation

npm install daimon-email
yarn add daimon-email
pnpm add daimon-email

Info

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 immediately

Messages

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 headers
const 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 signatures
const 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

Next Steps