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

@productcraft/envoi

v0.0.5

Published

Receive-and-store mail platform — template-rendered sends, mailboxes + domains + DKIM, message timeline, suppression lists, outbound webhooks via ProductCraft Envoi. Generated from the production OpenAPI spec.

Readme

@productcraft/envoi

Typed Node.js SDK for ProductCraft Envoi — receive-and-store mail platform: send template-rendered messages, manage mailboxes + domains + DKIM, lint templates, track deliveries, configure outbound webhooks, hold suppression lists.

npm install @productcraft/envoi

Server-side only. The SDK ships a Platform API Key in the Authorization header — never embed it in a browser bundle.

Quick start

import { Envoi } from "@productcraft/envoi";

const envoi = new Envoi({
  auth: { type: "apiKey", key: process.env.PCFT_KEY! },
});

// Send a template-rendered message
const { data, error } = await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/templates/{name}/send",
  {
    params: {
      path: { workspace_id: "ws_...", name: "welcome" },
      // Make retries safe — replays return the original response
      // with `Idempotent-Replay: true` for 24h.
      header: { "Idempotency-Key": "welcome-2026-05-22-alice" },
    },
    body: {
      from: "[email protected]",
      to: "[email protected]",
      subject: "Welcome",          // optional — falls back to template subject
      data: { name: "Alice" },     // rendered into the template
    },
  },
);

The client property is an openapi-fetch instance bound to https://api.mail.productcraft.co and your auth credential. Every endpoint in the published OpenAPI spec is reachable through it — your editor's autocomplete lists paths + method shapes + body fields.

Configuration

new Envoi({
  // The auth credential the SDK presents to Envoi
  auth: { type: "apiKey", key: "pcft_live_..." }
      | { type: "bearer", token: "eyJ..." }
      | { type: "cookie", value: "auth_token=..." },
  // Override the prod base URL — useful for staging / a local proxy
  baseUrl: "https://api.mail.example.test",
  // Custom fetch implementation (undici with retry, mock in tests, ...)
  fetch: customFetch,
});

Common operations

Every path is workspace-scoped — {workspace_id} is the UUID of the workspace that owns the resource, returned by @productcraft/platform-auth's introspect endpoint.

Templates

// List templates
await envoi.client.GET(
  "/v1/workspaces/{workspace_id}/templates",
  { params: { path: { workspace_id: "ws_..." } } },
);

// Lint a template before saving — the body field is `body_html`
// (wire) / `bodyHtml` (camelCase TS DTO), NOT `html`.
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/templates/lint",
  {
    params: { path: { workspace_id: "<workspace-uuid>" } },
    body: { body_html: "<p>Hello {{name}}</p>" },
  },
);
// → { score, findings: [...] }

// Preview / render with sample data
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/templates/{name}/render",
  {
    params: { path: { workspace_id: "ws_...", name: "welcome" } },
    body: { data: { name: "Alice" } },
  },
);

// Send to one address (template-rendered)
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/templates/{name}/send",
  { params: { ... }, body: { from, to, data } },
);

// Send the same template to many addresses (batch)
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/templates/{name}/send-batch",
  { params: { ... }, body: { recipients: [{ to, data }, ...] } },
);

Domains + DKIM

// List domains
await envoi.client.GET("/v1/workspaces/{workspace_id}/domains", { ... });

// Add a domain (then publish the DKIM TXT record Envoi prints)
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/domains",
  { params: { ... }, body: { name: "mail.yourbrand.com" } },
);

// Verify after DNS propagation
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/domains/{domain_id}/verify",
  { params: { path: { workspace_id, domain_id } } },
);

Messages

// Workspace-wide message listing + filters
await envoi.client.GET(
  "/v1/workspaces/{workspace_id}/messages",
  { params: { path: { workspace_id }, query: { limit: 50 } } },
);

// Read body / attachments / raw RFC822
await envoi.client.GET("/v1/workspaces/{workspace_id}/messages/{id}/body", { ... });
await envoi.client.GET(
  "/v1/workspaces/{workspace_id}/mailboxes/{id}/messages/{mid}/attachments/{position}",
  { ... },
);

Suppression list

// Add an email to the workspace suppression list
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/suppression",
  { params: { ... }, body: { email: "[email protected]", reason: "hard_bounce" } },
);

Outbound webhooks

// Configure the workspace's default webhook (delivery / open / bounce events)
await envoi.client.PUT(
  "/v1/workspaces/{workspace_id}/webhooks/default",
  {
    params: { ... },
    body: { url: "https://yourapp.com/hooks/envoi", events: ["message.delivered"] },
  },
);

// Per-domain webhook overrides + secret rotation
await envoi.client.POST(
  "/v1/workspaces/{workspace_id}/webhooks/domains/{domain_id}/rotate-secret",
  { ... },
);

Idempotency

Send endpoints accept an Idempotency-Key header (1–256 chars, [A-Za-z0-9_\-:]). Retries with the same key + same body replay the original response with Idempotent-Replay: true for 24h. Same key + different body returns 409 IDEMPOTENCY_KEY_REUSE.

How this SDK is built

Generated from the live OpenAPI spec at https://api.mail.productcraft.co/docs-json via openapi-typescript (types) + openapi-fetch (runtime). The nightly spec-refresh workflow opens a PR whenever the spec changes; merging publishes a patch bump so type-safe consumers stay current automatically.

License

MIT.