API Webhooks MCP SDK OpenAPI JavaScript and TypeScript SDK The package pinemail wraps the sending API in methods with types, and checks webhook signatures. It has no dependencies and runs on Node 18+, Bun, Deno and edge runtimes. Field names are the API's own (snake_case), so the rest of these docs apply as they are. Other languages use the API directly, with the OpenAPI contract .
Install bash
npm install https://pinemail.app/sdk/pinemail-0.4.0.tgz
Send ts
import { PineMail } from "pinemail";
const pinemail = new PineMail(process.env.PINEMAIL_API_KEY);
const { id, status } = await pinemail.emails.send(
{
from: "Acme <hello@acme.example>",
to: "ada@example.com",
template: { slug: "welcome", variables: { name: "Ada" } },
scheduled_at: "2026-11-02T09:00:00+01:00",
},
{ idempotencyKey: "welcome-42" },
);
await pinemail.emails.cancel(id);
A request that fails throws PineMailError with status, the API's problem (its detail and field errors) and, on 429, retryAfter in seconds.
What it covers emailssend, batch, get, list, reschedule, cancel templateslist, get, create, update, remove, versions, restore domainslist, get, create, dns, verify, remove webhookslist, get, create, update, remove, test, retryFailed, retryDelivery suppressionslist, get, add, remove, softBounces, softBounce limitsget
pinemail.request(method, path, body) reaches any other endpoint with the same key, such as mailboxes or the directory.
Check a webhook ts
import { verifyWebhook } from "pinemail";
export async function POST(request: Request) {
const raw = await request.text();
const ok = await verifyWebhook(raw, request.headers.get("x-mail-signature"), process.env.PINEMAIL_WEBHOOK_SECRET!);
if (!ok) return new Response("bad signature", { status: 400 });
const event = JSON.parse(raw);
// event.type is email.delivered, email.bounced, ...
return new Response(null, { status: 204 });
}
Pass the body exactly as it arrived, before parsing it. A signature older than five minutes is refused; the fourth argument changes that.