Taifa MailTaifa Mail Docs
SDKs

TypeScript & JavaScript SDK

Send email, manage domains, contacts, templates, and webhooks from Node and the browser with the official @taifamail/sdk SDK.

The official TypeScript and JavaScript SDK is published as @taifamail/sdk on npm. Source lives in the taifa-mail-sdks repository.

The SDK is fully typed, ships its own type definitions, and works in any runtime with a global fetch (Node 18+, Deno, Bun, Cloudflare Workers, and modern browsers).

Installation

npm install @taifamail/sdk

It also installs cleanly with pnpm or yarn:

pnpm add @taifamail/sdk
# or
yarn add @taifamail/sdk

The SDK uses the runtime's global fetch, which is available on Node 18 and later. On older Node versions, pass a fetch implementation (for example from node-fetch or undici) through the client options (see below).

Authentication and client setup

Create an API key in the dashboard under Settings -> API Keys. Keys start with tfm_k_. Construct the client with that key:

import { Taifa Mail } from '@taifamail/sdk';
 
const taifamail = new TaifaMail({ apiKey: process.env.TAIFA_MAIL_API_KEY! });

The constructor accepts these options:

OptionTypeDefaultDescription
apiKeystring(required)Your API key. Starts with tfm_k_.
baseUrlstringhttps://govconnect.keOverride the API base URL.
maxRetriesnumber3Total attempts on 429 / 5xx, including the first.
timeoutMsnumber30000Per-request timeout in milliseconds.
fetchfunctionglobal fetchA custom fetch implementation (for Node < 18 or testing).
import { Taifa Mail } from '@taifamail/sdk';
import fetch from 'node-fetch'; // only needed on Node < 18
 
const taifamail = new TaifaMail({
  apiKey: process.env.TAIFA_MAIL_API_KEY!,
  baseUrl: 'https://govconnect.ke',
  maxRetries: 3,
  timeoutMs: 30000,
  fetch: fetch as unknown as typeof globalThis.fetch,
});

Send an email

emails.send queues a single message and returns its id and initial status.

const result = await taifamail.emails.send({
  from: { email: 'hello@yourdomain.com', name: 'Your Company' },
  to: 'customer@example.com',
  subject: 'Your receipt',
  html: '<h1>Thanks for your order</h1><p>Your receipt is attached.</p>',
  text: 'Thanks for your order. Your receipt is attached.',
});
 
console.log(result.id, result.status);

The response is a SendEmailResponse:

interface SendEmailResponse {
  id: string;
  status: string;
  message_id?: string | null;
  rejection_reason?: string | null;
}

A few conveniences worth knowing:

  • The SDK exposes a clean from field, then maps it to the wire field from_ for you. You never write from_.
  • Any address field (from, to, cc, bcc, replyTo) accepts a bare string like 'customer@example.com', a { email, name } object, or an array of either. A bare string is treated as { email }.
  • Provide html, text, or both.
  • sendAt accepts an ISO 8601 string or a Date and schedules the message for later (Starter plan and up).
await taifamail.emails.send({
  from: 'hello@yourdomain.com',
  to: [{ email: 'a@example.com', name: 'Ada' }, 'b@example.com'],
  cc: 'manager@example.com',
  replyTo: { email: 'support@yourdomain.com' },
  subject: 'Welcome aboard',
  html: '<p>Welcome!</p>',
  tags: ['onboarding'],
  sendAt: new Date(Date.now() + 60 * 60 * 1000),
});

Emails

// Send a single email.
const sent = await taifamail.emails.send({ from, to, subject, html });
 
// Send a batch (bare array; Starter plan and up, capped by your plan).
const batch = await taifamail.emails.sendBatch([
  { from, to: 'a@example.com', subject, html },
  { from, to: 'b@example.com', subject, html },
]);
console.log(batch.total, batch.sent, batch.failed, batch.results);
 
// Dry-run a send without sending it.
const check = await taifamail.emails.validate({ from, to, subject, html });
if (!check.can_send) console.log(check.issues);
 
// List recent emails (newest first). page is zero-based.
const emails = await taifamail.emails.list({ status: 'delivered', page: 0, limit: 20 });
 
// Fetch one email with bodies and events.
const detail = await taifamail.emails.get(sent.id);
 
// List delivery / open / click / bounce events for an email.
const events = await taifamail.emails.events(sent.id);
 
// Re-send a bounced, rejected, or failed email as a new message.
const resent = await taifamail.emails.retry(sent.id);
 
// Search. q supports inline tokens: to:, from:, status:, domain:, tag:
const hits = await taifamail.emails.search({ q: 'welcome status:delivered', limit: 10 });
 
// Poll for emails whose status changed at or after a timestamp (max 50).
const updated = await taifamail.emails.updates(new Date(Date.now() - 60_000));

Scheduled emails:

const scheduled = await taifamail.emails.listScheduled();
await taifamail.emails.cancelScheduled(scheduled[0].id);
await taifamail.emails.sendScheduledNow(scheduled[0].id);

Saved searches (named filter sets stored per user):

const searches = await taifamail.emails.getSavedSearches();
await taifamail.emails.setSavedSearches([{ name: 'Bounced today', query: 'status:bounced' }]);

Domains

// List your sending domains and their verification status.
const domains = await taifamail.domains.list();
 
// Register a new domain. Returns the DNS records to publish.
const domain = await taifamail.domains.create('yourdomain.com');
console.log(domain.dns_records);
 
// Fetch a domain with its DKIM selector and DNS records.
const one = await taifamail.domains.get(domain.id);
 
// Re-check DNS and verify.
await taifamail.domains.verify(domain.id);
 
// Live DNS health checks (DKIM, SPF, DMARC, return-path, MX).
const health = await taifamail.domains.health(domain.id);
 
// Diagnose configuration issues and get a health score.
const diagnosis = await taifamail.domains.diagnose(domain.id);
 
// Rotate the DKIM key; returns the new record to publish.
const rotation = await taifamail.domains.rotateDkim(domain.id);
 
// Transfer the domain to another Taifa Mail account.
await taifamail.domains.transfer(domain.id, { targetEmail: 'owner@other.com', note: 'handoff' });
 
// Check availability against public DNS, or whether it already exists in your account.
const availability = await taifamail.domains.checkAvailability('newdomain.com');
const exists = await taifamail.domains.check('yourdomain.com');
 
// Delete a domain.
await taifamail.domains.delete(domain.id);

Contacts

Subscriber lists, their contacts, CSV imports, and templated bulk sends.

// Lists.
const lists = await taifamail.contacts.listLists();
const list = await taifamail.contacts.createList({ name: 'Newsletter', description: 'Monthly news' });
const listDetail = await taifamail.contacts.getList(list.id, { page: 0, limit: 50 });
await taifamail.contacts.updateList(list.id, { name: 'Monthly Newsletter' });
await taifamail.contacts.deleteList(list.id);
 
// Contacts within a list.
const contact = await taifamail.contacts.addContact(list.id, {
  email: 'subscriber@example.com',
  name: 'Subscriber',
  metadata: { plan: 'pro' },
});
await taifamail.contacts.removeContact(list.id, contact.id);

uploadCsv takes the raw file bytes (a Uint8Array) and a filename, sent as a single multipart field named file:

import { readFile } from 'node:fs/promises';
 
const bytes = await readFile('contacts.csv');
const importResult = await taifamail.contacts.uploadCsv(list.id, new Uint8Array(bytes), 'contacts.csv');
console.log(importResult.imported, importResult.skipped);

bulkSend mails a templated message to every contact in a list. Subject, html, and text may use {{email}}, {{name}}, and {{metadata_key}} placeholders:

const result = await taifamail.contacts.bulkSend(list.id, {
  senderAddressId: 'sender_123',
  subject: 'Hello {{name}}',
  html: '<p>Hi {{name}}, here is the latest.</p>',
  tags: ['newsletter'],
});
console.log(result.queued, result.skipped);

Suppressions

The do-not-send list. list returns a paginated Page envelope ({ items, total, page, limit }).

const page = await taifamail.suppressions.list({ page: 0, limit: 50, search: 'gmail.com' });
console.log(page.items, page.total);
 
await taifamail.suppressions.add({ email: 'unsubscribed@example.com', reason: 'manual' });
 
// Bulk import from a file (one email per line) as a single multipart field.
import { readFile } from 'node:fs/promises';
const bytes = await readFile('suppressions.txt');
const bulk = await taifamail.suppressions.bulkUpload(new Uint8Array(bytes), 'suppressions.txt');
console.log(bulk.added, bulk.skipped, bulk.total_processed);
 
await taifamail.suppressions.remove('suppression_id');

Templates

Reusable email templates. html maps to the wire field html_body and text to text_body for you.

const templates = await taifamail.templates.list();
 
const template = await taifamail.templates.create({
  name: 'Receipt',
  subject: 'Your receipt',
  html: '<h1>Thanks, {{name}}</h1>',
  text: 'Thanks, {{name}}',
});
 
const fetched = await taifamail.templates.get(template.id);
await taifamail.templates.update(template.id, { subject: 'Your order receipt' });
const copy = await taifamail.templates.duplicate(template.id);
await taifamail.templates.delete(template.id);

A template's variables are derived server-side from the {{name}} placeholders in its bodies, so you do not pass them when creating or updating.

Webhooks

const webhooks = await taifamail.webhooks.list();
 
// Create one. The signing secret is generated and returned in plaintext.
const webhook = await taifamail.webhooks.create({
  url: 'https://example.com/hooks/taifamail',
  events: ['email.delivered', 'email.bounced'],
});
console.log(webhook.secret);
 
await taifamail.webhooks.update(webhook.id, { events: ['email.opened'], isActive: true });
 
// Queue a sample email.delivered delivery to test the endpoint.
await taifamail.webhooks.test(webhook.id);
 
// Inspect delivery attempts (paginated envelope).
const deliveries = await taifamail.webhooks.listDeliveries(webhook.id, { page: 0, limit: 20 });
const delivery = await taifamail.webhooks.getDelivery(webhook.id, deliveries.items[0].id);
console.log(delivery.payload, delivery.response_body);
 
await taifamail.webhooks.delete(webhook.id);

Error handling

Every non-2xx response, and any transport failure that survives all retries, throws an TaifaMailError. Inspect status and code to branch on specific failures.

import { Taifa Mail, TaifaMailError } from '@taifamail/sdk';
 
const taifamail = new TaifaMail({ apiKey: process.env.TAIFA_MAIL_API_KEY! });
 
try {
  await taifamail.emails.send({
    from: 'hello@yourdomain.com',
    to: 'customer@example.com',
    subject: 'Hello',
    html: '<p>Hi</p>',
  });
} catch (err) {
  if (err instanceof TaifaMailError) {
    console.error(err.status); // HTTP status; 0 means a transport/network failure
    console.error(err.code);   // machine-readable code from the API body, when present
    console.error(err.message);
  } else {
    throw err;
  }
}

TaifaMailError also carries a detail field with the raw parsed response body for debugging.

Configuration

  • Retries. Requests that return 429 or 5xx are retried with exponential backoff, honoring the Retry-After header when present. Control the total attempt count (including the first) with maxRetries (default 3). Multipart uploads are not retried because they are not idempotent.
  • Timeout. Each request is aborted after timeoutMs (default 30000). A timeout is treated as a transport failure and is retried while attempts remain.
  • Custom base URL. Point the client at a staging or self-hosted host with baseUrl. A trailing slash is trimmed automatically.
const taifamail = new TaifaMail({
  apiKey: process.env.TAIFA_MAIL_API_KEY!,
  baseUrl: 'https://staging.govconnect.ke',
  maxRetries: 5,
  timeoutMs: 10000,
});

Next steps

On this page