Skip to main content

TypeScript SDK

Complete reference for the @notifykit/sdk TypeScript/Node.js client.

Installation

npm install @notifykit/sdk
# yarn add @notifykit/sdk
# pnpm add @notifykit/sdk

Requirements: Node.js 18+, TypeScript 5.0+ (optional but recommended)

Initialization

import { NotifyKitClient } from "@notifykit/sdk";

const client = new NotifyKitClient({
apiKey: process.env.NOTIFYKIT_API_KEY!,
baseUrl: "https://api.notifykit.dev", // Optional — defaults to production
});

Configuration Options

OptionTypeRequiredDescription
apiKeystringYesYour NotifyKit API key (nh_...)
baseUrlstringNoAPI base URL. Defaults to https://api.notifykit.dev

Methods

sendEmail(options)

Queue an email for delivery. Returns immediately with a job ID — the email is sent asynchronously.

Parameters:

interface SendEmailOptions {
to: string; // Recipient email address
subject: string; // Email subject line
body: string; // Email body (HTML supported)
from?: string; // Sender address. Use your verified domain (e.g. support@yourdomain.com). Paid plans only.
priority?: 1 | 5 | 10; // Job priority: 1=high, 5=normal (default), 10=low
idempotencyKey?: string; // Unique key to prevent duplicate sends
}

Returns: Promise<JobResponse>

interface JobResponse {
jobId: string;
status: string; // "pending" on creation
type: string; // "email"
createdAt: string; // ISO 8601 timestamp
}

Example:

const job = await client.sendEmail({
to: "user@example.com",
subject: "Welcome!",
body: "<h1>Hello World</h1>",
priority: 1, // High priority
idempotencyKey: "welcome-user-123", // Prevents duplicate sends
});

console.log(job.jobId); // "job_abc123"
Email providers by plan
  • Free: Sent via NotifyKit's shared SendGrid account. from is always noreply@notifykit.dev.
  • Indie / Startup: Sent via your own SendGrid account. Connect your key in Settings → Email Provider. Custom from addresses (using your verified domain) are supported.

Coming soon: Resend, Mailgun, and AWS SES support. :::


sendWebhook(options)

Queue a webhook delivery. Returns immediately with a job ID.

Parameters:

interface SendWebhookOptions {
url: string; // Webhook destination URL (must be HTTPS)
payload: object; // JSON payload to deliver (required)
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; // HTTP method (default: "POST")
headers?: Record<string, string>; // Custom headers to include
priority?: 1 | 5 | 10; // Job priority: 1=high, 5=normal (default), 10=low
idempotencyKey?: string; // Unique key to prevent duplicate sends
}

Returns: Promise<JobResponse>

Example:

const job = await client.sendWebhook({
url: "https://api.example.com/webhook",
payload: {
event: "payment.completed",
amount: 49.99,
},
headers: {
"X-Webhook-Secret": process.env.WEBHOOK_SECRET!,
},
idempotencyKey: "payment-456-webhook",
});

console.log(job.jobId); // "job_xyz789"

getJob(jobId)

Get the full status and details of a specific job.

Parameters:

  • jobId (string) — The job ID returned from sendEmail or sendWebhook

Returns: Promise<JobDetails>

interface JobDetails {
id: string;
type: string; // "email" or "webhook"
status: "pending" | "processing" | "completed" | "failed";
priority: number;
payload: object; // The original payload sent
attempts: number; // Number of delivery attempts made
maxAttempts: number; // Maximum attempts (3)
errorMessage?: string;
createdAt: string;
startedAt?: string;
completedAt?: string;
}

Example:

const status = await client.getJob("job_abc123");

console.log(status.status); // "completed"
console.log(status.attempts); // 1

if (status.status === "failed") {
console.error("Delivery failed:", status.errorMessage);
}

listJobs(options?)

List your jobs with optional filters and pagination.

Parameters:

interface ListJobsOptions {
page?: number; // Page number (default: 1)
limit?: number; // Per page (default: 20, max: 100)
type?: "email" | "webhook"; // Filter by notification type
status?: "pending" | "processing" | "completed" | "failed"; // Filter by status
}

Returns: Promise<{ data: JobSummary[]; pagination: PaginationMeta }>

interface JobSummary {
id: string;
type: string;
status: "pending" | "processing" | "completed" | "failed";
priority: number;
attempts: number;
errorMessage?: string;
createdAt: string;
completedAt?: string;
}

interface PaginationMeta {
page: number;
limit: number;
total: number;
totalPages: number;
}

Example:

const { data, pagination } = await client.listJobs({
status: "failed",
type: "webhook",
limit: 10,
});

console.log(`${pagination.total} failed webhook jobs`);

data.forEach((job) => {
console.log(`${job.id}: ${job.errorMessage} (${job.attempts} attempts)`);
});

retryJob(jobId)

Re-queue a failed job for another delivery attempt.

Parameters:

  • jobId (string) — ID of the job with failed status

Returns: Promise<RetryJobResponse>

interface RetryJobResponse {
jobId: string;
status: string; // "pending"
message: string;
}

Example:

const result = await client.retryJob("job_xyz789");
console.log(result.message); // "Job has been re-queued for processing"
console.log(result.status); // "pending"
Retry eligibility

Only jobs with failed status can be retried. pending, processing, and completed jobs cannot be retried.


ping()

Test API connectivity.

Returns: Promise<string>

Example:

const pong = await client.ping();
console.log(pong); // "pong"

getApiInfo()

Get API version and metadata.

Returns: Promise<ApiInfo>

interface ApiInfo {
name: string;
version: string;
description: string;
documentation: string;
}

Example:

const info = await client.getApiInfo();
console.log(info.name); // "NotifyKit API"
console.log(info.version); // "1.0.0"

Error Handling

All API errors throw a NotifyKitError.

import { NotifyKitClient, NotifyKitError } from "@notifykit/sdk";

try {
await client.sendEmail({
to: "invalid-email",
subject: "Test",
body: "Hello",
});
} catch (error) {
if (error instanceof NotifyKitError) {
// Formatted message with status code
console.error(error.getFullMessage());

// Check specific codes
if (error.isStatus(400)) {
console.error("Bad request:", error.message);
} else if (error.isStatus(401)) {
console.error("Invalid API key");
} else if (error.isStatus(403)) {
// Could be: domain not verified, quota exceeded, account inactive
console.error("Forbidden:", error.message);
} else if (error.isStatus(409)) {
console.error("Duplicate idempotency key");
} else if (error.isStatus(429)) {
console.error("Rate limit exceeded");
}

// Raw details
console.error("Status code:", error.statusCode);
console.error("Validation errors:", error.errors); // Array, if any
}
}

NotifyKitError Properties

PropertyTypeDescription
messagestringError message from the API
statusCodenumberHTTP status code
responseanyFull raw API response
errorsstring[]Validation error details (400 errors only)

NotifyKitError Methods

MethodDescription
isStatus(code)Returns true if statusCode === code
getFullMessage()Returns [statusCode] message\nValidation errors: ...

TypeScript Types

All types are exported from the package:

import type {
NotifyKitConfig,
SendEmailOptions,
SendWebhookOptions,
JobResponse,
JobDetails,
JobSummary,
PaginationMeta,
ApiInfo,
} from "@notifykit/sdk";
Domain verification types

Types like DomainVerificationResponse, DomainStatusResponse, DomainInfoResponse, and DnsRecord are exported from the package but domain verification methods are not yet available in the SDK client. Domain verification is currently managed through the NotifyKit dashboard (Settings → Domain).


Complete Example

import { NotifyKitClient, NotifyKitError } from "@notifykit/sdk";

const client = new NotifyKitClient({
apiKey: process.env.NOTIFYKIT_API_KEY!,
});

async function notifyUserAndSystem(
userId: string,
userEmail: string,
orderId: string,
) {
try {
// Send welcome email
const emailJob = await client.sendEmail({
to: userEmail,
subject: `Order #${orderId} Confirmed`,
body: `<h1>Thanks for your order!</h1><p>Order ID: ${orderId}</p>`,
priority: 1,
idempotencyKey: `order-${orderId}-email`,
});

console.log(`Email queued: ${emailJob.jobId}`);

// Notify internal system via webhook
const webhookJob = await client.sendWebhook({
url: "https://internal.your-app.com/order-events",
payload: { event: "order.confirmed", orderId, userId },
headers: { "X-Internal-Secret": process.env.INTERNAL_WEBHOOK_SECRET! },
idempotencyKey: `order-${orderId}-webhook`,
});

console.log(`Webhook queued: ${webhookJob.jobId}`);

// Check email delivery after 10 seconds
setTimeout(async () => {
const status = await client.getJob(emailJob.jobId);
if (status.status === "failed") {
console.error(`Email delivery failed: ${status.errorMessage}`);
// Optionally retry
await client.retryJob(emailJob.jobId);
}
}, 10_000);
} catch (error) {
if (error instanceof NotifyKitError) {
if (error.isStatus(409)) {
console.log("Already notified — idempotency key matched");
} else if (error.isStatus(403)) {
console.error("Quota or permission error:", error.message);
} else {
console.error("Notification failed:", error.getFullMessage());
}
}
throw error;
}
}

Next Steps