skillZs
★ LIVE SKILL TAGS ★
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
※ REAL INSTALL DATA ※
← back to all skills
schemavaults/send-email95 installs

send-email-to-mailing-list

Send an email to a mailing list via the SchemaVaults mail-server `/api/send` route, using either the `sendEmailToMailingList()` helper or the `schemavaults-send-email send-to-mailing-list` CLI from `@schemavaults/send-email`. Use when any server-side TypeScript/JavaScript code needs to send a notification to a mailing list audience — **or when Claude Code itself wants to send a one-shot notification at the end of a task** (preferred — invoke the CLI via `bunx schemavaults-send-email send-to-mailing-list …`; fallback — write a short script to `/tmp/` and run it with `bun`).

How do I install this agent skill?

npx skills add https://github.com/schemavaults/send-email --skill send-email-to-mailing-list
view source ↗

Is this agent skill safe to install?

  • Gen Agent Trust Hubpass

    This skill facilitates sending emails to mailing lists via the SchemaVaults service. It utilizes a vendor-provided NPM package and CLI. The skill involves downloading external code and sending data to an external API, which are standard operations for this functionality and are documented with appropriate safety measures.

  • Socketpass

    No alerts

  • Snykpass

    Risk: LOW · No issues

What does this agent skill do?

Send Email to Mailing List

This skill teaches Claude how to send an email to a mailing list, either (a) from any server-side TypeScript/JavaScript project that imports @schemavaults/send-email, or (b) directly from a Claude Code session — for example, to send a "I just finished this workflow" notification at the end of a task. In both cases the sendEmailToMailingList() helper wraps the SchemaVaults mail-server POST /api/send route and automatically resolves the API key from environment variables. The skill is self-contained and portable — drop it into any project's .claude/skills/ folder and you're done.

When to use this skill

There are two distinct use cases. Either fits this skill:

(a) Application code needs to send a notification to a mailing list audience. For example:

  • New user signup / first-purchase events
  • Unhandled errors in background jobs or cron tasks
  • Billing / subscription lifecycle events (trial ending, payment failed)
  • Ops alerts (deploy succeeded, rate-limit tripped, healthcheck failed)
  • Any ad-hoc "FYI, this just happened" message intended for a mailing list audience

(b) Claude Code itself wants to notify a mailing list at the end of a workflow. For example:

  • Claude just finished implementing a feature and pushed the branch.
  • Claude finished reviewing a PR and posted comments.
  • A long-running build, migration, or CI task finished (success or failure).
  • A scheduled maintenance script Claude was orchestrating completed.

For use case (b), see the "Usage -- Claude Code post-workflow notification" section below.

Do not use it for:

  • Sending to individual end users (use sendEmail() with an email string instead)
  • Client-side / browser code (the API key is a secret)
  • High-volume broadcasts beyond 50 recipients per send (the mail-server caps each send call at 50 recipients)

Prerequisites

  1. Install the helper package in the target project:

    bun add @schemavaults/send-email
    # or: npm install @schemavaults/send-email
    
  2. Set two environment variables wherever the code runs (local dev, CI, production):

    • SCHEMAVAULTS_MAIL_API_KEY -- Bearer token issued from the mail-server's api_keys table. Always starts with svlts_mail_pk_. Treat it like any other secret; never commit it, never ship it to browsers.
    • SCHEMAVAULTS_MAILING_LIST_ID -- UUID of the target mailing list from the mail-server's MAILING_LISTS table.

    Both are mandatory when not passing values directly -- the helper throws Error("Failed to load ... from environment variable ...") if either is missing.

  3. Optional third env var: SCHEMAVAULTS_APP_ENVIRONMENT = "production" | "development" | "staging". If unset, the helper falls back to production and targets the production mail-server. Only set this when you explicitly want to hit a non-prod environment.

  4. Call only from server-side code -- API routes, server actions, cron handlers, background workers. Never from a React client component or browser bundle.

Usage -- template form (preferred)

When a React Email template already exists in the mail-server catalog, reference it by template_id so the rendering (HTML + plain text) happens on the mail-server. Use listEmailTemplates() from @schemavaults/send-email (or see the list-email-templates skill) to discover available template IDs.

import { sendEmailToMailingList } from "@schemavaults/send-email";

export async function notifyMailingListOfSignup(userName: string): Promise<void> {
  await sendEmailToMailingList({
    body: {
      subject: `New signup: ${userName}`,
      message: {
        template_id: "<template-id-from-GET-/api/templates>",
        template_props: {
          /* prop shape per the template's description field */
        },
      },
    },
  });
}

If none of the registered templates fits your notification, use the raw text/html form below instead of trying to bend a mismatched template.

Usage -- raw HTML/text form (ad-hoc)

For one-off notifications where spinning up a dedicated React Email template is overkill, supply text and html directly. Both fields are required.

import { sendEmailToMailingList } from "@schemavaults/send-email";

export async function notifyMailingListOfError(err: Error, context: string): Promise<void> {
  const subject = `[alert] ${context}: ${err.message}`;
  const text =
    `An error occurred in ${context}.\n\n` +
    `Message: ${err.message}\n\n` +
    `Stack:\n${err.stack ?? "(no stack)"}\n`;
  const html =
    `<p>An error occurred in <code>${context}</code>.</p>` +
    `<p><strong>Message:</strong> ${err.message}</p>` +
    `<pre>${err.stack ?? "(no stack)"}</pre>`;

  await sendEmailToMailingList({
    body: { subject, message: { text, html } },
  });
}

Escape user-supplied values before embedding them in html if they can contain < / > / & -- the mail-server does not sanitize this for you.

Usage -- passing a mailing list ID explicitly

By default sendEmailToMailingList reads the mailing list UUID from the SCHEMAVAULTS_MAILING_LIST_ID env var. You can override this per-call:

await sendEmailToMailingList({
  mailingListId: "00000000-0000-0000-0000-000000000000",
  body: {
    subject: "Hello from a specific list",
    message: { text: "Hello", html: "<p>Hello</p>" },
  },
});

Usage -- CLI (preferred for one-off / ad-hoc sends)

@schemavaults/send-email ships a schemavaults-send-email binary that wraps the same helper. For any one-off send -- a manual notification, a quick smoke test, a bash cron entry, or Claude Code firing off a single end-of-workflow email -- the CLI is the simplest path. No /tmp/ script, no bun run.

# Raw text/html (both required)
bunx schemavaults-send-email send-to-mailing-list \
  --subject "[ops] nightly backup finished" \
  --text  "Backup completed at $(date -u +%FT%TZ). 0 errors." \
  --html  "<p>Backup completed at $(date -u +%FT%TZ). <strong>0 errors.</strong></p>"

# Template-based
bunx schemavaults-send-email send-to-mailing-list \
  --subject "Welcome aboard, Alice" \
  --template-id welcome-email \
  --template-props '{"name":"Alice"}'

# Override the mailing list per-call
bunx schemavaults-send-email send-to-mailing-list \
  --mailing-list-id 00000000-0000-0000-0000-000000000000 \
  --subject "..." --text "..." --html "..."

# Route through a specific mail-server transport (global flag, before the subcommand)
bunx schemavaults-send-email --transport smtp-primary send-to-mailing-list \
  --subject "..." --text "..." --html "..."

# Long bodies: read from files instead of inline strings
bunx schemavaults-send-email send-to-mailing-list \
  --subject "weekly digest" \
  --text-file /tmp/digest.txt \
  --html-file /tmp/digest.html

# Or supply the entire request body as a JSON file (validated server-side)
bunx schemavaults-send-email send-to-mailing-list --body-file /tmp/payload.json

(Substitute npx for bunx if bun is unavailable.) The CLI reads SCHEMAVAULTS_MAIL_API_KEY and SCHEMAVAULTS_MAILING_LIST_ID from the environment exactly like the helper, exits non-zero with a one-line error on failure, and exits 0 on a successful 200 from the mail-server.

Run bunx schemavaults-send-email send-to-mailing-list --help for the full flag reference.

Validating before sending (--dry-run)

Because a mailing-list send fans out to every subscriber, never use a real send to validate your request. Use --dry-run to round-trip Zod body + template_props shape + API-key/mailing-list scoping through the server without dispatching anything:

bunx schemavaults-send-email send-to-mailing-list --dry-run \
  --subject "Welcome aboard, Alice" \
  --template-id welcome-email \
  --template-props '{"name":"Alice"}'

A successful dry-run prints [dry-run] mailing-list request validated; no email sent. and exits 0. A malformed request exits non-zero with the server's validation error.

Usage -- Claude Code post-workflow notification

Claude itself can use this skill to send a one-shot notification to a mailing list at the end of a workflow in any repo that depends on @schemavaults/send-email (this repo already does).

Preferred: invoke the CLI directly

For most end-of-workflow notifications (a few sentences plus a short bullet list) the CLI is the right tool -- one shell command, no scratch file:

bunx schemavaults-send-email send-to-mailing-list \
  --subject "[claude-code] workflow finished: <short description>" \
  --text "$(printf 'Claude just finished a workflow.\n\nSummary:\n- <bullet 1>\n- <bullet 2>\n- <bullet 3>\n')" \
  --html "$(printf '<p>Claude just finished a workflow.</p><p><strong>Summary:</strong></p><ul><li>&lt;bullet 1&gt;</li><li>&lt;bullet 2&gt;</li><li>&lt;bullet 3&gt;</li></ul>')"

Replace the <short description> and bullet placeholders with a concrete summary. Keep the subject under ~70 characters and the body scannable (3-5 bullets is usually enough). A non-zero exit means the helper threw -- surface the error in your summary to the user rather than retrying silently.

Fallback: write a /tmp/ script

Reach for the script form only when the body is large enough or templated enough that string-quoting in shell is awkward (e.g. multi-paragraph HTML, dynamic data assembly, conditional content):

// /tmp/send-notification-after-workflow.ts
import { sendEmailToMailingList } from "@schemavaults/send-email";

async function main(): Promise<void> {
  await sendEmailToMailingList({
    body: {
      subject: "[claude-code] workflow finished: <short description>",
      message: {
        text:
          "Claude just finished a workflow.\n\n" +
          "Summary:\n" +
          "- <bullet 1>\n" +
          "- <bullet 2>\n" +
          "- <bullet 3>\n",
        html:
          "<p>Claude just finished a workflow.</p>" +
          "<p><strong>Summary:</strong></p>" +
          "<ul>" +
          "<li>&lt;bullet 1&gt;</li>" +
          "<li>&lt;bullet 2&gt;</li>" +
          "<li>&lt;bullet 3&gt;</li>" +
          "</ul>",
      },
    },
  });
  console.log("[notify] sent");
}

main().catch((err) => {
  console.error("[notify] failed:", err);
  process.exit(1);
});

Run from the repo root so Bun resolves @schemavaults/send-email through the repo's node_modules/:

bun run /tmp/send-notification-after-workflow.ts

When to trigger this

Send exactly one notification at the end of a workflow, after all commits and pushes have landed, so the email reflects the final state.

Cautions

  • The env vars SCHEMAVAULTS_MAIL_API_KEY and SCHEMAVAULTS_MAILING_LIST_ID must be set in Claude's process. If they're missing, the helper (and CLI) throws a clear error -- report it to the user instead of retrying blindly.
  • One notification per workflow, not per step. If a workflow had no meaningful outcome (e.g. "user asked a question, Claude answered"), skip the notification entirely. The inbox should not become chatty.
  • Do not send the notification before the work is finished. Push first, notify second.
  • Ask before sending if the user hasn't explicitly opted in to post-workflow notifications. Sending email is a side effect visible to other humans; don't do it silently on tasks where the user hasn't asked for it.
  • Never send a blank or "test" email to a mailing list to validate a request. Use --dry-run (CLI) or dryRun: true (helper body). The mail-server validates the full request -- including template_props shape -- without dispatching to the audience. A misfired real test reaches every subscriber.

Request body shape

sendEmailToMailingList accepts Omit<SendEmailRequestBody, "to" | "cc" | "bcc"> -- the audience is the mailing list, so to, cc, and bcc are intentionally not allowed. Allowed fields:

type MailingListNotificationBody = {
  subject: string;
  message:
    | { template_id: string; template_props?: unknown }
    | { text: string; html: string };
  from?: string;      // defaults to the mail-server's configured sender
  replyTo?: string;   // optional reply-to override
  dryRun?: boolean;   // server validates without dispatching
  transport?: string; // which mail-server transport to deliver with
};

// Full call signature:
type ISendEmailToMailingListOpts = {
  body: MailingListNotificationBody;
  mailingListId?: string; // override SCHEMAVAULTS_MAILING_LIST_ID
  bearerToken?: string;   // override SCHEMAVAULTS_MAIL_API_KEY; rarely needed
  mailServerUrl?: string; // override the server origin; rarely needed
  environment?: "production" | "development" | "staging";
  dryRun?: boolean;       // convenience; sets body.dryRun
  transport?: string;     // convenience; sets body.transport
};

Error handling

The helper throws on any non-200 response -- wrap the call in try/catch whenever a failed notification should not break the caller's primary flow:

try {
  await sendEmailToMailingList({
    body: {
      subject: `New signup: ${userName}`,
      message: { text, html },
    },
  });
} catch (notifyErr) {
  console.error("[notify] failed to send mailing list notification", notifyErr);
}

Common failure modes:

ErrorCause
Failed to load API key from environment variable 'SCHEMAVAULTS_MAIL_API_KEY'Env var not set (or empty string) in the runtime environment.
Failed to load mailing list ID from environment variable 'SCHEMAVAULTS_MAILING_LIST_ID'Env var not set (or empty string) in the runtime environment.
Bad request body to send email with!Your body does not match the schema -- typically a missing subject, missing text/html pair, or unknown fields.
Invalid or revoked API key. (HTTP 401)SCHEMAVAULTS_MAIL_API_KEY is wrong, expired, or revoked.
This API key is not permitted... (HTTP 403)The API key is allowlisted to a different mailing list than the one targeted.
Failed to parse request body! (HTTP 400)Server-side Zod parsing failed; usually a template template_props shape mismatch.
Unknown-transport error (HTTP 400)transport named a transport the mail-server does not have configured. The client only checks that the name is well-formed; the server decides whether it exists.

Environment targeting

By default the helper resolves the mail-server URL for the production environment. To hit staging or development explicitly:

await sendEmailToMailingList({
  environment: "development",
  body: {
    subject: "dev smoke test",
    message: { template_id: "my-test-email", template_props: { name: "test" } },
  },
});

Or set SCHEMAVAULTS_APP_ENVIRONMENT at the process level -- the helper reads it via getAppEnvironment() from @schemavaults/app-definitions when opts.environment is not passed in.

Adding this skill to another project

  1. Copy this file into the target project's .claude/skills/ folder.
  2. In the target project, install the helper package:
    bun add @schemavaults/send-email
    
  3. Populate the two environment variables via the project's normal secret management (e.g. .env.local for local dev, your hosting provider's secret store for production):
    SCHEMAVAULTS_MAIL_API_KEY=svlts_mail_pk_...
    SCHEMAVAULTS_MAILING_LIST_ID=00000000-0000-0000-0000-000000000000
    
  4. Commit the skill file. The next Claude Code session in that repo will automatically discover the skill.

Reference

Source files inside the installed package (node_modules/@schemavaults/send-email/dist/) -- read these when you need ground truth:

  • send-email-to-mailing-list.{d.ts,js} -- the sendEmailToMailingList() helper and its ISendEmailToMailingListOpts interface.
  • send-email.{d.ts,js} -- the underlying sendEmail() implementation, including getSchemaVaultsMailApiKey() and server-URL resolution.
  • send-email-request-body-schema.{d.ts,js} -- the Zod schema (createSendEmailRequestBodySchema) that both the client helper and the mail-server route use to validate bodies.
  • index.d.ts -- package entry point; lists every exported symbol.

Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.

<a href="https://skillzs.dev/skills/schemavaults/send-email/send-email-to-mailing-list">View send-email-to-mailing-list on skillZs</a>