Node.js / TypeScript SDK

Official Node.js SDK for the Tratto Email API

The official SDK for Node.js and TypeScript: @tratto/email

Every example on this page is written against the published 1.1.0 release and typechecks against it.

Installation

npm install @tratto/email

Initialization

The exported class is Tratto, and the API key is a positional argument, not an options object:

import { Tratto } from '@tratto/email';

const tratto = new Tratto(process.env.TRATTO_API_KEY!);

An optional second argument overrides the base URL:

const tratto = new Tratto(process.env.TRATTO_API_KEY!, {
  baseUrl: 'https://api.tratto.email',
});

Send Email

emails.send resolves to { id } — nothing else. To read the status, fetch the email back.

const { id } = await tratto.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Hello!',
  text: 'Welcome to Tratto',
  html: '<h1>Welcome!</h1>',
});

console.log(id); // email_abc123

Pass an idempotency key as the second argument:

await tratto.emails.send(
  { from: '[email protected]', to: '[email protected]', subject: 'Hi', text: 'Hi' },
  'order-4711-confirmation',
);

Send with Template

const { id } = await tratto.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome, Alice!',
  templateId: 'tmpl_abc123',
  variables: {
    firstName: 'Alice',
    code: 'SECRET123',
  },
});

subject is required on every send, template or not. A template carries no subject of its own — see Templates.

Send with Markdown

Send Markdown and let the server render responsive email HTML — markdown is mutually exclusive with html:

const { id } = await tratto.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Welcome!',
  markdown: '# Welcome, {{firstName}}!',
  variables: { firstName: 'Alice' },
});

templates.create and templates.update take markdown too, and the Template response type carries format, source and renderWarnings.

Schedule Email

const { id } = await tratto.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Reminder',
  text: 'See you tomorrow!',
  scheduledAt: new Date('2026-10-01T09:00:00Z'),
});

Canceling or rescheduling a scheduled email is available over HTTP but has no SDK method yet — see What 1.1.0 does not cover.

Get Email Status

const email = await tratto.emails.get('email_abc123');
console.log(email.status); // 'delivered'
console.log(email.events); // full timeline

List Emails

const emails = await tratto.emails.list({
  limit: 50,
  after: 'cursor_value', // for pagination
});

emails.data.forEach((email) => {
  console.log(`${email.id}: ${email.status}`);
});

Add a Domain

The method is add, and it takes the domain as a string:

const domain = await tratto.domains.add('hello.yourdomain.com');

// `records` is an array of DNS records, not an object keyed by type
for (const record of domain.records) {
  console.log(`${record.type} ${record.host} → ${record.value}`);
}

Verify Domain

const verified = await tratto.domains.verify('dom_abc123');
console.log(verified.status); // 'pending' | 'verified' | 'failed'

Create Contact

Custom properties live in customFields. There is no metadata field:

const { id } = await tratto.contacts.create({
  email: '[email protected]',
  firstName: 'Alice',
  lastName: 'Smith',
  tags: ['vip', 'newsletter'],
  customFields: { plan: 'pro' },
});

Create Campaign

CreateCampaignParams in 1.1.0 requires templateId, audienceId, fromName, fromEmail and subjectA. create resolves to { id }:

const { id } = await tratto.campaigns.create({
  name: 'Q3 Sale',
  templateId: 'tmpl_abc123',
  audienceId: 'aud_abc123',
  fromName: 'Acme',
  fromEmail: '[email protected]',
  subjectA: 'Our summer sale starts today',
  subjectB: 'Summer sale: 30% off, today only', // optional A/B subject
});

const campaign = await tratto.campaigns.get(id);
console.log(campaign.status); // 'draft'

The API itself is more permissive than the 1.1.0 types: it accepts inline html instead of a templateId, and treats an omitted audienceId as "every contact in the workspace". Neither is expressible through the SDK yet — use HTTP for those.

Send Campaign

const sent = await tratto.campaigns.send('camp_xyz789');
console.log(sent.status); // 'sending'

Schedule it instead by passing a date:

await tratto.campaigns.send('camp_xyz789', {
  scheduledAt: new Date('2026-10-01T09:00:00Z'),
});

Get Campaign Stats

The method is getStats. Rates are percentages: 50 means 50%, not 0.5 — the same scale as Analytics.

const stats = await tratto.campaigns.getStats('camp_xyz789');

console.log(stats.stats.delivered);   // 4950
console.log(stats.rates.openRate);    // 50 → 50%
console.log(stats.rates.deliveryRate) // 99 → 99%

An alert written as rates.deliveryRate < 0.95 will never fire. The comparison you want is < 95.

Register Webhook

const webhook = await tratto.webhooks.create({
  url: 'https://yourapi.com/webhooks/tratto',
  events: ['sent', 'delivered', 'opened', 'clicked', 'bounced'],
});

console.log(webhook.secret); // whsec_xyz789...

Analytics

AnalyticsPeriod in 1.1.0 is '7d' | '30d' | '90d':

const summary = await tratto.analytics.getSummary('30d');
console.log(summary.delivered);

The API also accepts 180d and 1y; those two are not in the SDK type yet, so reach for HTTP if you need them.

Error Handling

TrattoError carries code, statusCode and docs:

import { Tratto, TrattoError } from '@tratto/email';

const tratto = new Tratto(process.env.TRATTO_API_KEY!);

try {
  await tratto.emails.send({
    from: '[email protected]', // not verified
    to: '[email protected]',
    subject: 'Hello',
    text: 'Test',
  });
} catch (error) {
  if (error instanceof TrattoError) {
    // The message names the domain that is not verified.
    console.error(error.code, error.statusCode, error.message);
  }
}

See Error Codes for the full list.

What 1.1.0 does not cover yet

These endpoints exist in the API and have no SDK method in 1.1.0. Call them over HTTP until the SDK catches up:

CapabilityHTTP
Cancel a scheduled emailDELETE /v1/emails/{id}
Reschedule an emailPATCH /v1/emails/{id}
Get one contactGET /v1/contacts/{id}
Update / delete a campaignPATCH, DELETE /v1/campaigns/{id}
Unschedule a campaignPOST /v1/campaigns/{id}/unschedule
Campaign link clicksGET /v1/campaigns/{id}/links
Update / delete / refresh an audiencePATCH, DELETE, POST …/refresh on /v1/audiences/{id}
List or remove audience contactsGET, DELETE on /v1/audiences/{id}/contacts
Render a markdown previewPOST /v1/templates/render-preview
API key management/v1/api-keys
Workspace brand logoPUT, DELETE /v1/workspace/brand/logo

Some newer response fields are likewise absent from the 1.1.0 types: Workspace has no limits or brand, Contact has no trackingOptOut, ListContactsParams has no q, and CampaignStats has no skipped, untracked or variants. The API returns them; the types do not describe them yet.



Edit this page on GitHub

Last updated on