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.
npm install lumail
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"
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.
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.
const { subscriber } = await lumail.subscribers.get("[email protected]");
// or by ID
const { subscriber } = await lumail.subscribers.get("sub_abc123");const { subscriber } = await lumail.subscribers.update("[email protected]", {
name: "Jane Doe",
tags: ["premium"],
replaceTags: true, // Replace all tags instead of appending
});const { subscriber } = await lumail.subscribers.unsubscribe("[email protected]");// 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",
]);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,
});
}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"
});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"
});const { campaign } = await lumail.campaigns.get("campaign_id");
// Includes sender info, recipient filters, and full contentOnly DRAFT campaigns can be updated.
await lumail.campaigns.update("campaign_id", {
subject: "Updated Subject Line",
preview: "New preview text",
});Only DRAFT campaigns can be deleted.
await lumail.campaigns.delete("campaign_id");// 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",
});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.
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.
// 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" });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
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.
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");
}
}| Method | Retries | When |
|---|---|---|
| GET, PUT, DELETE | Up to 3 | Network errors, 429 rate limits |
| POST, PATCH | No retries | Prevents duplicate operations |
Retry delays follow exponential backoff: 1s, 2s, 4s. The SDK respects Retry-After headers from rate limit responses.