Lumail
Product
Agents
PricingDocsChangelog
Log inSign up
IntroductionTutorialsAI IntegrationSDKAPI ReferenceIntegrationsFeaturesDeliverabilityWorkflows

Privacy & GDPR

GDPR in LumailData Processing Agreement (DPA) Status

Ship with Email

Choose an Email MCP for Claude Code, Then Ship the LaunchSend a Newsletter from Cursor, Then Ship the LaunchMove a Launch List from Kit Without Paying for Dormant Subscribers

Tutorials

Create an API TokenSend Transactional Email with SMTPSend React Email Templates with LumailBuild a V0 Capture PageDynamic Promo Codes with Webhooks

AI Integration

Claude Code (Plugin)Codex (Plugin)ChatGPT PluginHermesSkills (CLI)OpenClawCursor (MCP)TypeScript SDK IntegrationsTools API (v2)Examples & Recipes

SDK

SDK IntroductionSDK ConfigurationSDK ResourcesSDK ToolsErrors and Retries

API Reference

API TokensTypeScript SDKCLIMCP ServerTools API (v2)Admin Tools APIRate LimitsSMTP EndpointPOSTSend Transactional EmailPOSTSend Email in HTMLPOSTSend Email in MarkdownPOSTSend Email in TiptapPOSTEmail Verification APIPOSTCreate SubscriberGETGet SubscriberPATCHUpdate SubscriberPOSTUnsubscribe SubscriberPOSTAdd Tags to SubscriberDELETERemove Tags from SubscriberPOSTTrack EventGETGet Subscriber EventsGETGet All TagsPOSTCreate a TagGETList All CampaignsPOSTCreate CampaignGETGet CampaignPATCHUpdate CampaignDELETEDelete CampaignPOSTSend CampaignGETList SubscribersGETGet Tag by ID or Name

Integrations

ClickFunnels IntegrationSystemIO Integration

Features

Affiliate ProgramAccount VerificationLanguageVariablesTag Action LinksSurveysContent Deliverability CheckerSender HealthCampaign Delivery ScoreEmail Engagement ScoreSubscriber EventsRevenue TrackingEmail Sending Queue

Deliverability

How to strengthen DMARCHow to add DMARCHow to fix DKIMHow to verify a sending domainHow to fix MAIL FROMHow to fix BIMIHow to lower bounce rateHow to lower complaint rateHow to fix delivery delaysHow to fix blocklist bouncesHow to fix sending infrastructureHow to fix a dedicated IPHow to fix a failed sendHow to fix a fallback-domain sendHow to fix an invalid email parameterHow to fix sending rate or daily quotaHow to fix a monthly email limitHow to fix a past-due payment pauseHow to fix an admin sending pauseHow to complete additional verificationHow to fix a failed campaignHow to fix a campaign render error

Workflows

WorkflowWorkflow Getting StartedWorkflow TriggersWorkflow Manual EnrollmentWorkflow BranchingWorkflow A/B TestingWorkflow GoalsWorkflow Exit RulesWorkflow Publishing and VersionsWorkflow Test Runs and ResultsWait StepEmail StepAction StepWebhook StepWorkflow Groups

domains

Email DomainsWeb Domains

TypeScript SDK

Official TypeScript SDK for the Lumail API - type-safe client with full autocompletion for subscribers, campaigns, emails, tags, events, and tools.

The Lumail TypeScript SDK provides a type-safe client for interacting with the Lumail API. It covers all V1 REST endpoints and V2 tools, with built-in error handling, retries, and full TypeScript autocompletion.

For a task-focused guide with separate pages for setup, resources, tools, and error handling, start with the SDK Introduction.

Installation

npm install lumail

Quick Start

import { Lumail } from "lumail";

const lumail = new Lumail({ apiKey: "lum_your_api_token_here" });

// Create a subscriber
const { subscriber } = await lumail.subscribers.create({
  email: "[email protected]",
  name: "John Doe"




PreviousAPI TokensNextCLI

On This Page

InstallationQuick StartConfigurationSubscribersCreate or update a subscriberGet a subscriberUpdate a subscriberUnsubscribeManage tagsList eventsCampaignsList campaignsCreate a campaignGet campaign detailsUpdate a campaignDelete a campaignSend or scheduleEmails (Transactional)Send an emailVerify an emailTagsEventsTools (V2 API)Error HandlingRetry BehaviorRelated Documentation
,
tags: ["newsletter"],
});
// Send a campaign
await lumail.campaigns.send("campaign_id");

Configuration

const lumail = new Lumail({
  apiKey: "lum_...", // Required - your API token
  baseUrl: "https://lumail.io/api", // Optional - defaults to production
});

Get your API token from Settings > API Tokens in your Lumail dashboard, or follow the API Tokens guide.

Subscribers

Create or update a subscriber

const { subscriber } = await lumail.subscribers.create({
  email: "[email protected]",
  name: "John Doe",
  phone: "+1234567890",
  tags: ["vip", "newsletter"],
  fields: { company: "Acme", role: "CEO" },
  resubscribe: true, // Re-subscribe if previously unsubscribed
  triggerWorkflows: true, // Trigger matching workflows
  country: "US", // 2-letter country code
});

If the email already exists, it updates the existing subscriber.

Get a subscriber

const { subscriber } = await lumail.subscribers.get("[email protected]");
// or by ID
const { subscriber } = await lumail.subscribers.get("sub_abc123");

Update a subscriber

const { subscriber } = await lumail.subscribers.update("[email protected]", {
  name: "Jane Doe",
  tags: ["premium"],
  replaceTags: true, // Replace all tags instead of appending
});

Unsubscribe

const { subscriber } = await lumail.subscribers.unsubscribe("[email protected]");

Manage tags

// Add tags (creates tags if they don't exist)
const { added, tags } = await lumail.subscribers.addTags("[email protected]", [
  "premium",
  "beta-tester",
]);

// Remove tags
const { removed } = await lumail.subscribers.removeTags("[email protected]", [
  "old-tag",
]);

List events

const { events, nextCursor } = await lumail.subscribers.listEvents(
  "[email protected]",
  {
    take: 50,
    order: "desc",
    eventTypes: ["EMAIL_OPENED", "EMAIL_CLICKED"],
    startDate: "2025-01-01T00:00:00Z",
  },
);

// Cursor-based pagination
if (nextCursor) {
  const next = await lumail.subscribers.listEvents("[email protected]", {
    cursor: nextCursor,
    take: 50,
  });
}

Campaigns

List campaigns

const { campaigns, total, pageCount } = await lumail.campaigns.list({
  status: "DRAFT", // "all" | "DRAFT" | "ARCHIVED" | "SCHEDULED" | "SENT"
  page: 1,
  limit: 20,
  query: "welcome", // Search by name or subject
  sortBy: "name", // "name" | "name_desc"
});

Create a campaign

const { campaign, campaignId } = await lumail.campaigns.create({
  subject: "Welcome to our newsletter!",
  name: "Welcome Campaign",
  preview: "You're in. Here's what to expect.",
  contentType: "MARKDOWN", // "MAILY" | "PLATE" | "MARKDOWN"
});

Get campaign details

const { campaign } = await lumail.campaigns.get("campaign_id");
// Includes sender info, recipient filters, and full content

Update a campaign

Only DRAFT campaigns can be updated.

await lumail.campaigns.update("campaign_id", {
  subject: "Updated Subject Line",
  preview: "New preview text",
});

Delete a campaign

Only DRAFT campaigns can be deleted.

await lumail.campaigns.delete("campaign_id");

Send or schedule

// Send immediately
await lumail.campaigns.send("campaign_id");

// Schedule for later
await lumail.campaigns.send("campaign_id", {
  scheduledAt: "2025-12-25T10:00:00Z",
  timezone: "Europe/Paris",
});

Emails (Transactional)

Send an email

const { id } = await lumail.emails.send({
  to: "[email protected]",
  from: "[email protected]",
  subject: "Order Confirmation",
  content: "Your order **#1234** has been confirmed.",
  contentType: "MARKDOWN", // "MARKDOWN" | "HTML" | "TIPTAP"
  replyTo: "[email protected]",
  transactional: true,
  tracking: {
    links: true,
    open: true,
  },
});

The from address must belong to a verified domain in your organization.

Verify an email

const result = await lumail.emails.verify({ email: "[email protected]" });

if (result.success) {
  console.log(result.warnings);
} else {
  console.log(result.code, result.error, result.suggestion);
}

Returns a discriminated union:

type VerifyEmailResponse =
  | { success: true; warnings?: string[] }
  | {
      success: false;
      error: string;
      code:
        | "invalid_format"
        | "disposable_email"
        | "spam_domain"
        | "invalid_domain"
        | "test_email"
        | "internal_error";
      suggestion?: string;
      warnings?: string[];
    };

When success is false, code identifies the reason and suggestion may contain a corrected address for common typos. Disposable providers such as passmail.net and yopmail.com return success: false with code: "disposable_email". Results are cached for 30 days.

Tags

// List all tags
const { tags } = await lumail.tags.list();

// Create a tag
const { tag } = await lumail.tags.create({ name: "premium" });

// Get tag details (by name or ID)
const { tag } = await lumail.tags.get("premium");
// tag.subscribersCount shows how many subscribers have this tag

// Rename a tag
await lumail.tags.update("premium", { name: "gold" });

Events

Track custom subscriber events:

await lumail.events.create({
  eventType: "SUBSCRIBER_PAYMENT",
  subscriber: "[email protected]", // Email or subscriber ID
  data: {
    amount: 99,
    plan: "pro",
    currency: "USD",
  },
});

Available event types: SUBSCRIBED, UNSUBSCRIBED, TAG_ADDED, TAG_REMOVED, EMAIL_OPENED, EMAIL_CLICKED, EMAIL_SENT, EMAIL_RECEIVED, WORKFLOW_STARTED, WORKFLOW_COMPLETED, WORKFLOW_CANCELED, FIELD_UPDATED, EMAIL_BOUNCED, EMAIL_COMPLAINED, WEBHOOK_EXECUTED, SUBSCRIBER_PAYMENT, SUBSCRIBER_REFUND

Tools (V2 API)

Access all 59+ Lumail tools programmatically:

// List available tools
const { tools, grouped } = await lumail.tools.list();

// Get a tool's schema
const { tool } = await lumail.tools.get("list_subscribers");

// Run a tool with typed response
const result = await lumail.tools.run<{ subscribers: unknown[] }>(
  "list_subscribers",
  { limit: 10, status: "SUBSCRIBED" },
);

See Tools API (v2) for the full list of available tools.

Error Handling

The SDK throws typed errors for different HTTP status codes:

import {
  Lumail,
  LumailAuthenticationError, // 401 - invalid API key
  LumailPaymentRequiredError, // 402 - plan limit reached
  LumailValidationError, // 400 - invalid request
  LumailNotFoundError, // 404 - resource not found
  LumailRateLimitError, // 429 - rate limited
  LumailError, // Other errors
} from "lumail";

try {
  await lumail.subscribers.get("[email protected]");
} catch (error) {
  if (error instanceof LumailNotFoundError) {
    console.log("Subscriber not found");
  } else if (error instanceof LumailRateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}ms`);
  } else if (error instanceof LumailAuthenticationError) {
    console.log("Check your API key");
  }
}

Retry Behavior

MethodRetriesWhen
GET, PUT, DELETEUp to 3Network errors, 429 rate limits
POST, PATCHNo retriesPrevents duplicate operations

Retry delays follow exponential backoff: 1s, 2s, 4s. The SDK respects Retry-After headers from rate limit responses.

Related Documentation

  • API Tokens - Generate your API key
  • SDK Introduction - Task-focused SDK guide
  • CLI - Command-line interface
  • MCP Server - AI agent integration
  • API Limits - Rate limits and quotas