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

@illumetric/humblesend

v0.1.1

Published

Official server-side JavaScript and TypeScript SDK for Humblesend

Readme

@illumetric/humblesend

The official server-side JavaScript and TypeScript SDK for the Humblesend API.

It works in Node.js 18 and newer, Bun, Deno, and Cloudflare Workers. The package uses the runtime's standard fetch implementation and has no runtime dependencies.

Never put a Humblesend API key in browser code. Calls from a browser expose the key to every visitor. Use this SDK only in a trusted server, worker, or backend application.

Install

npm install @illumetric/humblesend

ES modules:

import { Humblesend } from '@illumetric/humblesend';

const humblesend = new Humblesend({ apiKey: process.env.HUMBLESEND_API_KEY! });

CommonJS:

const { Humblesend } = require('@illumetric/humblesend');

const humblesend = new Humblesend({ apiKey: process.env.HUMBLESEND_API_KEY });

The default API URL is https://humblesend.com/api/v1. A custom baseUrl and fetch implementation can be supplied for testing or another compatible runtime.

API keys

Create a workspace API key in Humblesend and keep it in a secret manager or environment variable. Grant only the scopes the integration needs:

  • transactional:send for transactional messages
  • subscribers:read to list or read contacts
  • subscribers:write to create or update contacts
  • lists:write when assigning List or Tag IDs
  • consent:write to record marketing consent

Test keys exercise the transactional delivery workflow without contacting the email provider. Live keys can send real email and are required for contact and consent operations. Keep test and live keys in separate environment settings so a development deployment cannot send live email by mistake.

Transactional email

Send inline content:

const result = await humblesend.transactional.send(
	{
		to: { email: '[email protected]', name: 'Ada' },
		subject: 'Your receipt',
		text: 'Thanks for your order.'
	},
	{ idempotencyKey: `receipt:${order.id}` }
);

console.log(result.data.id, result.meta.requestId);

For an internal contact-form notification, keep the authenticated Sender and direct replies to the customer:

await humblesend.transactional.send({
	to: { email: '[email protected]', name: 'Support' },
	replyTo: '[email protected]',
	subject: 'New contact request',
	text: 'A customer sent a contact request.'
});

replyTo overrides the selected transactional Sender's configured reply-to address for only that message. It must contain one email address.

Send a published Humblesend Email, schedule it, and pass typed template parameters:

await humblesend.transactional.send({
	to: { externalCustomerId: customer.id, source: 'shop' },
	emailId: '11111111-1111-4111-8111-111111111111',
	parameters: { order: { number: order.number, total: order.total } },
	sendAt: new Date('2026-10-01T09:00:00Z'),
	tags: { kind: 'order_confirmation' }
});

Inspect, reschedule, or cancel a message:

const message = await humblesend.transactional.get(messageId);
await humblesend.transactional.reschedule(messageId, { sendAt: new Date(Date.now() + 3_600_000) });
await humblesend.transactional.cancel(messageId);

Date objects are serialized as ISO 8601 timestamps. ISO timestamp strings are accepted too.

Contacts

Create or update a contact by email:

const contact = await humblesend.contacts.upsert({
	email: '[email protected]',
	profile: { firstName: 'Ada', signupAt: new Date() },
	externalIdentities: [{ namespace: 'shop.customer', value: 'customer-42' }],
	listIds: ['11111111-1111-4111-8111-111111111111'],
	tagIds: ['22222222-2222-4222-8222-222222222222']
});

List and Tag memberships are assigned by ID on contact writes. Memberships are additive: omitting IDs does not remove existing memberships. List and Tag discovery, removal, and management are not part of SDK 0.1.0.

Other contact operations:

const page = await humblesend.contacts.list({ limit: 50, cursor });
const details = await humblesend.contacts.get(contactId);
await humblesend.contacts.update(contactId, { profile: { company: 'Analytical Engines' } });
await humblesend.contacts.upsertMany(contacts, { idempotencyKey: `import:${batchId}` });

Single-contact upserts and updates do not use idempotency keys and are not automatically retried.

Marketing consent

Contact data and permission to send marketing email are intentionally separate. Recording a subscribed event requires a legal basis and either evidence or an explanatory note:

await humblesend.contacts.recordConsent(
	contactId,
	{
		scope: 'global',
		state: 'subscribed',
		source: 'checkout',
		legalBasis: 'consent',
		evidence: 'Checked the marketing opt-in box during checkout',
		effectiveAt: new Date()
	},
	{ idempotencyKey: `marketing-consent:${checkout.id}` }
);

For a specific consent topic, use scope: 'topic' and provide topicId. For a global subscribed event, topicIds can limit the grant to selected topics; omit it to include every active topic.

contacts.subscribe provides a safe convenience operation. It upserts the contact first and only then records an explicit subscribed consent event:

const subscription = await humblesend.contacts.subscribe(
	{
		contact: { email: '[email protected]', listIds: [newsletterListId] },
		consent: {
			scope: 'global',
			source: 'newsletter_form',
			legalBasis: 'consent',
			note: 'Submitted the footer newsletter form',
			effectiveAt: new Date()
		}
	},
	{ idempotencyKey: `newsletter:${formSubmissionId}` }
);

console.log(subscription.meta.contact.requestId, subscription.meta.consent.requestId);

The two API calls are not atomic. If consent recording fails, the error includes error.partial.contact; a contact may exist without consent, but this method never grants consent before a successful contact upsert. Retry with the same caller-provided idempotency key.

Results and errors

Every direct API operation resolves to { data, meta }. Metadata contains the HTTP status, request ID, idempotency key when used, and whether the server replayed a prior response.

import { HumblesendError } from '@illumetric/humblesend';

try {
	await humblesend.transactional.send(message, { idempotencyKey: stableKey });
} catch (error) {
	if (error instanceof HumblesendError) {
		console.error(error.code, error.status, error.requestId, error.fields);
	}
}

The SDK automatically generates a UUID idempotency key when one is omitted. Generated keys protect retries during the current process. For deduplication across restarts, queues, or deployments, provide a stable key derived from the business operation and store it with that operation.

By default, requests time out after 30 seconds and retry at most twice. Network failures and HTTP 408, 429, 500, 502, 503, and 504 responses are retried only for reads and mutations that carry an idempotency key. The same key is retained, and Retry-After is respected. Configure these defaults when creating the client:

const humblesend = new Humblesend({
	apiKey: process.env.HUMBLESEND_API_KEY!,
	timeoutMs: 15_000,
	maxRetries: 1
});

Pass an AbortSignal in the final options argument to cancel any operation.

License

MIT