> ## Documentation Index
> Fetch the complete documentation index at: https://docs.brew.new/llms.txt
> Use this file to discover all available pages before exploring further.

# API Introduction

> How to authenticate, generate emails, manage contacts, and send campaigns using the Brew Public API v1. Covers base URL, API keys, permission scopes, brand scoping, and a working quickstart.

## Overview

The Brew API lets you build email programs without touching the dashboard. Generate emails, manage contacts, fire automations, and send campaigns, all from your backend.

The surface is organized around four ideas:

1. **Emails are pure designs.** A design carries no send state and
   no type. Generate one, edit it, restore old versions, then send
   it as many times as you like.
2. **A send is the unit of delivery + analytics.** Every send
   (campaign or automation) is one `sendId`, the thing you poll for
   status and the key every delivery event attaches to.
3. **Analytics is read-only reporting** derived from sends + their
   per-recipient events.
4. **Automations** wire a trigger to a graph of `sendEmail` / `wait`
   / `filter` / `split` nodes that deliver per-recipient when the
   trigger fires.

Use it to:

* Generate, import, and edit emails with AI (`/v1/emails`).
* Send an email to a saved audience, an inline recipient list, or a
  single address (`POST /v1/sends`), and QA it first with the same
  endpoint and `{ test: true }`.
* Read delivery + engagement analytics
  (`/v1/analytics/sends`, `/v1/analytics/campaigns`,
  `/v1/analytics/automations`, `/v1/analytics/events`).
* Define event triggers (`/v1/automations/triggers`), assemble
  automation graphs (`/v1/automations`), publish them, and fire them
  (`/v1/automations/triggers/{triggerEventId}/fire`).
* Manage contacts, custom fields, audiences, and domains.

Brand management lives in the Brew dashboard. The public API does
not expose endpoints to create, list, or edit brands. **Each API key
is bound to exactly one brand at creation time and can never be
retargeted later.** A brand can have any number of keys (dev,
staging, production, per-service, per-teammate); each one acts on
the same single brand it was created against. Need a key for a
different brand? Switch brands in the dashboard and create a new
key.

Create and manage your API keys at
[brew.new/settings/api](https://brew.new/settings/api).

The full endpoint reference in this docs site is generated from
Brew's OpenAPI source of truth. The human-written overview on this
page explains the flow; the generated endpoint pages explain the
exact request and response contract.

## Base URL

```bash theme={null}
https://brew.new/api
```

All current public endpoints are under the `/v1` prefix.

## Discover the API Programmatically

Three no-auth discovery endpoints let an agent learn the whole
surface before it has a key:

| Endpoint           | Returns                                                                                                                                               |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /v1/help`     | A structured-JSON catalog: auth, scopes, rate limits, credit costs, the error envelope, and the full endpoint list. Built for MCP servers and agents. |
| `GET /v1/llms.txt` | The long-form prose agent guide (the same surface as `/v1/help`, written for an LLM to read once).                                                    |
| `GET /v1/health`   | Liveness: `{ status, version }`.                                                                                                                      |

`GET /v1/help` and `GET /v1/llms.txt` are the machine + prose mirror
of this page; keep them handy when wiring an agent.

## Authentication

Every request needs a Brew API key. Create and manage keys at
[brew.new/settings/api](https://brew.new/settings/api).

You can send it in either header:

```bash theme={null}
Authorization: Bearer brew_your_api_key
```

```bash theme={null}
X-API-Key: brew_your_api_key
```

<Warning>
  Keep API keys on your server only. Do not put them in browser code.
</Warning>

Two endpoints bootstrap a new key:

* `GET /v1/brand` → `{ brand }` is the brand the key is pinned to.
  Check `ready` before generating emails.
* `GET /v1/usage` → `{ plan, credits, emailSends, period }` is the
  billing + quota surface (credit balance and send allowance).

### Brand Scoping

Every API key is bound to **exactly one brand** at creation time.
The brand is resolved from the key on every request: **no public
endpoint accepts a `brandId` field** in its request body or query
string. To operate on a different brand, switch brands in the
dashboard at
[brew.new/settings/api](https://brew.new/settings/api) and create a
new key for that brand.

Reads filter to the key's brand automatically; mutations write only
to the key's brand. Cross-brand identifiers surface as `404` (not
`403`) so the API never confirms the existence of resources in
another brand. The only organization-wide resource is the public
template gallery (`GET /v1/templates`).

If you send a `brandId` field (body or query) to any `/v1/*`
endpoint, the request fails with `400 INVALID_REQUEST` and
`param: "brandId"`. Strip the field; the brand always comes from the
key.

### Permission Scopes

Each API key carries a list of permission scopes. The dashboard
defaults new keys to `all`. The coarse scopes **imply** the granular
ones: `contacts` ⊇ `audiences`; `emails` ⊇ `domains` + `sends`.
Mint a least-privilege key when you only need one resource.

| Permission    | Endpoints                                                                                                                                            |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contacts`    | `/v1/contacts*`, `/v1/fields*` (also satisfies `audiences`)                                                                                          |
| `emails`      | `/v1/emails*`, `/v1/content/*`, `/v1/templates*`, `/v1/brand*`, `/v1/usage`, `/v1/analytics/{campaigns,events}` (also satisfies `domains` + `sends`) |
| `automations` | `/v1/automations*` (incl. `/triggers*`, `/runs*`), `/v1/analytics/automations*`, `/v1/analytics/trigger-instances*`                                  |
| `audiences`   | `/v1/audiences*`                                                                                                                                     |
| `domains`     | `/v1/domains*`                                                                                                                                       |
| `sends`       | `/v1/sends`, `/v1/analytics/sends`                                                                                                                   |
| `all`         | every endpoint above                                                                                                                                 |

Sending a request with a key that lacks the required permission
returns `403 INSUFFICIENT_PERMISSIONS` with the missing scope name
in the error envelope's `param` field. See
[Authentication](/api-reference/api/authentication) for the per-route
scope table.

## How the API Fits Together

The surface is built around two delivery modes:

1. **One-shot campaigns**: generate (or pick) a design, then
   `POST /v1/sends` with either a brand-owned `audienceId` or an
   inline `to` list (≤ 50). The workflow runtime fans out to every
   recipient and mints one `sendId` you poll for status + stats.
2. **Event-driven automations**: define a `trigger`, generate the
   design bodies, assemble an `automation` graph that wires the
   trigger to one or more `sendEmail` nodes, publish it, and fire
   `POST /v1/automations/triggers/{triggerEventId}/fire` from your
   backend whenever the real event happens. The runtime delivers
   per-recipient, **one `sendId` per recipient**.

Both modes write rows to the same `sends` entity, discriminated by a
`kind` of `campaign` or `automation`. Analytics reads from there.

```
design ──► send ──► analyze ──► automate

1. DESIGN   POST /v1/emails { prompt }
2. SEND     POST /v1/sends { emailId, domainId, subject, audienceId | to }
            POST /v1/sends { test: true, emailId, subject, to }   (one-off QA)
3. ANALYZE  GET  /v1/analytics/sends?sendId=…                 → status + stats
            GET  /v1/analytics/sends?sendId=…&include=events  → per-recipient feed
            GET  /v1/analytics/campaigns                      → per-send lifetime KPIs
4. AUTOMATE POST  /v1/automations/triggers                    → mint a trigger
            POST  /v1/automations { nodes, connections }
            PATCH /v1/automations/{automationId} { published: true }
            POST  /v1/automations/triggers/{triggerEventId}/fire { payload }
            GET  /v1/automations/runs?automationRunId=…&include=logs  → run + per-node logs
```

## Resource Reference

The surface lives in three top-level domains plus the supporting
resources (emails, contacts, audiences, domains, brand, templates).

### Emails: `/v1/emails`

AI-generated email designs, plus the action that delivers them. A
design carries **no type and no send state**: any design in the
brand can be sent to a target or referenced by an automation
`sendEmail` node, any number of times.

* `POST /v1/emails`: generate from `{ prompt, contentUrls?,
  referenceEmailId? }`. Returns `201 { emailId, emailVersionId, html,
  previewImage? }`, or `200 { response }` when the agent answered in
  prose. **Credit-metered.**
* `POST /v1/emails/import`: ingest existing markup
  `{ format: 'html' | 'mjml' | 'jsx', content, title?, baseUrl? }` into an
  editable design (external images are re-hosted on the CDN). Returns
  `201 { emailId, emailVersionId, html, previewImage? }`.
  **Usage-metered.**
* `POST /v1/emails/figma`: transpile a Figma frame into an editable design
  `{ figmaUrl, title?, format?: 'jsx' | 'html' }`. The `figmaUrl` must include
  a `node-id`, i.e. the link to one frame. It requires the API-key brand's
  connected Figma integration; credentials never travel in the request.
  Returns `201 { emailId, emailVersionId, title, format, content, warningCount,
  exportedNodeCount, previewImage? }`. Deterministic, so no model runs and it
  is **free**.
* `GET /v1/emails`: list designs (filter `status`,
  `createdAtFrom/To`, `updatedAtFrom/To`). Pass `?emailId=` for one
  design + its rendered `html`; the row already carries `previewImage`.
  Add `?include=html,versions` to embed the rendered HTML and the
  version history.
* `PATCH /v1/emails/{emailId}`: AI edit `{ prompt, emailVersionId? }`
  → new latest version (usage-metered).
* `DELETE /v1/emails/{emailId}`: delete every version.
* `POST /v1/emails/{emailId}/restore`: `{ version }` clones a
  numbered version into a new latest.
* `POST /v1/emails/{emailId}/accessibility-audit`: WCAG 2.1 audit
  (fixed credit cost).
* `POST /v1/emails/{emailId}/client-previews`: render the design in
  real inboxes/devices (Gmail, Outlook, Apple Mail, iOS, light & dark)
  → a screenshot per client. Fixed credit cost, charged only when at
  least one screenshot is produced.

There is no longer a get-one, versions, preview, or per-design sends
endpoint: the design read carries `previewImage`, `?include=versions`
returns history, on-demand rendering is `POST /v1/content/html-to-png`,
and a design's send history is `GET /v1/analytics/sends?emailId=`.

**Sending (`/v1/sends`).** Delivering a design is action-only: it
starts a delivery; the reads live under Analytics. Sending is not
campaign-specific: send the same design to a saved audience, an
inline list, or a single address, as many times as you like.

* `POST /v1/sends`: polymorphic by `test`. A **campaign** send
  `{ emailId, emailVersionId?, domainId, subject, previewText?,
  replyTo?, (audienceId | to), scheduledAt? }` provides exactly one of
  `audienceId` (a brand-owned saved audience) or `to` (an inline email
  or array of ≤ 50) and returns `202 { status: 'queued' | 'scheduled',
  sendId, runId, scheduledAt? }`. A **test** send `{ test: true,
  emailId, emailVersionId?, subject, previewText?, to, replyTo? }` does
  a one-off `[TEST]` delivery to a single inbox, forced Brew sender,
  no verified domain, audience, or send row, and resolves
  synchronously → `200 { status: 'sent', recipient }`.
* `POST /v1/sends/{sendId}/cancel`: cancel a scheduled or queued send
  before it goes out → `200 { sendId, status: 'canceled' }`. Idempotent
  (an already-`canceled` send returns `200`); once the send is
  `sending`, `sent`, or `failed` it is `409 SEND_NOT_CANCELLABLE`, and
  an unknown / cross-brand id is `404 SEND_NOT_FOUND`.

### Analytics: `/v1/analytics`

Every analytics read is `emails`-scoped except the automation +
trigger feeds, which are `automations`-scoped.

* `GET /v1/analytics/sends`: the single send read. List every send
  (filter `status`, `from`, `to`), narrow to one design with
  `?emailId=`, or fetch one send with `?sendId=`; poll its lifecycle
  `status` + aggregated `stats`. Add `?include=events` to inline the
  per-recipient event feed (`sent` / `delivered` / `opened` /
  `clicked` / `bounced` / `complained` / `unsubscribed`).
* `GET /v1/analytics/campaigns`: lifetime KPIs per campaign send
  (one row per `sendId`).
* `GET /v1/analytics/automations`: windowed per-automation
  performance + totals (default last 30 days). `automations` scope.
* `GET /v1/analytics/events`: unified event feed across domains
  (filter `recipientEmail`, `eventType`, `automationId`, `sendId`,
  `from`, `to`).
* `GET /v1/analytics/trigger-instances`: audit log of fired trigger
  instances (filter `?triggerEventId=`, or `?triggerInstanceId=` for
  one fired-event row: state, attempts, matched automations, started
  runs). `automations` scope.

### Automations: `/v1/automations`

Versioned graphs of nodes (trigger / sendEmail / wait / filter /
split). The public surface is **deterministic**: every body carries
the explicit `{ nodes, connections }` graph.

* `POST /v1/automations`: create
  `{ name, description?, triggerEventId, nodes, connections, dryRun? }`.
  `dryRun: true` validates without writing.
* `GET /v1/automations`: list (lean rows; graph omitted). Pass
  `?automationId=` for one automation, and `?include=graph,versions`
  to embed the full `{ nodes, connections }` graph and its version
  history.
* `PATCH /v1/automations/{automationId}`: update `name` /
  `description` / `nodes` / `connections` / `triggerEventId`, **or**
  flip the lifecycle with `{ published }`. `{ published: true,
  automationVersionId? }` validates the graph and goes live
  (`409 PUBLISH_VALIDATION_FAILED` if it fails); `{ published: false }`
  stops matching fires (`422 AUTOMATION_NOT_PUBLISHED` if it was never
  published). `published` is **mutually exclusive** with any field /
  graph update. Send it on its own.
* `DELETE /v1/automations/{automationId}`: cascade delete (versions
  * runs + logs; the referenced designs survive).
* `POST /v1/automations/{automationId}/test`: suppression-aware test
  run `{ payload? }` → `202`.

Every `sendEmail` node requires `emailId`, `emailVersionId`,
`domainId`, `subject`, `previewText`. The server-side graph resolver
(`AUTOMATION_GRAPH_INVALID`) verifies each foreign key + structural
constraint before any write. **Each `sendEmail` node creates one
`sends` row per recipient that flows through it.**

### Triggers: `/v1/automations/triggers`

Brand-scoped event definitions. A trigger is a `payloadSchema`
contract plus a stable `triggerEventId`. Triggers created via this
resource are hardcoded to `provider: 'brew_api'`; integration
triggers (clerk, stripe, shopify, …) are provisioned by the
corresponding integration and listed here read-only.

* `POST /v1/automations/triggers`: create
  `{ title, description?, payloadSchema }` (no `provider` /
  `providerEventKey`. Those are rejected).
* `GET /v1/automations/triggers`: list every trigger; pass
  `?triggerEventId=` for one trigger row.
* `PATCH /v1/automations/triggers/{triggerEventId}`: update
  `title` / `description` / `payloadSchema`. Triggers have no status
  field; whether one fires is gated by the bound automation being
  published.
* `DELETE /v1/automations/triggers/{triggerEventId}`: delete
  (refused with `409 TRIGGER_HAS_DEPENDENT_AUTOMATIONS` while a
  non-archived automation references it).
* `POST /v1/automations/triggers/{triggerEventId}/fire`: fire
  `{ payload, idempotencyKey? }`. Starts every published automation
  on the trigger. This is the **one** endpoint that returns the
  legacy fire envelope. Read run ids from `details.automationRunIds`.

### Automation Runs: `/v1/automations/runs`

Read-only view of the workflow runs created by firing a trigger or
test-running an automation.

* `GET /v1/automations/runs`: list (filter `automationId`,
  `triggerEventId`, `triggerInstanceId`, `recipientEmail`, `status`,
  `mode`, `from`, `to`). Pass `?automationRunId=` for one run, and
  `?include=logs` to attach its per-node `logs[]`.

### Contacts + Fields: `/v1/contacts`, `/v1/fields`

Contacts are keyed on `email` and live under one brand; `customFields`
columns are declared via `/v1/fields`. The contact **read** is a single
endpoint, `POST /v1/contacts/search` `{ filters, audienceId?, search?,
sort, count?, cursor }`, and a by-email lookup is just a
`{ field: 'email', operator: 'equals', value }` filter. Writes are
path-based: `POST /v1/contacts` upserts a single contact or a batch
(≤ 1000), and `PATCH` / `DELETE /v1/contacts/{email}` update or remove
one. Contacts also offer deliverability validation
(`POST /v1/contacts/validate`), CSV import (`POST /v1/contacts/import-csv`),
and batch delete (`POST /v1/contacts/batch-delete`).

`POST /v1/contacts/validate` **writes the verdict back** onto matching
contacts (it is no longer read-only), and both the upsert (`POST /v1/contacts`)
and CSV import accept an optional `validate: true` to deliverability-check each
address as it's ingested, 2 credits per address charged only on success. Up to
100 addresses validate inline and the response carries a `validation` count
summary; larger submissions upsert first and validate as a background job,
returning a `validationJobId`. On the contact itself the verdict field is
`validationStatus` (`valid` / `risky` / `invalid`); the legacy
`verificationStatus` is still dual-emitted with the same value for back-compat.

### Audiences + Domains: `/v1/audiences`, `/v1/domains`

Brand-owned saved audiences (full CRUD; read one with
`GET /v1/audiences?audienceId=`, and add `&include=count` to make the
row's `count` field the live total) and sending domains (list with
`GET /v1/domains`, one with `?domainId=`, `?sendableOnly=true` to
filter to verified senders; add → verify → set sender defaults →
delete).

### Brand: `/v1/brand`

Read the key's brand (`GET /v1/brand`, check `ready`) and the design
system the agent follows. Sub-resources fold into the brand row:
`GET /v1/brand?include=identity,emailDesign,imageStyle,logos` expands
the requested facets inline, and `PATCH /v1/brand { identity?,
emailDesign?, imageStyle? }` writes them back. The brand image library
is `GET /v1/brand/images`, dual-mode: pass `?q=` for credit-metered
semantic search (also `?type=` / `?aspectRatio=`), or omit it to browse
the library for free.

### Templates: `/v1/templates`

Public template gallery. `GET /v1/templates` returns full rows:
each carries `html`, `previewImage`, `title`, `category`, `brand`,
and `updatedAt`. Filter on `brand`, `category`, `semantic`. Pass a
template's `emailId` as `referenceEmailId` to `POST /v1/emails` to
seed generation.

## Quick Start

<Steps>
  <Step title="Get an API key">
    Go to [brew.new/settings/api](https://brew.new/settings/api) and
    create a key. The key is bound to whatever brand is active in the
    dashboard at creation time, so switch brands first if you want a
    key for a different brand.
  </Step>

  <Step title="Check that auth works">
    Read the brand pinned to the key:

    ```bash theme={null}
    curl -H "Authorization: Bearer brew_your_api_key" \
      https://brew.new/api/v1/brand
    ```
  </Step>

  <Step title="Generate a design">
    Create a saved design from a prompt:

    ```bash theme={null}
    curl -X POST "https://brew.new/api/v1/emails" \
      -H "Authorization: Bearer brew_your_api_key" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: email-generate-001" \
      -d '{ "prompt": "Create a welcome email for new subscribers" }'
    ```
  </Step>

  <Step title="Send to an audience">
    Use the returned `emailId`, a verified `domainId`, and a saved
    `audienceId`:

    ```bash theme={null}
    curl -X POST "https://brew.new/api/v1/sends" \
      -H "Authorization: Bearer brew_your_api_key" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: send-001" \
      -d '{
        "emailId": "eml_123",
        "domainId": "dom_123",
        "audienceId": "aud_123",
        "subject": "Welcome to Brew"
      }'
    ```

    Then poll `GET /v1/analytics/sends?sendId=…` for status + stats.
  </Step>

  <Step title="Send it">
    List your verified domains and saved audiences, then `POST /v1/sends`
    with the `emailId` from step 3:

    ```bash theme={null}
    curl -H "Authorization: Bearer brew_your_api_key" \
      https://brew.new/api/v1/domains

    curl -H "Authorization: Bearer brew_your_api_key" \
      https://brew.new/api/v1/audiences

    curl -X POST "https://brew.new/api/v1/sends" \
      -H "Authorization: Bearer brew_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "emailId": "email_123",
        "domainId": "domain_123",
        "audienceId": "audience_123",
        "subject": "Welcome to Brew"
      }'
    ```
  </Step>

  <Step title="(or) Fire an automation">
    Build an automation graph against a trigger, publish it, then
    fire it from your backend whenever the event happens:

    ```bash theme={null}
    curl -X POST "https://brew.new/api/v1/automations/triggers/tri_user_signup/fire" \
      -H "Authorization: Bearer brew_your_api_key" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: signup-jane@example.com-1779292800" \
      -d '{
        "payload": { "email": "jane@example.com", "firstName": "Jane" }
      }'
    ```
  </Step>
</Steps>

## Current Public v1 Surface

| Method                              | Endpoint                                         | Purpose                                                                                                     |
| ----------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| `POST`                              | `/v1/emails`                                     | Generate a design (credit-metered).                                                                         |
| `POST`                              | `/v1/emails/import`                              | Import HTML / MJML / JSX into an editable design (usage-metered).                                           |
| `POST`                              | `/v1/emails/figma`                               | Convert a Figma frame into an editable design (deterministic, free).                                        |
| `GET`                               | `/v1/emails`                                     | List designs; `?emailId=` for one, `?include=html,versions`.                                                |
| `PATCH` / `DELETE`                  | `/v1/emails/{emailId}`                           | AI-edit / delete a design.                                                                                  |
| `POST`                              | `/v1/emails/{emailId}/client-previews`           | Render across real inboxes/devices: a screenshot per client (fixed cost, billed only when ≥1 renders).      |
| `POST`                              | `/v1/sends`                                      | Send an email to an audience, inline `to`, or single address; `{ test: true }` for a one-off `[TEST]` send. |
| `POST`                              | `/v1/sends/{sendId}/cancel`                      | Cancel a scheduled / queued send (idempotent); `409` once it is sending / sent / failed.                    |
| `POST`                              | `/v1/sends/{sendId}/pause`                       | Pause an in-flight gradual send, halting the ramp; reversible.                                              |
| `POST`                              | `/v1/sends/{sendId}/resume`                      | Resume a paused gradual send.                                                                               |
| `GET`                               | `/v1/analytics/sends`                            | List / fetch sends (`?sendId`, `?emailId`, `?include=events`).                                              |
| `GET`                               | `/v1/analytics/campaigns`                        | Lifetime per-campaign-send KPIs.                                                                            |
| `GET`                               | `/v1/analytics/automations`                      | Per-automation performance.                                                                                 |
| `GET`                               | `/v1/analytics/events`                           | Unified event explorer.                                                                                     |
| `GET`                               | `/v1/analytics/trigger-instances`                | Fired-trigger audit log (`?triggerInstanceId` for one).                                                     |
| `POST` / `GET`                      | `/v1/automations`                                | Create / list automation graphs (`?automationId`, `?include=graph,versions`).                               |
| `PATCH` / `DELETE`                  | `/v1/automations/{automationId}`                 | Update / cascade-delete. `PATCH { published }` publishes or unpublishes.                                    |
| `POST`                              | `/v1/automations/{automationId}/test`            | Suppression-aware test run.                                                                                 |
| `GET`                               | `/v1/automations/runs`                           | Read runs (`?automationRunId`, `?include=logs`).                                                            |
| `POST` / `GET`                      | `/v1/automations/triggers`                       | Create / list triggers (`?triggerEventId` for one).                                                         |
| `PATCH` / `DELETE`                  | `/v1/automations/triggers/{triggerEventId}`      | Update / delete a trigger.                                                                                  |
| `POST`                              | `/v1/automations/triggers/{triggerEventId}/fire` | Fire a trigger.                                                                                             |
| `POST`                              | `/v1/contacts/search`                            | The contact read (filters, `audienceId`, `search`, `count`).                                                |
| `GET` / `POST` / `PATCH` / `DELETE` | `/v1/contacts*`                                  | Contact writes, validate, import, batch-delete.                                                             |
| `GET` / `POST` / `DELETE`           | `/v1/fields*`                                    | Custom field definitions.                                                                                   |
| `GET` / `POST` / `PATCH` / `DELETE` | `/v1/audiences*`                                 | Audience CRUD (`?include=count`).                                                                           |
| `GET` / `POST` / `PATCH` / `DELETE` | `/v1/domains*`                                   | Domain lifecycle + verify (`?sendableOnly`).                                                                |
| `GET`                               | `/v1/templates`                                  | Public template gallery (full rows).                                                                        |
| `GET` / `PATCH`                     | `/v1/brand*`                                     | Brand + design-system reads (`?include=…`) and writes.                                                      |
| `GET`                               | `/v1/usage`                                      | Billing: plan, credits, send quota.                                                                         |
| `POST`                              | `/v1/content/*`                                  | Credit-metered media + render ops.                                                                          |
| `GET`                               | `/v1/help`, `/v1/llms.txt`, `/v1/health`         | No-auth discovery + liveness.                                                                               |

## Idempotency

`POST` endpoints support idempotency via the `Idempotency-Key` header
(≤ 100 chars). Replays with the **same body** return the cached
response for 24 h; replays with a **different** body return
`409 IDEMPOTENCY_CONFLICT`.

`POST /v1/automations/triggers/{triggerEventId}/fire` also
honors a body `idempotencyKey` field for legacy back-compat. Prefer
the header.

Use it on every `POST` your code might retry, especially the fire
endpoint, so a double-fired webhook doesn't double-deliver. See
[Idempotency](/api-reference/api/idempotency) for the full contract.

## Credits

Credit-metered routes use two modes. **Usage-metered** ops:
`POST /v1/emails`, `POST /v1/emails/import`, AI edits via
`PATCH /v1/emails/{emailId}`, `GET /v1/brand/images?q=` (semantic
search), and `POST /v1/content/generate-image`, charge the **actual**
model usage (tokens / image cost), with no fixed number and no
`X-Credit-Cost` header. **Fixed** ops, the other `/v1/content/*`
operations and `POST /v1/emails/{emailId}/client-previews`, charge a
flat, published cost reported via `X-Credit-Cost`
/ `X-Credits-Remaining` headers. Both gate
on balance: a call against an empty balance returns
`402 INSUFFICIENT_CREDITS`. Check your balance up front with
`GET /v1/usage`. See [Credits](/api-reference/api/credits).

## Rate Limits and Debugging Headers

Every response carries:

* `x-request-id`: opaque correlator. Always include this when you
  contact support.
* `X-RateLimit-Limit`: requests allowed in the current 60s window.
* `X-RateLimit-Remaining`: requests left.
* `X-RateLimit-Reset`: unix epoch seconds when the window resets.

`429 RATE_LIMITED` also sets `Retry-After: <seconds>`. Limits
are per API key, per route, per 60s window. UI-backed session traffic
gets a `300/min` ceiling across the board.

See [Rate limits](/api-reference/api/rate-limits) for the full
per-route policy table and a `429` recovery cookbook, and
[Response headers](/api-reference/api/response-headers) for every
header Brew sets.

## TypeScript SDK

If you prefer typed wrappers over raw HTTP, Brew also ships an
official TypeScript SDK at `@brew.new/sdk`. Every resource has a
matching client method (`brew.emails.generate`, `brew.emails.send`,
`brew.analytics.sends.list`, `brew.automations.create`,
`brew.automations.triggers.fire`, …).

<CardGroup cols="2">
  <Card title="TypeScript SDK" icon="js" href="/sdks/overview">
    Use `@brew.new/sdk` for typed requests, retries, idempotency, and a
    resource-oriented client surface.
  </Card>

  <Card title="Agentic Cookbook" icon="robot" href="/sdks/typescript/agentic-cookbook">
    End-to-end recipes for AI agents wiring designs → sends →
    analytics → automations.
  </Card>
</CardGroup>

## What to Read Next

<CardGroup cols="2">
  <Card title="Public API v1" icon="book-open" href="/api-reference/public-v1/contacts/get-contacts">
    Browse every generated endpoint page from the current OpenAPI spec.
  </Card>

  <Card title="SDK Overview" icon="terminal" href="/sdks/overview">
    Start with the official TypeScript SDK.
  </Card>
</CardGroup>

## 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:

<Tabs>
  <Tab title="Self-Service Tools">
    <CardGroup cols="2">
      <Card title="Search Documentation" icon="magnifying-glass" color="#c44925">
        Type in the "Ask any question" search bar at the top left to instantly find relevant documentation pages.
      </Card>

      <Card title="ChatGPT/Claude Integration" icon="robot" color="#c44925">
        Click "Open in ChatGPT" at the top right of any page to analyze documentation with ChatGPT or Claude for deeper insights.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="Talk to Our Team">
    <CardGroup cols="2">
      <Card title="Schedule a Call" icon="calendar" color="#c44925" href="https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1iYoRUG1J792XQpbuQLjSRRDupr7MwraFK-HQRCtTYdBmrQi8nZu2qXfzKQigb8gbKJK3KN3-R">
        Book time with our founders for personalized guidance on strategy, best practices, or complex implementation questions.
      </Card>

      <Card title="Call Us Directly" icon="phone" color="#c44925">
        Need immediate assistance? Reach us at **+1-(332)-203-2145** for urgent issues or time-sensitive questions.
      </Card>

      <Card title="Slack Channel" icon="slack" color="#c44925">
        Our preferred support channel. You'll receive an invite after signup for direct founder support and fast responses.
      </Card>

      <Card title="Email Support" icon="envelope" color="#c44925" href="mailto:support@brew.new">
        Contact us at **[support@brew.new](mailto:support@brew.new)** for detailed inquiries or if you prefer not to use Slack.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
