lemn

SKILL.md

Lemn Skill

Use this skill when the user wants to send emails, manage contact lists, run broadcast campaigns, or configure webhooks using the Lemn email marketing platform via the lemn-api npm package.


Setup

npm install lemn-api
const LemnAPI = require('lemn-api');
const lemn = new LemnAPI('your_api_key');

How Authentication Works (Important)

The package handles auth internally. It sends your API key as the header X-Auth-APIKey on every request. You never set headers manually — just pass the key to the constructor and use the methods.

The base URL used internally is https://app.xn--lemn-sqa.com/api. You do not need to use this directly, but it is good to know for debugging.


Module Overview

Namespace What it does
lemn.lists Contact lists — create, manage contacts, tags, domain stats
lemn.broadcasts Email campaigns — create, send, test, stats
lemn.transactional Send one-off transactional emails
lemn.supplists Suppression lists — block specific contacts
lemn.exclusion Exclusion lists — global send exclusions
lemn.webhooks Create and manage event webhooks
lemn.exports Retrieve exported files

Lists (lemn.lists)

Create a list

const list = await lemn.lists.create('Newsletter Subscribers');
// list.id is used in all subsequent list operations

Get all lists

const lists = await lemn.lists.getAll();

Get a specific list

const list = await lemn.lists.get(listId);

Update a list

await lemn.lists.update(listId, { name: 'New Name' });

Delete a list

await lemn.lists.delete(listId);

Export a list

await lemn.lists.export(listId);

Add a single contact to a list

const contact = {
  email: 'john@example.com',
  first_name: 'John',
  last_name: 'Doe'
};
await lemn.lists.addSingleContact(listId, contact);

Add contacts via CSV

// csvData must be a CSV string with headers
const csvData = 'email,first_name,last_name\njohn@example.com,John,Doe';
await lemn.lists.addData(listId, csvData);
// Internally sends Content-Type: text/csv

Delete contacts from a list

// Pass the emails array directly (not wrapped in an object)
await lemn.lists.deleteContacts(listId, ['john@example.com', 'jane@example.com']);

Add emails to unsubscribe list

// Pass emails array directly — sent as text/plain internally
await lemn.lists.addUnsubscribes(listId, ['john@example.com', 'jane@example.com']);

Get domain statistics for a list

const stats = await lemn.lists.getDomainStats(listId);

Delete specific domains from a list

// Pass domains array directly (not wrapped in an object)
await lemn.lists.deleteDomains(listId, ['gmail.com', 'yahoo.com']);

Get contact data by email

const contact = await lemn.lists.getContactData('john@example.com');

Delete contact data by email

await lemn.lists.deleteContactData('john@example.com');

Get all tags

const tags = await lemn.lists.getAllTags();

Broadcasts (lemn.broadcasts)

Get available postal routes (needed before creating broadcasts)

const routes = await lemn.broadcasts.getUserRoutes();
// Use a route id from this response when creating broadcasts

Create a broadcast

const broadcast = await lemn.broadcasts.create({
  name: 'My Campaign',
  subject: 'Hello from Lemn',
  fromname: 'Your Company',
  fromemail: 'hello@yourcompany.com',
  body: '<h1>Hello!</h1><p>Your email content here.</p>',
  listid: listId,       // contact list to send to
  route: routeId        // from getUserRoutes()
});
// broadcast.id is used in all subsequent broadcast operations

Get all broadcasts

// No filters
const broadcasts = await lemn.broadcasts.getAll();

// With optional filters
const filtered = await lemn.broadcasts.getAll({
  older: 'cursor-value',   // for pagination
  search: 'keyword'
});

Get a specific broadcast

const broadcast = await lemn.broadcasts.get(broadcastId);

Update a draft broadcast

await lemn.broadcasts.updateDraft(broadcastId, {
  subject: 'Updated Subject',
  body: '<p>Updated content</p>'
});
// Only works on drafts — cannot update already-sent broadcasts this way

Send a test email

await lemn.broadcasts.sendTest(broadcastId, {
  to: 'test@example.com'
});

Start sending a broadcast

await lemn.broadcasts.start(broadcastId);
// Immediately begins sending to the list

Cancel a broadcast

await lemn.broadcasts.cancel(broadcastId);

Duplicate a broadcast

const copy = await lemn.broadcasts.duplicate(broadcastId);

Update a sent broadcast

await lemn.broadcasts.updateSent(broadcastId, { name: 'New Name' });

Export broadcast data

await lemn.broadcasts.export(broadcastId);

Get domain statistics

const stats = await lemn.broadcasts.getDomainStats(broadcastId);

Get client statistics (devices, browsers, locations)

const clientStats = await lemn.broadcasts.getClientStats(broadcastId);

Get bounce messages

const bounces = await lemn.broadcasts.getBounceMessages(broadcastId, 'gmail.com', 'hard');
// type can be 'hard' or 'soft'

Upload a file (for use in broadcast content)

const fs = require('fs');
const fileStream = fs.createReadStream('./image.jpg');
const result = await lemn.broadcasts.uploadFile(fileStream);
// Pass a ReadStream, not a file path string

Transactional Emails (lemn.transactional)

Use this for one-off emails triggered by user actions (welcome emails, receipts, password resets, etc.).

Send a transactional email

await lemn.transactional.send({
  fromname: 'Your Company',          // required
  fromemail: 'noreply@company.com',  // required
  to: 'user@example.com',            // required
  toname: 'John Doe',                // optional
  subject: 'Welcome!',               // required
  body: '<h1>Welcome!</h1>',         // required, HTML format
  replyto: 'support@company.com',    // optional
  returnpath: 'bounce@company.com',  // optional
  tag: 'welcome-email',              // optional, for reporting
  route: routeId,                    // optional, specific postal route
  template: templateId,              // optional, use a saved template instead of body
  variables: {                       // optional, Jinja2 template variables
    username: 'johndoe',
    plan: 'pro'
  }
});

Note: if template is provided, it overrides body. Variables are applied using Jinja2 syntax in the template.


Suppression Lists (lemn.supplists)

Suppression lists prevent emails from being sent to specific contacts.

Create a suppression list

const suplist = await lemn.supplists.create('Unsubscribed Users');

Get all suppression lists

const lists = await lemn.supplists.getAll();

Get a specific suppression list

const list = await lemn.supplists.get(listId);

Update a suppression list

await lemn.supplists.update(listId, 'New List Name');
// Second arg is just the new name string, not an object

Delete a suppression list

await lemn.supplists.delete(listId);

Add emails to a suppression list

// Pass newline-separated email addresses as a string
// Sent internally as Content-Type: text/plain
await lemn.supplists.addData(listId, 'john@example.com\njane@example.com');

Exclusion Lists (lemn.exclusion)

Global exclusions that apply across all sends.

Get all exclusion lists

const lists = await lemn.exclusion.getAll();

Add data to an exclusion list

// data is wrapped internally as { data: yourData }
await lemn.exclusion.addToList(listId, 'john@example.com\njane@example.com');

Webhooks (lemn.webhooks)

Create a webhook

await lemn.webhooks.createWebhook({
  url: 'https://yoursite.com/webhook',
  events: ['delivered', 'bounced', 'opened', 'clicked', 'unsubscribed']
});

Get all webhooks

const webhooks = await lemn.webhooks.getAllWebhooks();

Update a webhook

await lemn.webhooks.updateWebhook(webhookId, {
  url: 'https://yoursite.com/new-webhook-url'
});

Delete a webhook

await lemn.webhooks.deleteWebhook(webhookId);

Exports (lemn.exports)

Get all exported files

const exports = await lemn.exports.getAll();

Error Handling

All methods return Promises and throw on non-2xx responses. The thrown error is the parsed JSON error body from the API.

try {
  await lemn.lists.create('My List');
} catch (error) {
  console.error(error); // parsed API error object
}

Common Workflows

Create a list and add contacts, then send a broadcast

const lemn = new LemnAPI('your_api_key');

// 1. Get a postal route first
const routes = await lemn.broadcasts.getUserRoutes();
const routeId = routes[0].id; // pick the first available route

// 2. Create a contact list
const list = await lemn.lists.create('My Campaign List');

// 3. Add contacts
await lemn.lists.addSingleContact(list.id, {
  email: 'john@example.com',
  first_name: 'John',
  last_name: 'Doe'
});

// 4. Create a broadcast draft
const broadcast = await lemn.broadcasts.create({
  name: 'My First Campaign',
  subject: 'Hello from our team',
  fromname: 'My Company',
  fromemail: 'hello@mycompany.com',
  body: '<h1>Hello!</h1><p>Thanks for signing up.</p>',
  listid: list.id,
  route: routeId
});

// 5. Send a test first
await lemn.broadcasts.sendTest(broadcast.id, { to: 'me@mycompany.com' });

// 6. Start the broadcast when ready
await lemn.broadcasts.start(broadcast.id);

Send a transactional welcome email

const lemn = new LemnAPI('your_api_key');

await lemn.transactional.send({
  fromname: 'My App',
  fromemail: 'noreply@myapp.com',
  to: newUser.email,
  toname: newUser.name,
  subject: 'Welcome to My App!',
  body: `<h1>Hi ${newUser.name}!</h1><p>Your account is ready.</p>`,
  tag: 'welcome'
});

Gotchas

  • broadcasts.uploadFile takes a ReadStream, not a file path. Use fs.createReadStream('./file.jpg').
  • supplists.addData takes a newline-separated string of emails, not an array.
  • supplists.update second argument is just a name string, not an object.
  • lists.deleteContacts and lists.deleteDomains take the array directly as the second argument, not wrapped in an object.
  • lists.addUnsubscribes takes an array but sends it as text/plain internally — do not pre-format it.
  • exclusion.addToList wraps your data internally as { data: yourData } — do not wrap it yourself.
  • Always call getUserRoutes() before creating broadcasts — you need a valid route ID.
  • Node.js 16 or higher is required.
Weekly Installs
2
First Seen
Mar 2, 2026
Installed on
cline2
gemini-cli2
github-copilot2
codex2
kimi-cli2
cursor2