npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lystica-cloud

v0.1.0

Published

Official Node.js & TypeScript SDK for the Lystica Cloud API — access professional contacts, companies, email campaigns, and lists programmatically.

Readme

lystica-cloud

Official Node.js & TypeScript SDK for the Lystica Cloud API.

Access professional contacts, companies, email campaigns, and lists programmatically. Integrate Lystica data into your own applications, CRMs, and workflows.

npm License: MIT

Installation

npm install lystica-cloud
yarn add lystica-cloud
pnpm add lystica-cloud

Quick Start

import { LysticaCloud } from "lystica-cloud";

const lystica = new LysticaCloud({
  apiKey: process.env.LYSTICA_API_KEY!,
});

// Fetch contacts
const { data, meta } = await lystica.contacts.list({
  limit: 50,
  industry: "Technology",
});

console.log(`Found ${meta.total} contacts`);
data.forEach((contact) => {
  console.log(`${contact.fullName} — ${contact.email}`);
});

Getting an API Key

  1. Sign in to lystica.cloud
  2. Go to Dashboard → API Keys
  3. Click Create API Key
  4. Copy the key immediately (it's only shown once)
  5. Store it securely (environment variable, secrets manager, etc.)

API keys use the format lys_live_... (production) or lys_test_... (sandbox).

Configuration

const lystica = new LysticaCloud({
  apiKey: "lys_live_...",       // Required
  baseUrl: "https://...",       // Default: https://api.lystica.cloud
  timeout: 30000,               // Default: 30s
  maxRetries: 2,                // Default: 2 (retries on 5xx / network errors)
});

| Option | Type | Default | Description | | ------------ | -------- | ---------------------------- | -------------------------------------- | | apiKey | string | — | Required. Your Lystica API key. | | baseUrl | string | https://api.lystica.cloud | API base URL. | | timeout | number | 30000 | Request timeout in ms. | | maxRetries | number | 2 | Auto-retries on 5xx / network errors. |


Resources

Contacts

// List with filters
const { data, meta } = await lystica.contacts.list({
  limit: 100,
  industry: "Finance",
  country: "United States",
  seniority: "Director",
});

// Get by ID
const contact = await lystica.contacts.get("cnt_abc123");

// Create
const newContact = await lystica.contacts.create({
  email: "[email protected]",
  firstName: "Jane",
  lastName: "Doe",
  company: "Acme Inc",
  tags: ["lead", "enterprise"],
});

// Update
const updated = await lystica.contacts.update("cnt_abc123", {
  jobTitle: "VP of Engineering",
});

// Delete
await lystica.contacts.delete("cnt_abc123");

// Search
const { data: results } = await lystica.contacts.search("[email protected]");

// Tag management
await lystica.contacts.addTags("cnt_abc123", ["vip"]);
await lystica.contacts.removeTags("cnt_abc123", ["old-tag"]);

// Iterate all (auto-pagination)
for await (const contact of lystica.contacts.listAll({ industry: "SaaS" })) {
  console.log(contact.fullName);
}

Companies

// List
const { data } = await lystica.companies.list({
  industry: "Technology",
  size: "51-200",
});

// Get by ID
const company = await lystica.companies.get("cmp_abc123");

// Search
const { data: results } = await lystica.companies.search("acme");

// List contacts in a company
const { data: contacts } = await lystica.companies.listContacts("cmp_abc123");

// Iterate all
for await (const company of lystica.companies.listAll()) {
  console.log(company.name);
}

Emails

// Send an email
const email = await lystica.emails.send({
  from: "[email protected]",
  to: "[email protected]",
  subject: "Hello from Lystica",
  html: "<h1>Welcome!</h1>",
  text: "Welcome!",
});

// Schedule for later
const scheduled = await lystica.emails.send({
  from: "[email protected]",
  to: ["[email protected]", "[email protected]"],
  subject: "Weekly Update",
  html: "<p>Here's your weekly update...</p>",
  scheduledAt: "2026-03-01T09:00:00Z",
});

// Check status
const status = await lystica.emails.get("eml_abc123");
console.log(status.status); // "queued" | "sent" | "delivered" | "bounced" | "failed"

// Cancel a scheduled email
await lystica.emails.cancel("eml_abc123");

// List emails
const { data: emails } = await lystica.emails.list({ status: "bounced" });

Lists

// Create a list
const list = await lystica.lists.create({
  name: "Enterprise Leads Q1",
  description: "Filtered leads for Q1 outreach",
});

// Update
await lystica.lists.update("lst_abc123", { name: "Enterprise Leads Q2" });

// Add/remove contacts
await lystica.lists.addContacts("lst_abc123", ["cnt_1", "cnt_2", "cnt_3"]);
await lystica.lists.removeContacts("lst_abc123", ["cnt_1"]);

// List contacts in a list
const { data: contacts } = await lystica.lists.listContacts("lst_abc123");

// Delete
await lystica.lists.delete("lst_abc123");

Verify API Key

const info = await lystica.verifyKey();
console.log(info.name);     // "Production Key"
console.log(info.scopes);   // ["contacts:read", "contacts:write", ...]

Pagination

All list endpoints return cursor-based paginated responses:

interface PaginatedResponse<T> {
  data: T[];
  meta: {
    total: number;
    limit: number;
    cursor: string | null;
    hasMore: boolean;
  };
}

Manual pagination:

let cursor: string | undefined;

do {
  const response = await lystica.contacts.list({ limit: 100, cursor });
  process.stdout.write(`Fetched ${response.data.length} contacts\n`);
  cursor = response.meta.cursor ?? undefined;
} while (cursor);

Automatic pagination (async generator):

for await (const contact of lystica.contacts.listAll({ country: "US" })) {
  console.log(contact.email);
}

Webhooks

Verify incoming webhook payloads from Lystica:

import { Webhooks } from "lystica-cloud";

const webhooks = new Webhooks("whsec_your_signing_secret");

// In your webhook handler (e.g. Express):
app.post("/webhooks/lystica", async (req, res) => {
  try {
    const event = await webhooks.verify(
      req.body,                             // raw body string
      req.headers["x-lystica-signature"],   // signature header
      req.headers["x-lystica-timestamp"],   // timestamp header
    );

    switch (event.type) {
      case "contact.created":
        console.log("New contact:", event.data);
        break;
      case "email.delivered":
        console.log("Email delivered:", event.data);
        break;
    }

    res.status(200).send("ok");
  } catch (err) {
    res.status(400).send("Invalid signature");
  }
});

Error Handling

The SDK throws typed errors you can catch individually:

import {
  LysticaCloud,
  LysticaAuthError,
  LysticaForbiddenError,
  LysticaNotFoundError,
  LysticaValidationError,
  LysticaRateLimitError,
  LysticaError,
  LysticaNetworkError,
} from "lystica-cloud";

try {
  const { data } = await lystica.contacts.list();
} catch (err) {
  if (err instanceof LysticaAuthError) {
    console.error("Invalid or expired API key");
  } else if (err instanceof LysticaForbiddenError) {
    console.error("Insufficient permissions");
  } else if (err instanceof LysticaNotFoundError) {
    console.error("Resource not found");
  } else if (err instanceof LysticaValidationError) {
    console.error("Invalid request data:", err.message);
  } else if (err instanceof LysticaRateLimitError) {
    console.error("Rate limit exceeded — slow down");
  } else if (err instanceof LysticaNetworkError) {
    console.error("Network failure:", err.message);
  } else if (err instanceof LysticaError) {
    console.error(`API error ${err.status}: ${err.message}`);
  } else {
    throw err;
  }
}

| Error Class | HTTP Status | When | | ------------------------ | ----------- | ------------------------------ | | LysticaAuthError | 401 | Missing or invalid API key | | LysticaForbiddenError | 403 | Insufficient scope or IP block | | LysticaNotFoundError | 404 | Resource does not exist | | LysticaValidationError | 422 | Invalid request data | | LysticaRateLimitError | 429 | Rate limit exceeded | | LysticaError | any | All other API errors | | LysticaNetworkError | — | Network failure or timeout |


TypeScript

Full TypeScript support with exported types:

import type {
  Contact,
  Company,
  Email,
  ContactList,
  CreateContactData,
  SendEmailData,
  PaginatedResponse,
  LysticaConfig,
} from "lystica-cloud";

Environments

Works in Node.js 18+ and any runtime with a global fetch implementation.

// Node.js
const lystica = new LysticaCloud({ apiKey: process.env.LYSTICA_API_KEY! });

// Next.js (server components / API routes / middleware)
const lystica = new LysticaCloud({ apiKey: process.env.LYSTICA_API_KEY! });

// Deno
const lystica = new LysticaCloud({ apiKey: Deno.env.get("LYSTICA_API_KEY")! });

// Bun
const lystica = new LysticaCloud({ apiKey: Bun.env.LYSTICA_API_KEY! });

Security: Never expose your API key in client-side / browser code. Always call the Lystica API from a server or serverless function.


Examples

See the examples/ directory for complete, runnable examples:


Contributing

git clone https://github.com/lystica/lystica-cloud-sdk.git
cd lystica-cloud-sdk
npm install
npm run build
npm test

License

MIT — see LICENSE for details.