Skip to main content

Create The Client

Get an API key from brew.new/settings/api. Each key is bound to one brand at creation — pick the brand you want this client to act on before generating.
import { createBrewClient } from '@brew.new/sdk'

const brew = createBrewClient({
  apiKey: process.env.BREW_API_KEY!,
})

1. Upsert A Contact

const contactResult = await brew.contacts.upsert({
  email: 'john@example.com',
  firstName: 'John',
  lastName: 'Doe',
  subscribed: true,
  customFields: {
    plan: 'enterprise',
  },
})

console.log(contactResult.contact.email)
console.log(contactResult.created)
created: false means the contact already existed and was updated. Both outcomes are successful.

2. Browse Public Templates (optional)

const { data: templates } = await brew.templates.list({
  brand: 'vercel.com',
})
Templates are public reference emails. Pass any one as referenceEmailId to email generation when you want to anchor the output on its layout. Your own brand design context is automatic — every API key is bound to one brand at creation, and brew.emails.generate grounds itself in that brand without you ever passing a brandId.

3. Generate An Email

const generated = await brew.emails.generate({
  prompt: 'Create a welcome email for new subscribers',
  referenceEmailId: templates[0]?.emailId,
})

if ('emailId' in generated) {
  console.log(generated.emailId)
  console.log(generated.html)
} else {
  console.log(generated.response)
}

4. Edit A Saved Email (optional)

brew.emails.edit runs the agent against an existing email’s current latest version and persists a new version: "latest" row on the same emailId, demoting the previous head to a numeric historical version.
if ('emailId' in generated) {
  const edited = await brew.emails.edit({
    emailId: generated.emailId,
    prompt: 'Tighten the headline and add a friendlier sign-off.',
  })

  if ('emailId' in edited) {
    console.log(edited.emailId, edited.html)
  }
}
The brand is resolved from the API key. The emailId is a path parameter — neither brandId nor emailId may appear in the body.

4b. Import An Existing Email (optional)

Already have HTML or react-email JSX? brew.emails.import ingests it into an editable Brew design — external images are re-hosted on the CDN. It is usage-metered.
const imported = await brew.emails.import({
  format: 'html', // or 'mjml' / 'jsx'
  content: '<html><body><h1>Hello</h1></body></html>',
  title: 'Imported welcome',
})

console.log(imported.emailId, imported.emailVersionId)

5. List Verified Domains

const { data: domains } = await brew.domains.list()

if (domains.length === 0) {
  throw new Error('You need a verified sending domain before sending.')
}

6. Send An Email

brew.emails.send(...) delivers a design to a target — a saved brand-owned audienceId, an inline to list (≤ 50), or a single address — via a verified domain. It is not campaign-specific: send the same design as many times as you like. For per-recipient event-driven delivery — welcome flows, drip campaigns, transactional fires — chain brew.automations.triggers.fire(...) against a published automation graph instead (see the cookbook). Audience send example:
const { data: audiences } = await brew.audiences.list()

if ('emailId' in generated) {
  const sendResult = await brew.emails.send({
    emailId: generated.emailId,
    domainId: domains[0]!.domainId,
    audienceId: audiences[0]!.audienceId,
    subject: 'Welcome to Brew',
  })

  console.log(sendResult.status)
  console.log(sendResult.runId)
}
Scheduled send example (pin to a specific email version with emailVersionId):
await brew.emails.send({
  emailId: 'email_123',
  emailVersionId: 'emv_123_v2',
  domainId: 'domain_123',
  audienceId: 'audience_123',
  subject: 'Launch update',
  scheduledAt: '2099-01-01T00:00:00.000Z',
})
QA a design before the blast with the same send method — pass test: true and a single to address. It delivers synchronously from the Brew sender (no verified domain or audience needed) and returns { status: 'sent', recipient }:
const test = await brew.emails.send({
  test: true,
  emailId: 'email_123',
  subject: 'Welcome to Brew',
  to: 'you@example.com',
})

console.log(test.recipient)

Handle Errors

import { BrewApiError } from '@brew.new/sdk'

try {
  await brew.domains.list()
} catch (error) {
  if (error instanceof BrewApiError) {
    console.error(error.code, error.message, error.requestId)
  }
  throw error
}

Next Steps

Resource Surface

See the current TypeScript SDK resources and methods.

API Reference

See the raw HTTP contract behind the SDK.

Need Help?

Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:

Search Documentation

Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.

ChatGPT/Claude Integration

Click “Open in ChatGPT” at the top right of any page to analyze documentation with ChatGPT or Claude for deeper insights.