Files
GibSend/packages/sdk
2026-01-18 22:08:26 +11:00
..
2026-01-18 20:50:54 +11:00
2025-10-18 10:31:43 +11:00
2025-09-03 08:21:55 +10:00
2024-05-25 08:40:35 +10:00
2026-01-18 20:50:54 +11:00
2025-09-03 08:21:55 +10:00
2026-01-18 20:50:54 +11:00
2025-09-03 08:21:55 +10:00

useSend SDK

Prerequisites

Installation

NPM

npm install usesend

Yarn

yarn add usesend

PNPM

pnpm add usesend

Bun

bun add usesend

Usage

import { UseSend } from "usesend";

const usesend = new UseSend("us_12345");

// for self-hosted installations you can pass your base URL
// const usesend = new UseSend("us_12345", "https://app.usesend.com");

usesend.emails.send({
  to: "hello@acme.com",
  from: "hello@company.com",
  subject: "useSend email",
  html: "<p>useSend is the best open source product to send emails</p>",
  text: "useSend is the best open source product to send emails",
});

// Safely retry sends with an idempotency key
await usesend.emails.send(
  {
    to: "hello@acme.com",
    from: "hello@company.com",
    subject: "useSend email",
    html: "<p>useSend is the best open source product to send emails</p>",
  },
  { idempotencyKey: "signup-123" },
);

// Works for bulk sends too
await usesend.emails.batch(
  [
    {
      to: "a@example.com",
      from: "hello@company.com",
      subject: "Welcome",
      html: "<p>Hello A</p>",
    },
    {
      to: "b@example.com",
      from: "hello@company.com",
      subject: "Welcome",
      html: "<p>Hello B</p>",
    },
  ],
  { idempotencyKey: "bulk-welcome-1" },
);
// Reusing the same key with a different payload returns HTTP 409.

Campaigns

Create and manage email campaigns:

import { UseSend } from "usesend";

const usesend = new UseSend("us_12345");

// Create a campaign
const campaign = await usesend.campaigns.create({
  name: "Welcome Series",
  from: "hello@company.com",
  subject: "Welcome to our platform!",
  contactBookId: "cb_12345",
  html: "<h1>Welcome!</h1><p>Thanks for joining us.</p>",
  sendNow: false,
});

// Schedule a campaign
await usesend.campaigns.schedule(campaign.data.id, {
  scheduledAt: "2024-12-01T09:00:00Z",
  batchSize: 1000,
});

// Get campaign details
const details = await usesend.campaigns.get(campaign.data.id);

// Pause a campaign
await usesend.campaigns.pause(campaign.data.id);

// Resume a campaign
await usesend.campaigns.resume(campaign.data.id);

Webhooks

Verify webhook signatures and get typed events:

import { UseSend } from "usesend";

const usesend = new UseSend("us_12345");
const webhooks = usesend.webhooks(process.env.USESEND_WEBHOOK_SECRET!);

// In a Next.js App Route
export async function POST(request: Request) {
  try {
    const rawBody = await request.text(); // important: raw body, not parsed JSON
    const event = webhooks.constructEvent(rawBody, {
      headers: request.headers,
    });

    if (event.type === "email.delivered") {
      // event.data is strongly typed here
    }

    return new Response("ok");
  } catch (error) {
    return new Response((error as Error).message, { status: 400 });
  }
}

You can also use the Webhooks class directly:

import { Webhooks } from "usesend";

const webhooks = new Webhooks(process.env.USESEND_WEBHOOK_SECRET!);
const event = webhooks.constructEvent(rawBody, { headers: request.headers });

Need only signature verification? Use the verify method:

const isValid = webhooks.verify(rawBody, { headers: request.headers });

if (!isValid) {
  return new Response("Invalid signature", { status: 401 });
}

Express example (ensure raw body is preserved):

import express from "express";
import { Webhooks } from "usesend";

const webhooks = new Webhooks(process.env.USESEND_WEBHOOK_SECRET!);

const app = express();
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  try {
    const event = webhooks.constructEvent(req.body, {
      headers: req.headers,
    });

    if (event.type === "email.bounced") {
      // handle bounce
    }

    res.status(200).send("ok");
  } catch (error) {
    res.status(400).send((error as Error).message);
  }
});

Headers sent by UseSend:

  • X-UseSend-Signature: v1= + HMAC-SHA256 of ${timestamp}.${rawBody}
  • X-UseSend-Timestamp: Unix epoch in milliseconds
  • X-UseSend-Event: webhook event type
  • X-UseSend-Call: unique webhook attempt id

By default, signatures are only accepted within 5 minutes of the timestamp. Override with toleranceMs if needed.