Back to Developer

Email

Send transactional emails, manage templates, track delivery, and handle inbound messages.

Sending Email

Send transactional emails using a template or raw HTML. Every email is tracked for delivery, opens, and clicks automatically.

// Send an email using a template
curl -X POST https://vm.whaletools.cloud/v1/stores/{store_id}/email/send \
  -H "x-api-key: wk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "jane@example.com",
    "template_id": "tmpl_order_confirmation",
    "variables": {
      "customer": { "name": "Jane Smith" },
      "order": {
        "number": "#10042",
        "total": "$47.50",
        "items": [
          { "name": "Running Shoes", "quantity": 1, "price": "$39.99" },
          { "name": "Sport Socks", "quantity": 2, "price": "$3.75" }
        ]
      }
    },
    "tags": ["order-confirmation", "transactional"]
  }'

// Response
{
  "object": "email_message",
  "id": "msg_550e8400-e29b-41d4-a716-446655440000",
  "to": "jane@example.com",
  "subject": "Order #10042 Confirmed",
  "status": "queued",
  "template_id": "tmpl_order_confirmation",
  "created_at": "2026-03-10T12:00:00.000Z"
}

// Send a raw HTML email
curl -X POST https://vm.whaletools.cloud/v1/stores/{store_id}/email/send \
  -H "x-api-key: wk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "jane@example.com",
    "from": "hello@your-store.com",
    "subject": "Your receipt from My Store",
    "html": "<h1>Thanks for your purchase!</h1><p>Order #10042 has been confirmed.</p>",
    "text": "Thanks for your purchase! Order #10042 has been confirmed.",
    "reply_to": "support@your-store.com"
  }'

Email Templates

Templates use Handlebars syntax for dynamic content. Create reusable templates for order confirmations, shipping updates, password resets, and marketing campaigns.

VariableDescription
{{customer.name}}Customer full name.
{{customer.email}}Customer email address.
{{order.number}}Order number (e.g., #10042).
{{order.total}}Formatted order total with currency.
{{order.items}}Array of line items (iterable).
{{store.name}}Your store name.
{{store.url}}Your storefront URL.
{{unsubscribe_url}}One-click unsubscribe link (required).
// Create a template
curl -X POST https://vm.whaletools.cloud/v1/stores/{store_id}/email/templates \
  -H "x-api-key: wk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order Confirmation",
    "slug": "order_confirmation",
    "subject": "Order {{order.number}} Confirmed",
    "html": "<!DOCTYPE html><html><body style=\"font-family: sans-serif;\"><h1>Thanks, {{customer.name}}!</h1><p>Your order {{order.number}} for {{order.total}} has been confirmed.</p>{{#each order.items}}<p>{{this.name}} x{{this.quantity}} — {{this.price}}</p>{{/each}}<hr><p style=\"font-size: 12px; color: #888;\"><a href=\"{{unsubscribe_url}}\">Unsubscribe</a></p></body></html>",
    "text": "Thanks, {{customer.name}}! Your order {{order.number}} for {{order.total}} has been confirmed."
  }'

// Response
{
  "object": "email_template",
  "id": "tmpl_order_confirmation",
  "name": "Order Confirmation",
  "slug": "order_confirmation",
  "version": 1,
  "created_at": "2026-03-10T12:00:00.000Z"
}

// List templates
curl https://vm.whaletools.cloud/v1/stores/{store_id}/email/templates \
  -H "x-api-key: wk_live_..."

// Preview a template with sample data
curl -X POST https://vm.whaletools.cloud/v1/stores/{store_id}/email/templates/{template_id}/preview \
  -H "x-api-key: wk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "variables": {
      "customer": { "name": "Jane Smith" },
      "order": { "number": "#10042", "total": "$47.50", "items": [] }
    }
  }'

Domain Setup and Verification

Send emails from your own domain for better deliverability and brand consistency. Add the required DNS records and verify via the API.

// Add a sending domain
curl -X POST https://vm.whaletools.cloud/v1/stores/{store_id}/email/domains \
  -H "x-api-key: wk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "domain": "your-store.com" }'

// Response — includes the DNS records to add
{
  "object": "email_domain",
  "id": "dom_550e8400",
  "domain": "your-store.com",
  "status": "pending",
  "dns_records": [...]
}

Add the following DNS records to your domain:

TypeNameValuePurpose
TXT@v=spf1 include:_spf.whaletools.dev ~allSPF — authorizes WhaleTools to send on your behalf.
CNAMEwt._domainkeywt.dkim.whaletools.devDKIM — cryptographic email signing.
CNAME_dmarcdmarc.whaletools.devDMARC — email authentication policy.
CNAMEtrackingtrack.whaletools.devClick and open tracking via your domain.
// Verify domain after adding DNS records
curl -X POST https://vm.whaletools.cloud/v1/stores/{store_id}/email/domains/{domain_id}/verify \
  -H "x-api-key: wk_live_..."

// Response
{
  "object": "email_domain",
  "id": "dom_550e8400",
  "domain": "your-store.com",
  "status": "verified",
  "spf": "valid",
  "dkim": "valid",
  "dmarc": "valid",
  "verified_at": "2026-03-10T12:05:00.000Z"
}

Email Tracking

Every email is automatically tracked for delivery, opens, clicks, bounces, and complaints. Query events per message or in aggregate.

EventDescription
deliveredEmail was accepted by the recipient mail server.
openedRecipient opened the email (pixel tracking).
clickedRecipient clicked a link in the email.
bouncedEmail could not be delivered (hard or soft bounce).
complainedRecipient marked the email as spam.
unsubscribedRecipient clicked the unsubscribe link.
// Get events for a specific message
curl https://vm.whaletools.cloud/v1/stores/{store_id}/email/messages/{message_id}/events \
  -H "x-api-key: wk_live_..."

// Response
{
  "object": "list",
  "data": [
    {
      "event": "delivered",
      "timestamp": "2026-03-10T12:00:02.000Z",
      "details": { "smtp_response": "250 OK" }
    },
    {
      "event": "opened",
      "timestamp": "2026-03-10T12:05:30.000Z",
      "details": { "user_agent": "Apple Mail", "ip": "203.0.113.42" }
    },
    {
      "event": "clicked",
      "timestamp": "2026-03-10T12:06:15.000Z",
      "details": { "url": "https://your-store.com/orders/10042" }
    }
  ]
}

// Aggregate stats for a date range
curl https://vm.whaletools.cloud/v1/stores/{store_id}/email/stats \
  -H "x-api-key: wk_live_..." \
  -G -d "date_from=2026-03-01" -d "date_to=2026-03-10"

// Response
{
  "date_range": { "from": "2026-03-01", "to": "2026-03-10" },
  "sent": 4250,
  "delivered": 4180,
  "opened": 1840,
  "clicked": 620,
  "bounced": 45,
  "complained": 3,
  "unsubscribed": 12,
  "open_rate": 44.0,
  "click_rate": 14.8,
  "bounce_rate": 1.1
}

Inbox and Threads

WhaleTools provides a shared inbox for receiving and replying to customer emails. Messages are organized into threads and linked to customer profiles automatically.

// List inbox threads
curl https://vm.whaletools.cloud/v1/stores/{store_id}/email/threads \
  -H "x-api-key: wk_live_..." \
  -G -d "status=open" -d "limit=20"

// Response
{
  "object": "list",
  "data": [
    {
      "id": "thread_a1b2c3d4",
      "subject": "Re: Order #10042 — Where is my package?",
      "customer_id": "cust_550e8400",
      "customer_email": "jane@example.com",
      "status": "open",
      "message_count": 3,
      "last_message_at": "2026-03-10T14:30:00.000Z",
      "assigned_to": null
    }
  ],
  "has_more": true
}

// Reply to a thread
curl -X POST https://vm.whaletools.cloud/v1/stores/{store_id}/email/threads/{thread_id}/reply \
  -H "x-api-key: wk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "html": "<p>Hi Jane, your order shipped yesterday via USPS. Tracking: 9400111899223456789012. It should arrive by Wednesday.</p>",
    "text": "Hi Jane, your order shipped yesterday via USPS. Tracking: 9400111899223456789012. It should arrive by Wednesday."
  }'

Best Practices

  • Verify your sending domain. Authenticated domains (SPF + DKIM + DMARC) have significantly better inbox placement rates.
  • Always include a text version. Some email clients and spam filters prefer plain text. The text field is required for all templates.
  • Include an unsubscribe link. Use the {{unsubscribe_url}} variable in every marketing email. This is required by law (CAN-SPAM, GDPR).
  • Monitor bounce rates. Hard bounces above 2% can damage your sender reputation. Remove invalid addresses promptly.
  • Use tags for filtering. Add tags like transactional or marketing to categorize emails and filter analytics.
  • Preview before sending. Use the template preview endpoint to render with sample data and verify formatting before going live.