Skip to content
DashboardRequest access

SDK Reference

@develemail/sdk exposes a DevelEmail client with one property per resource. All methods are async and throw DevelEmailError on non-2xx responses.

import { DevelEmail } from '@develemail/sdk';

const client = new DevelEmail({
  apiKey: 'your-api-key',
  baseUrl: 'https://develemail.com/api', // http://localhost:3001 for a self-hosted dev instance
  maxRetries: 3, // optional, defaults to 3. Retries on 429 and 5xx.
});

Emails

MethodSignature
sendsend(input: SendEmailInput, options?: SendOptions): Promise<EmailResponse>
batchbatch(emails: SendEmailInput[], options?: SendOptions): Promise<BatchEmailResponse>
getget(id: string): Promise<EmailResponse>
await client.emails.send({
  from: '[email protected]',
  to: ['[email protected]', '[email protected]'],
  subject: 'Your receipt',
  html: '<p>Thanks for your order.</p>',
  tags: ['receipts'],
});

const { id, status } = await client.emails.get('email_123');

from accepts a bare address ([email protected]) or a display-name mailbox (Acme <[email protected]>). Quote names that contain commas or other specials ("Acme, Inc." <[email protected]>); non-ASCII names are encoded for you. The address part must belong to a verified domain.

Email status & webhooks

status moves through queued → rendering → submitted_to_postfix → sent, then to one of delivered, bounced, deferred (retrying), rejected, or failed. delivered means the recipient's mail gateway accepted the message with a 2xx response — it does not mean the message reached the inbox. A delivered message can still be filtered to spam or quarantined afterward. The full set of statuses is exported as EMAIL_STATUSES / EmailStatus. Five more statuses cover paths outside that flow:

Every event type recorded for a project's emails — including delivered, deferred, and bounced — is sent to that project's configured webhook URL automatically; there's no per-event-type subscription to manage. The known event types are exported as EMAIL_EVENT_TYPES / EmailEventType, and the delivered/deferred/bounced event's metadata matches DeliveryEventData (queueId, relay, response, timestamp). See docs/webhooks.md in the repo for the full payload shape.

import { EMAIL_EVENT_TYPES, type DeliveryEventData } from '@develemail/sdk';

function handleWebhook(event: string, metadata: Record<string, unknown>) {
  if (!EMAIL_EVENT_TYPES.includes(event as never)) {
    console.warn(`Unrecognized event type: ${event}`);
    return;
  }
  if (event === 'delivered') {
    const { relay, response } = metadata as unknown as DeliveryEventData;
    console.log(`Delivered via ${relay}: ${response}`);
  }
}

Verifying webhook signatures

verifyWebhookSignature(payload, signature, secret) checks the HMAC-SHA256 signature of the raw request body. It returns Promise<boolean>, accepts the signature with or without a sha256= prefix, and compares in constant time. Verify the raw string before parsing the JSON.

import { verifyWebhookSignature } from '@develemail/sdk';

const ok = await verifyWebhookSignature(rawBody, signatureHeader, secret);
if (!ok) return new Response('invalid signature', { status: 401 });

Sandbox testing

Send to any address at @sandbox.develemail.com to exercise your integration without delivering mail. The API accepts the send, stores it with status sandboxed, and skips delivery. Sandbox sends are not metered: they bypass the monthly quota and the daily tier limit and don't count toward usage.

const { status } = await client.emails.send({
  from: '[email protected]',
  to: '[email protected]',
  subject: 'Test',
  text: 'Hello',
});
// status === 'sandboxed'

A single send can't mix sandbox and real recipients. It fails with a 400 and error code mixed_sandbox_recipients; send them as separate messages.

Domains

MethodSignature
listlist(): Promise<Domain[]>
getget(id: string): Promise<Domain>
createcreate(input: DomainCreateInput): Promise<Domain>
deletedelete(id: string): Promise<void>
verifyverify(id: string): Promise<Domain>
const domain = await client.domains.create({ name: 'yourdomain.com' });
await client.domains.verify(domain.id);

Templates

MethodSignature
listlist(): Promise<Template[]>
getget(id: string): Promise<Template>
createcreate(input: TemplateCreateInput): Promise<Template>
updateupdate(id: string, input: TemplateUpdateInput): Promise<Template>
deletedelete(id: string): Promise<void>
const template = await client.templates.create({
  name: 'welcome-email',
  subject: 'Welcome, {{firstName}}!',
  html: '<p>Hi {{firstName}}, welcome aboard.</p>',
  variables: ['firstName'],
});

Reference a template by name from emails.send via the template and variables fields on SendEmailInput.

SMS, campaigns, contacts and lists

These resources have their own pages: SMS and Campaigns, contacts and lists.

Error handling

Every method throws DevelEmailError on a failed request, with the HTTP status, a message, and an optional machine-readable code.

import { DevelEmail, DevelEmailError } from '@develemail/sdk';

try {
  await client.emails.send(input);
} catch (err) {
  if (err instanceof DevelEmailError) {
    console.error(err.status, err.code, err.message);
  }
}