TypeScript SDK

Install

npm install @pigeons/pigeons-ts

Initialize

import { Pigeons } from '@pigeons/pigeons-ts'

const pigeons = new Pigeons({
  baseUrl: 'https://pigeons.yourdomain.com',
  apiKey: 'pgn_your_key_here',
})

Both options can be set via environment variables instead:

OptionEnv varDescription
baseUrlPIGEONS_URLPigeons server URL (no default)
apiKeyPIGEONS_API_KEYAPI key starting with pgn_

Constructor values take precedence over environment variables.


Emails

Send an email

const email = await pigeons.emails.send({
  to: 'alice@example.com',
  subject: 'Welcome!',
  html: '<h1>Welcome aboard</h1>',
})

console.log(email.id)     // msg_abc123...
console.log(email.status) // "queued"

Send with a template

const email = await pigeons.emails.send({
  to: 'alice@example.com',
  templateId: 'welcome-email', // slug or tpl_ ID
  data: { name: 'Alice', company: 'Acme' },
})

Multiple recipients

const email = await pigeons.emails.send({
  to: ['alice@example.com', 'bob@example.com'],
  cc: ['manager@example.com'],
  subject: 'Team Update',
  html: '<p>Here is your update.</p>',
})

Schedule an email

const email = await pigeons.emails.send({
  to: 'alice@example.com',
  subject: 'Reminder',
  text: 'Your trial expires tomorrow.',
  scheduledAt: '2024-06-01T09:00:00Z',
})

Get email status

const email = await pigeons.emails.get('msg_abc123...')
console.log(email.status) // "delivered"

List emails

const page = await pigeons.emails.list({ limit: 10, status: 'delivered' })

for (const email of page.data) {
  console.log(email.id, email.subject, email.status)
}

if (page.has_more) {
  const next = await pigeons.emails.list({ cursor: page.next_cursor! })
}

Cancel a queued email

const cancelled = await pigeons.emails.cancel('msg_abc123...')

Batch send

const results = await pigeons.emails.batch([
  { to: 'alice@example.com', subject: 'Hi Alice', text: 'Hello!' },
  { to: 'bob@example.com', subject: 'Hi Bob', text: 'Hello!' },
])

Idempotent sends

const email = await pigeons.emails.send(
  { to: 'alice@example.com', subject: 'Order #123', html: '...' },
  { idempotencyKey: 'order-123-confirmation' },
)

Contacts

Create a contact

const contact = await pigeons.contacts.create({
  email: 'alice@example.com',
  firstName: 'Alice',
  lastName: 'Smith',
  tags: ['newsletter', 'beta'],
  properties: { plan: 'pro', signupSource: 'website' },
})

Get a contact

// By ID
const contact = await pigeons.contacts.get('ctc_abc123...')

// By email
const contact = await pigeons.contacts.get('alice@example.com')

Update a contact

const updated = await pigeons.contacts.update('ctc_abc123...', {
  tags: ['newsletter', 'beta', 'vip'],
  properties: { plan: 'enterprise' },
})

List contacts

const page = await pigeons.contacts.list({ tag: 'newsletter', limit: 50 })

Delete a contact

await pigeons.contacts.delete('ctc_abc123...')

Batch create

const result = await pigeons.contacts.batchCreate([
  { email: 'alice@example.com', firstName: 'Alice', tags: ['import'] },
  { email: 'bob@example.com', firstName: 'Bob', tags: ['import'] },
])
console.log(result.created) // 2
console.log(result.skipped) // 0

Unsubscribe / Resubscribe

// Global unsubscribe
await pigeons.contacts.unsubscribe('ctc_abc123...')

// Per-topic unsubscribe
await pigeons.contacts.unsubscribe('ctc_abc123...', { topicId: 'top_xyz...' })

// Resubscribe
await pigeons.contacts.resubscribe('ctc_abc123...')

Templates

Create a template

const template = await pigeons.templates.create({
  slug: 'welcome-email',
  name: 'Welcome Email',
  subject: 'Welcome, {{name}}!',
  body: '<h1>Welcome to {{company}}</h1><p>Hi {{name}},</p>',
  sampleData: { name: 'Alice', company: 'Acme' },
})

Get / Update / Delete

const tpl = await pigeons.templates.get('welcome-email') // by slug or ID
const updated = await pigeons.templates.update('welcome-email', { subject: 'New subject' })
await pigeons.templates.delete('welcome-email')

Preview a template

const preview = await pigeons.templates.preview('welcome-email', {
  name: 'Alice',
  company: 'Acme',
})
console.log(preview.subject) // "Welcome, Alice!"
console.log(preview.html)    // rendered HTML

Campaigns

Create and send a campaign

const campaign = await pigeons.campaigns.create({
  name: 'Product Launch',
  templateId: 'product-launch',
  templateData: { launchDate: '2024-06-01' },
  audienceFilter: { tags: { contains: 'newsletter' } },
})

// Send immediately
await pigeons.campaigns.send(campaign.id)

// Or schedule
await pigeons.campaigns.send(campaign.id, {
  scheduledAt: '2024-06-01T09:00:00Z',
})

Campaign lifecycle

await pigeons.campaigns.pause(campaign.id)
await pigeons.campaigns.resume(campaign.id)
await pigeons.campaigns.cancel(campaign.id)

Monitor stats

const campaign = await pigeons.campaigns.get('cmp_abc123...')
console.log(campaign.stats)
// { total: 500, sent: 450, delivered: 440, opened: 120, bounced: 5, complained: 1, failed: 4 }

Sequences

Create a sequence

const sequence = await pigeons.sequences.create({
  name: 'Onboarding',
  steps: [
    { type: 'email', templateId: 'welcome-email', subject: 'Welcome!' },
    { type: 'delay', delayDuration: '1d' },
    { type: 'email', templateId: 'getting-started', subject: 'Getting Started' },
    { type: 'delay', delayDuration: '3d' },
    { type: 'email', templateId: 'tips', subject: 'Pro Tips' },
  ],
})

Activate and enroll

await pigeons.sequences.activate(sequence.id)

const enrollment = await pigeons.sequences.enroll(sequence.id, 'ctc_abc123...')
console.log(enrollment.status) // "active"

Monitor enrollments

const page = await pigeons.sequences.enrollments(sequence.id, { status: 'active' })

Inboxes

Create an inbox

const inbox = await pigeons.inboxes.create({
  name: 'Support',
  emailAddress: 'support@yourdomain.com',
  webhookUrl: 'https://your-app.com/webhooks/email',
})

Inbox operations

await pigeons.inboxes.pause(inbox.id)
await pigeons.inboxes.resume(inbox.id)
await pigeons.inboxes.setCatchAll(inbox.id)
await pigeons.inboxes.delete(inbox.id)

Read messages

const messages = await pigeons.inboxes.messages.list(inbox.id, {
  direction: 'inbound',
  limit: 10,
})

const message = await pigeons.inboxes.messages.get(inbox.id, 'imsg_abc123...')

Send from an inbox

const sent = await pigeons.inboxes.messages.create(inbox.id, {
  to: 'customer@example.com',
  subject: 'Re: Your ticket',
  html: '<p>Thanks for reaching out.</p>',
  inReplyTo: 'imsg_previous...', // maintains thread
})

Threads

const threads = await pigeons.inboxes.threads.list(inbox.id, { status: 'open' })

const thread = await pigeons.inboxes.threads.get(inbox.id, 'thr_abc123...')

await pigeons.inboxes.threads.update(inbox.id, 'thr_abc123...', {
  status: 'closed',
})

Webhooks

const webhook = await pigeons.webhooks.create({
  url: 'https://your-app.com/webhooks/pigeons',
  events: ['email.delivered', 'email.bounced', 'inbox.message.received'],
})

// The secret is only returned on creation
console.log(webhook.secret) // "whsec_..."

Domains

const domain = await pigeons.domains.add({ domain: 'example.com' })
console.log(domain.records) // DNS records to add

// Re-check verification
const verified = await pigeons.domains.verify(domain.id)
console.log(verified.verificationStatus) // "verified"

Error Handling

All errors extend PigeonsError:

import { PigeonsError, NotFoundError, AuthError, RateLimitError } from '@pigeons/pigeons-ts'

try {
  await pigeons.emails.get('msg_nonexistent')
} catch (err) {
  if (err instanceof NotFoundError) {
    console.log(err.message)    // "Email 'msg_nonexistent' not found"
    console.log(err.statusCode) // 404
    console.log(err.code)       // "not_found"
    console.log(err.type)       // "not_found"
  }
}

Error classes:

ClassStatusWhen
PigeonsErrorvariesBase class for all errors
ValidationError400Invalid request parameters
AuthError401Missing or invalid API key
NotFoundError404Resource not found
RateLimitError429Rate limit exceeded