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

# Get an email design

> Reads one design as the bare `EmailDetail` row: `emailId`, the `emailVersionId` (the pin for `POST /v1/sends` and sendEmail nodes), `title`, `status`, `previewStatus`, `subjectLine`, `previewText`, `previewImage`, `updatedAt`, `group` and `createdBy` / `createdByUserId` (the design’s creator, shared by every version), plus `errorMessage` when a generation failed.

**Use when** inspecting a design before sending it, pinning a version, polling a generation you started, or pulling rendered HTML or version history.

**Input** `emailId` in the path. By default the row is the current head; `emailVersionId` reads a saved version and `runId` (returned by a create or edit) reads the version that run produced; pass one or neither, never both. `include=html` adds the selected version’s rendered HTML once it is `ready`, `include=versions` the lean `{ version, emailVersionId }` history, `include=text` the visible body text a reader sees (merge tags kept; `textTruncated` past 20,000 characters), and `include=links` each link destination once with its first visible text and `count` (`linksTruncated` past 200); comma-separate any of them.

**Returns** `200` with the row; the `include` fields are present only when requested. A run that never saved a version reads as `failed` with the reason, and the design keeps its last saved version.

**Errors** `404 EMAIL_NOT_FOUND` for an unknown or cross-brand id; `404 EMAIL_VERSION_NOT_FOUND` for a version or run this design does not have; `409 EMAIL_RUN_AMBIGUOUS` when a legacy run matches more than one version (read it by `emailVersionId`); `503 SERVICE_UNAVAILABLE` when storage is briefly unreachable (retry the same read); `400 INVALID_REQUEST` for an unknown `include` token or both selectors.

**See also** `listEmails`, `editEmail`, `createSend`, `restoreEmailVersion`.



## OpenAPI

````yaml /api-reference/openapi-public-v1.yaml get /v1/emails/{emailId}
openapi: 3.1.0
info:
  title: Brew Public API v1
  version: 1.0.0
  description: >-
    Brew Public API v1. Generated from the app Zod contracts
    (`lib/<domain>/contracts.ts`): this document is the contract, and every
    operation documents exactly the error codes it can return.


    - Base URL `https://brew.new/api`; every path starts with `/v1`; JSON in and
    out. Request bodies and query strings are strict: an unknown key is `400
    INVALID_REQUEST` and `error.param` names it.

    - Identity is always in the path. `GET /v1/<collection>` lists (`{ data,
    pagination: { limit, cursor, hasMore } }`; page until `cursor` is `null`),
    and `GET /v1/<collection>/{id}` returns the bare resource, the same object a
    write returns. `?include=` expands relations on a detail read only, and is
    `400` on a list. There is no `?<idKey>=` read anywhere.

    - Writes carry identity in the path and return the bare resource: `POST
    /v1/<collection>` creates (`201`, or `202` when work continues
    asynchronously), `PATCH` and `DELETE /v1/<collection>/{id}` update or delete
    (`DELETE` answers `{ <idField>, deleted }` and is idempotent). Lifecycle
    state changes such as publishing ride `PATCH` as attributes. Two writes
    report more than the row and say so in their shape: `POST` and `PATCH
    /v1/contacts` are upserts that answer `{ contact, created | updated, … }`
    because whether a row was created, which fields changed, and which field
    definitions were minted are facts about the write, not about the contact.

    - Non-CRUD operations are explicit action sub-paths, for example `POST
    /v1/automations/{automationId}/test`, `POST /v1/domains/{domainId}/verify`,
    `POST /v1/sends/{sendId}/cancel` and `POST
    /v1/automations/triggers/{triggerEventId}/fire`.

    - Every resource has one root. Sends read and write at `/v1/sends`, fired
    triggers at `/v1/automations/trigger-instances`, and `/v1/analytics/*` holds
    only reports (aggregates over a window). Every endpoint answers in the same
    envelopes, with no exceptions.

    - Field names are camelCase; enum values are lowercase snake_case;
    timestamps are ISO 8601 UTC strings. Identifiers are opaque strings of at
    most 64 characters (`triggerEventId` up to 256); never parse a prefix.

    - Everything that runs shares one status vocabulary: `queued`, `scheduled`,
    `running`, `paused`, `completed`, `partially_completed`, `failed`,
    `canceled`. It covers sends, automation runs, manual audience runs, audience
    builds and inbox placement tests, so one status switch reads them all. A
    step inside a run is `running`, `completed`, `failed` or `skipped`.

    - Every error on every endpoint is `{ error: { code, type, message,
    suggestion, docs, param?, retryAfter?, details? } }`. Branch on `code`: the
    closed list is the `ApiErrorCode` component and every operation documents
    exactly the codes it can return. Non-fatal caveats arrive as `warnings: [{
    code, message, field? }]` on `2xx` bodies.

    - A credential is brand-scoped (the brand is implicit; send nothing) or
    organization-scoped (name the brand with the `X-Brand-Id` header on
    brand-scoped operations, else `400 BRAND_ID_REQUIRED`; there is no default
    brand). Only `POST /v1/api-keys` carries a `brandId` field, and only a
    signed-in organization admin session may call `/v1/api-keys`. A brand
    outside your reach surfaces as `404`, never `403`.

    - Send `Idempotency-Key` (up to 100 characters) on any POST you might
    repeat: the same key and body replays the original response for 24 hours;
    the same key with a different body is `409 IDEMPOTENCY_CONFLICT`. Rate
    limits are per credential and named policy (`X-RateLimit-*` headers; `429`
    carries `Retry-After`). Credit-metered operations advertise `402` and carry
    `x-brew-credited: true` in the spec.

    - Discovery without a key: `GET /v1/help` (JSON catalog with scopes, credits
    and rate limits per operation), `GET /v1/llms.txt` (this guide) and `GET
    /v1/health`. The OpenAPI document is served at `/openapi.json`.
  contact:
    name: Brew Support
    url: https://docs.brew.new
    email: support@brew.new
servers:
  - url: https://brew.new/api
    description: Production
  - url: http://localhost:3000/api
    description: Local development
security:
  - bearerAuth: []
  - apiKeyAuth: []
tags:
  - name: Emails
    description: >-
      Email designs and sending. Generate a design with the Brew email agent,
      edit, version, restore — then send it: `POST /v1/sends` delivers a design
      to a target (a saved audience, an inline list, or a single address) via a
      verified domain, and `POST /v1/sends` with `test: true` fires a one-off
      test. Sending is not campaign-specific. Send reads live under Sends
      (`/v1/sends`).
  - name: Sends
    description: >-
      The unit of delivery and analytics. `POST /v1/sends` delivers a design to
      a target; `GET /v1/sends` lists campaign sends (with lifetime stats) or,
      with a join filter, one automation’s per-recipient deliveries; `GET
      /v1/sends/{sendId}` reads one send and its events. Cancel, pause and
      resume are action sub-paths.
  - name: Brands
    description: >-
      Brand lifecycle for ORGANIZATION-scoped credentials: list the brands a
      credential can reach, read one, and create a new one (extraction runs
      asynchronously — poll `GET /v1/brands/{brandId}` until `status:
      completed`). These endpoints act on the organization, so they take no
      `X-Brand-Id`.
  - name: Analytics
    description: >-
      Read-only cross-resource reports: the brand overview, windowed automation
      performance and the unified event feed. Send rows (with lifetime stats)
      live at `/v1/sends`; fired triggers at
      `/v1/automations/trigger-instances`.
  - name: Automations
    description: >-
      Automation graphs — deterministic create from explicit `nodes` +
      `connections`, update, version, publish / unpublish, test. Includes
      trigger event definitions + the fire endpoint (`/v1/automations/triggers`)
      and run history (`/v1/automations/runs`).
  - name: Contacts
    description: Create, search, patch, and delete contacts. Email is the primary key.
  - name: Contact Fields
    description: List, create, and delete custom contact field definitions.
  - name: Audiences
    description: Saved contact filter sets — a recipient target for sends.
  - name: Domains
    description: 'Sending domains: add, read DNS records, verify, configure sender defaults.'
  - name: Templates
    description: Public template gallery (read-only) usable as generation references.
  - name: Brand
    description: The single brand bound to the API key.
  - name: Chats
    description: >-
      List the brand's Brew chats, and read a brand-scoped digest of one —
      referenced emails/automations/triggers + a trimmed transcript — so an
      external agent can resume the conversation.
  - name: Notifications
    description: >-
      The brand's notification feed, as the app's bell shows it. Each row is
      gated by the credential's access to its feature, and a comment mention
      reaches only the person it names.
  - name: Insights
    description: >-
      Brew Insights, read-only: the insight engine’s deterministic findings
      about the brand, ranked as the Insights page shows them, one finding in
      full with its frozen metrics and evidence, and the intelligence layer
      beside them (weekly pulse, latest report, open suggestions, agent memo).
  - name: Integrations
    description: >-
      Brand-scoped catalog of connectable providers plus which ones are already
      connected. Connect itself stays in Settings (`/integrations/{provider}`).
  - name: API Keys
    description: >-
      Mint, list, and revoke API keys through a signed-in Clerk session whose
      active organization role is exactly `org:admin`. API-key and OAuth actors
      receive `403`. `POST` body `brandId` is the new key's binding (the only v1
      body field named `brandId`).
  - name: Meta
    description: >-
      Public discovery surface (no auth): the machine-readable API catalog
      (`/v1/help`).
paths:
  /v1/emails/{emailId}:
    get:
      tags:
        - Emails
      summary: Get an email design
      description: >-
        Reads one design as the bare `EmailDetail` row: `emailId`, the
        `emailVersionId` (the pin for `POST /v1/sends` and sendEmail nodes),
        `title`, `status`, `previewStatus`, `subjectLine`, `previewText`,
        `previewImage`, `updatedAt`, `group` and `createdBy` / `createdByUserId`
        (the design’s creator, shared by every version), plus `errorMessage`
        when a generation failed.


        **Use when** inspecting a design before sending it, pinning a version,
        polling a generation you started, or pulling rendered HTML or version
        history.


        **Input** `emailId` in the path. By default the row is the current head;
        `emailVersionId` reads a saved version and `runId` (returned by a create
        or edit) reads the version that run produced; pass one or neither, never
        both. `include=html` adds the selected version’s rendered HTML once it
        is `ready`, `include=versions` the lean `{ version, emailVersionId }`
        history, `include=text` the visible body text a reader sees (merge tags
        kept; `textTruncated` past 20,000 characters), and `include=links` each
        link destination once with its first visible text and `count`
        (`linksTruncated` past 200); comma-separate any of them.


        **Returns** `200` with the row; the `include` fields are present only
        when requested. A run that never saved a version reads as `failed` with
        the reason, and the design keeps its last saved version.


        **Errors** `404 EMAIL_NOT_FOUND` for an unknown or cross-brand id; `404
        EMAIL_VERSION_NOT_FOUND` for a version or run this design does not have;
        `409 EMAIL_RUN_AMBIGUOUS` when a legacy run matches more than one
        version (read it by `emailVersionId`); `503 SERVICE_UNAVAILABLE` when
        storage is briefly unreachable (retry the same read); `400
        INVALID_REQUEST` for an unknown `include` token or both selectors.


        **See also** `listEmails`, `editEmail`, `createSend`,
        `restoreEmailVersion`.
      operationId: getEmail
      parameters:
        - schema:
            type: string
            minLength: 1
            maxLength: 64
            description: >-
              Design id returned by `POST /v1/emails` and listed by `GET
              /v1/emails`.
            example: 2SmZOWV3ZQ7W5x6g3m4pA
          required: true
          description: >-
            Design id returned by `POST /v1/emails` and listed by `GET
            /v1/emails`.
          name: emailId
          in: path
        - schema:
            type: string
            description: >-
              Comma-separated expansions: `html` (rendered HTML of the selected
              version, once it is `ready`), `versions` (lean `{ version,
              emailVersionId }` history), `text` (the visible body text a reader
              sees), `links` (each link destination once, with its visible text
              and count).
            x-brew-include-tokens:
              - html
              - versions
              - text
              - links
          required: false
          description: >-
            Comma-separated expansions: `html` (rendered HTML of the selected
            version, once it is `ready`), `versions` (lean `{ version,
            emailVersionId }` history), `text` (the visible body text a reader
            sees), `links` (each link destination once, with its visible text
            and count).
          name: include
          in: query
        - schema:
            type: string
            minLength: 1
            maxLength: 64
            description: >-
              Read this saved version instead of the current head. Mutually
              exclusive with `runId`.
          required: false
          description: >-
            Read this saved version instead of the current head. Mutually
            exclusive with `runId`.
          name: emailVersionId
          in: query
        - schema:
            type: string
            minLength: 1
            maxLength: 128
            description: >-
              Read the version one generation run produced — the `runId` a
              create or edit returned. Mutually exclusive with `emailVersionId`.
          required: false
          description: >-
            Read the version one generation run produced — the `runId` a create
            or edit returned. Mutually exclusive with `emailVersionId`.
          name: runId
          in: query
        - name: X-Brand-Id
          in: header
          required: false
          description: >-
            The brand this request acts on. REQUIRED for organization-scoped
            credentials (otherwise `400 BRAND_ID_REQUIRED` — there is no default
            brand); list ids with `GET /v1/brands`. Brand-scoped credentials may
            omit it, and sending a different brand returns `403
            BRAND_SCOPE_MISMATCH`. A brand outside your organization returns
            `404 BRAND_NOT_FOUND`.
          schema:
            type: string
            minLength: 1
            maxLength: 64
          example: kx7b3s7fapqz8mjm12ekz1kxdx87yceg
      responses:
        '200':
          description: The design row.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
            X-RateLimit-Limit:
              schema:
                type: integer
                description: Requests allowed in the current rolling rate limit window.
                example: 100
              required: true
              description: Requests allowed in the current rolling rate limit window.
            X-RateLimit-Remaining:
              schema:
                type: integer
                description: Requests remaining in the current rolling rate limit window.
                example: 99
              required: true
              description: Requests remaining in the current rolling rate limit window.
            X-RateLimit-Reset:
              schema:
                type: integer
                description: >-
                  Unix timestamp in seconds for when the rolling window fully
                  resets.
                example: 1712592360
              required: true
              description: >-
                Unix timestamp in seconds for when the rolling window fully
                resets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailDetail'
              example:
                emailId: V1StGXR8_Z5jdHi6B-myT
                emailVersionId: Yc5Kz8Sq2Wn7Lp4Tm1RvX
                version: latest
                title: Welcome Email
                status: ready
                previewStatus: available
                subjectLine: Welcome to Acme
                previewText: Your first week, planned.
                previewImage: https://cdn.brew.new/p/V1StGXR8_Z5jdHi6B-myT.png
                updatedAt: '2026-04-08T12:34:56.789Z'
                group:
                  groupId: grp_welcome
                  groupName: Welcome
                versions:
                  - version: latest
                    emailVersionId: Yc5Kz8Sq2Wn7Lp4Tm1RvX
                  - version: 1
                    emailVersionId: Lw9Rn1Vc5KzSGm2Tq7Xp4
        '400':
          description: >-
            `BRAND_ID_REQUIRED`: An organization-scoped credential called a
            brand-scoped operation without naming the brand.


            `INVALID_REQUEST`: The body or query failed validation: an unknown
            key, a wrong type, or a missing required field. `param` names the
            offender.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                brandIdRequired:
                  summary: BRAND_ID_REQUIRED
                  value:
                    error:
                      code: BRAND_ID_REQUIRED
                      type: invalid_request
                      message: >-
                        An organization-scoped credential called a brand-scoped
                        operation without naming the brand.
                      suggestion: >-
                        List brands with GET /v1/brands, then name one with the
                        X-Brand-Id header. There is no default brand.
                      docs: https://docs.brew.new/api-reference/api/authentication
                invalidRequest:
                  summary: INVALID_REQUEST
                  value:
                    error:
                      code: INVALID_REQUEST
                      type: invalid_request
                      message: >-
                        The body or query failed validation: an unknown key, a
                        wrong type, or a missing required field. `param` names
                        the offender.
                      suggestion: Fix the field reported in `param` and retry.
                      docs: https://docs.brew.new/api-reference/api/errors
        '401':
          description: >-
            `API_KEY_REVOKED`: The API key was revoked.


            `AUTHENTICATION_REQUIRED`: No API key or session accompanied the
            request.


            `INVALID_API_KEY`: The API key is malformed or unknown.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                apiKeyRevoked:
                  summary: API_KEY_REVOKED
                  value:
                    error:
                      code: API_KEY_REVOKED
                      type: authentication_error
                      message: The API key was revoked.
                      suggestion: Create a new active API key and retry.
                      docs: https://docs.brew.new/api-reference/api/authentication
                authenticationRequired:
                  summary: AUTHENTICATION_REQUIRED
                  value:
                    error:
                      code: AUTHENTICATION_REQUIRED
                      type: authentication_error
                      message: No API key or session accompanied the request.
                      suggestion: >-
                        Provide a valid API key or sign in with an organization
                        session.
                      docs: https://docs.brew.new/api-reference/api/authentication
                invalidApiKey:
                  summary: INVALID_API_KEY
                  value:
                    error:
                      code: INVALID_API_KEY
                      type: authentication_error
                      message: The API key is malformed or unknown.
                      suggestion: >-
                        Check the API key format and retry with a valid active
                        key.
                      docs: https://docs.brew.new/api-reference/api/authentication
        '403':
          description: >-
            `ACCOUNT_SUSPENDED`: The organization behind the credential is
            suspended.


            `BRAND_SCOPE_MISMATCH`: A brand-scoped credential named a brand
            other than the one it is bound to.


            `INSUFFICIENT_PERMISSIONS`: The credential lacks the permission
            scope the operation needs.


            `INSUFFICIENT_ROLE`: The caller lacks the access the operation
            needs: `param` names `member` (access to the brand) or `org_admin`
            (the organization role).
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                accountSuspended:
                  summary: ACCOUNT_SUSPENDED
                  value:
                    error:
                      code: ACCOUNT_SUSPENDED
                      type: authorization_error
                      message: The organization behind the credential is suspended.
                      suggestion: >-
                        The organization behind this credential is suspended.
                        Contact support@brew.new; do not retry.
                      docs: https://docs.brew.new/api-reference/api/authentication
                brandScopeMismatch:
                  summary: BRAND_SCOPE_MISMATCH
                  value:
                    error:
                      code: BRAND_SCOPE_MISMATCH
                      type: authorization_error
                      message: >-
                        A brand-scoped credential named a brand other than the
                        one it is bound to.
                      suggestion: >-
                        Omit the brand to use the one this credential is bound
                        to, or use an organization-scoped credential to reach
                        other brands.
                      docs: https://docs.brew.new/api-reference/api/authentication
                insufficientPermissions:
                  summary: INSUFFICIENT_PERMISSIONS
                  value:
                    error:
                      code: INSUFFICIENT_PERMISSIONS
                      type: authorization_error
                      message: >-
                        The credential lacks the permission scope the operation
                        needs.
                      suggestion: Use an API key or session with the required permission.
                      docs: https://docs.brew.new/api-reference/api/authentication
                insufficientRole:
                  summary: INSUFFICIENT_ROLE
                  value:
                    error:
                      code: INSUFFICIENT_ROLE
                      type: authorization_error
                      message: >-
                        The caller lacks the access the operation needs: `param`
                        names `member` (access to the brand) or `org_admin` (the
                        organization role).
                      suggestion: >-
                        Ask an organization admin to run this, to add you to the
                        brand, or to make you an admin.
                      docs: https://docs.brew.new/api-reference/api/authentication
        '404':
          description: >-
            `BRAND_NOT_FOUND`: The named or bound brand does not exist in this
            organization (unknown, deleting, or another organization).


            `EMAIL_NOT_FOUND`: No email design with that id exists in the brand
            (cross-brand ids surface as 404).


            `EMAIL_VERSION_NOT_FOUND`: The design has no version with that id.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                brandNotFound:
                  summary: BRAND_NOT_FOUND
                  value:
                    error:
                      code: BRAND_NOT_FOUND
                      type: not_found
                      message: >-
                        The named or bound brand does not exist in this
                        organization (unknown, deleting, or another
                        organization).
                      suggestion: >-
                        List the brands this credential can reach with GET
                        /v1/brands.
                      docs: https://docs.brew.new/api-reference/api/errors
                emailNotFound:
                  summary: EMAIL_NOT_FOUND
                  value:
                    error:
                      code: EMAIL_NOT_FOUND
                      type: not_found
                      message: >-
                        No email design with that id exists in the brand
                        (cross-brand ids surface as 404).
                      suggestion: List designs with GET /v1/emails.
                      docs: https://docs.brew.new/api-reference/api/errors
                emailVersionNotFound:
                  summary: EMAIL_VERSION_NOT_FOUND
                  value:
                    error:
                      code: EMAIL_VERSION_NOT_FOUND
                      type: not_found
                      message: The design has no version with that id.
                      suggestion: >-
                        List versions with GET
                        /v1/emails/{emailId}?include=versions and retry with a
                        valid emailVersionId.
                      docs: https://docs.brew.new/api-reference/api/errors
        '409':
          description: >-
            `EMAIL_RUN_AMBIGUOUS`: A legacy generation run matches more than one
            saved version of the design.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                emailRunAmbiguous:
                  summary: EMAIL_RUN_AMBIGUOUS
                  value:
                    error:
                      code: EMAIL_RUN_AMBIGUOUS
                      type: conflict
                      message: >-
                        A legacy generation run matches more than one saved
                        version of the design.
                      suggestion: >-
                        List versions with GET
                        /v1/emails/{emailId}?include=versions and read the one
                        you want by emailVersionId.
                      docs: https://docs.brew.new/api-reference/api/errors
        '429':
          description: >-
            `RATE_LIMITED`: The credential exhausted the rolling window for this
            route policy; Retry-After says when it reopens.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
            X-RateLimit-Limit:
              schema:
                type: integer
                description: Requests allowed in the current rolling rate limit window.
                example: 100
              required: true
              description: Requests allowed in the current rolling rate limit window.
            X-RateLimit-Remaining:
              schema:
                type: integer
                description: Requests remaining in the current rolling rate limit window.
                example: 99
              required: true
              description: Requests remaining in the current rolling rate limit window.
            X-RateLimit-Reset:
              schema:
                type: integer
                description: >-
                  Unix timestamp in seconds for when the rolling window fully
                  resets.
                example: 1712592360
              required: true
              description: >-
                Unix timestamp in seconds for when the rolling window fully
                resets.
            Retry-After:
              schema:
                type: integer
                description: Seconds to wait before retrying the request.
                example: 42
              required: true
              description: Seconds to wait before retrying the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                rateLimited:
                  summary: RATE_LIMITED
                  value:
                    error:
                      code: RATE_LIMITED
                      type: rate_limit
                      message: >-
                        The credential exhausted the rolling window for this
                        route policy; Retry-After says when it reopens.
                      suggestion: >-
                        Wait for the retry window before sending another
                        request.
                      docs: https://docs.brew.new/api-reference/api/rate-limits
                      retryAfter: 42
        '500':
          description: >-
            `INTERNAL_ERROR`: An unexpected failure; the x-request-id header
            identifies it.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                internalError:
                  summary: INTERNAL_ERROR
                  value:
                    error:
                      code: INTERNAL_ERROR
                      type: internal_error
                      message: >-
                        An unexpected failure; the x-request-id header
                        identifies it.
                      suggestion: >-
                        Retry the request. If it keeps failing, contact support
                        with the x-request-id header.
                      docs: https://docs.brew.new/api-reference/api/errors
        '503':
          description: >-
            `SERVICE_UNAVAILABLE`: A dependency the operation must consult
            (billing, the idempotency store) is temporarily unavailable, so the
            request was refused rather than run unmetered.
          headers:
            x-request-id:
              schema:
                type: string
                description: >-
                  Unique request identifier. Share this with support when
                  debugging a request.
                example: req_8cac13fd94e6420cacdd75a1aa403a28
              required: true
              description: >-
                Unique request identifier. Share this with support when
                debugging a request.
            Retry-After:
              schema:
                type: integer
                description: Seconds to wait before retrying the request.
                example: 42
              required: true
              description: Seconds to wait before retrying the request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorEnvelope'
              examples:
                serviceUnavailable:
                  summary: SERVICE_UNAVAILABLE
                  value:
                    error:
                      code: SERVICE_UNAVAILABLE
                      type: service_unavailable
                      message: >-
                        A dependency the operation must consult (billing, the
                        idempotency store) is temporarily unavailable, so the
                        request was refused rather than run unmetered.
                      suggestion: Retry the request after a short delay.
                      docs: https://docs.brew.new/api-reference/api/errors
components:
  schemas:
    EmailDetail:
      type: object
      properties:
        emailId:
          type: string
          minLength: 1
        emailVersionId:
          type: string
          minLength: 1
        title:
          type: string
          minLength: 1
        status:
          type: string
          enum:
            - generating
            - ready
            - failed
        previewImage:
          type: string
          format: uri
        updatedAt:
          type: string
          format: date-time
          description: >-
            Last stable design update. While status is `generating`, collection
            reads hold this at `createdAt` so body chunks do not reorder or
            invalidate the whole list; the completion transition publishes the
            final source timestamp.
        subjectLine:
          type: string
          description: >-
            The design's default inbox subject, persisted on the latest version.
            Distinct from `title` (canvas name). Detail-only; absent on rows
            where it has never been set (via the write surfaces' `subjectLine`
            field, or in-app). `POST /v1/sends` still requires an explicit
            `subject`.
        previewText:
          type: string
          description: >-
            The design's inbox preview line, read directly from the latest
            version's JSX <Preview> (its single source of truth) — what a send
            delivers when no explicit `previewText` override is passed to POST
            /v1/sends. Detail-only; absent when the design has no <Preview>.
        html:
          type: string
        versions:
          type: array
          items:
            type: object
            properties:
              version:
                anyOf:
                  - type: integer
                    minimum: 0
                  - type: string
                    enum:
                      - latest
              emailVersionId:
                type: string
                minLength: 1
            required:
              - version
              - emailVersionId
            additionalProperties: false
        group:
          type:
            - object
            - 'null'
          properties:
            groupId:
              type: string
              minLength: 1
              maxLength: 64
            groupName:
              type: string
              minLength: 1
              maxLength: 60
          required:
            - groupId
            - groupName
          additionalProperties: false
        createdBy:
          type: string
          description: >-
            Creator as Brew shows it: a name or "Former member", "API" or "MCP",
            or both joined ("Name · MCP"); absent when the creator cannot be
            named.
        createdByUserId:
          type: string
          description: Creator user id; absent for an organization API key.
        version:
          anyOf:
            - type: integer
              minimum: 0
            - type: string
              enum:
                - latest
          description: >-
            The version that was read: its number, or `latest` for the current
            head.
        runId:
          type: string
          description: Echoes the `runId` query parameter when one was passed.
        previewStatus:
          type: string
          enum:
            - available
            - unavailable
            - not_ready
          description: >-
            `available` when `previewImage` is set; `unavailable` when a ready
            design has no saved screenshot; `not_ready` while generating or
            after a failure.
        content:
          type: object
          properties:
            url:
              type: string
              format: uri
            mimeType:
              type: string
              enum:
                - text/html
            bytes:
              type: integer
              minimum: 0
            expiresAt:
              type: string
              format: date-time
          required:
            - url
            - mimeType
            - bytes
            - expiresAt
          additionalProperties: false
          description: >-
            MCP only: a signed, expiring download of the selected version’s
            HTML, in place of an `html` too large to inline.
        text:
          type: string
          description: >-
            `include=text`: visible body text, merge tags kept (max 20,000
            chars).
        textTruncated:
          type: boolean
        links:
          type: array
          items:
            type: object
            properties:
              href:
                type:
                  - string
                  - 'null'
              text:
                type: string
              count:
                type: integer
                exclusiveMinimum: 0
            required:
              - href
              - text
              - count
            additionalProperties: false
          description: >-
            `include=links`: each destination once (`href`, first visible
            `text`, `count`); max 200.
        linksTruncated:
          type: boolean
        errorMessage:
          type: string
          description: Why the generation failed. Present when `status` is `failed`.
        errorCause:
          type: string
          description: A stable failure category, when one was recorded.
      required:
        - emailId
        - title
        - status
        - updatedAt
        - group
        - previewStatus
      additionalProperties: false
    ApiErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              $ref: '#/components/schemas/ApiErrorCode'
            type:
              type: string
              enum:
                - authentication_error
                - authorization_error
                - invalid_request
                - not_found
                - not_implemented
                - conflict
                - rate_limit
                - payment_required
                - service_unavailable
                - internal_error
            message:
              type: string
              minLength: 1
            param:
              type: string
              minLength: 1
            suggestion:
              type: string
              minLength: 1
            docs:
              type: string
              format: uri
            retryAfter:
              type: integer
              minimum: 0
            details:
              type: object
              additionalProperties: {}
          required:
            - code
            - type
            - message
            - suggestion
            - docs
      required:
        - error
    ApiErrorCode:
      type: string
      enum:
        - ACCOUNT_SUSPENDED
        - API_KEY_REVOKED
        - AUDIENCE_BUILD_ACTIVE
        - AUDIENCE_BUILD_ALREADY_ACTIVE
        - AUDIENCE_EDIT_CONFLICT
        - AUDIENCE_MEMBERSHIP_NOT_EXPRESSIBLE
        - AUDIENCE_NOT_FOUND
        - AUDIENCE_RUN_NOT_FOUND
        - AUDIT_NOT_FOUND
        - AUTHENTICATION_REQUIRED
        - AUTOMATION_GRAPH_INVALID
        - AUTOMATION_NOT_FOUND
        - AUTOMATION_NOT_PAUSABLE
        - AUTOMATION_NOT_PUBLISHED
        - AUTOMATION_RUN_NOT_FOUND
        - AUTOMATION_VERSION_CONFLICT
        - AUTOMATION_VERSION_NOT_FOUND
        - BATCH_TOO_LARGE
        - BRAND_DOMAIN_CONFLICT
        - BRAND_ID_REQUIRED
        - BRAND_LIMIT_REACHED
        - BRAND_NOT_FOUND
        - BRAND_NOT_READY
        - BRAND_SCOPE_MISMATCH
        - CHAT_NOT_FOUND
        - COMMENT_NOT_FOUND
        - CONSENT_REQUIRED
        - CONTACT_NOT_FOUND
        - CONTENT_OPERATION_FAILED
        - CONTRACT_LOCKED_BY_PUBLISHED_AUTOMATIONS
        - CORE_FIELD_IMMUTABLE
        - DOMAIN_ALREADY_EXISTS
        - DOMAIN_CLAIMED_ELSEWHERE
        - DOMAIN_NOT_FOUND
        - DOMAIN_NOT_READY
        - DOMAIN_OTHER_BRAND
        - DOMAIN_PROVIDER_ERROR
        - DOMAIN_PURPOSE_NOT_ALLOWED
        - DOMAIN_VERIFICATION_FAILED
        - DOMAIN_VERIFIED_ELSEWHERE
        - EMAIL_GENERATION_FAILED
        - EMAIL_GROUP_NAME_CONFLICT
        - EMAIL_GROUP_NOT_FOUND
        - EMAIL_IMAGES_MISSING
        - EMAIL_IMPORT_FAILED
        - EMAIL_IN_PROGRESS
        - EMAIL_IN_USE_BY_AUTOMATION
        - EMAIL_NOT_FOUND
        - EMAIL_NOT_READY
        - EMAIL_RUN_AMBIGUOUS
        - EMAIL_TEMPLATE_INVALID
        - EMAIL_VERSION_NOT_FOUND
        - EXPORT_PROVIDER_ERROR
        - EXPORT_UNSUPPORTED
        - FIELD_NOT_FOUND
        - FIELD_TYPE_MISMATCH
        - FIGMA_ACCESS_DENIED
        - FIGMA_CONVERSION_FAILED
        - FIGMA_FRAME_NOT_FOUND
        - FIGMA_NOT_CONNECTED
        - FIGMA_UNAVAILABLE
        - FIGMA_URL_INVALID
        - FLOW_NOT_FOUND
        - IDEMPOTENCY_CONFLICT
        - IDEMPOTENCY_IN_PROGRESS
        - INSIGHT_NOT_FOUND
        - INSUFFICIENT_CREDITS
        - INSUFFICIENT_PERMISSIONS
        - INSUFFICIENT_ROLE
        - INTEGRATION_NOT_CONNECTED
        - INTERNAL_ERROR
        - INVALID_API_KEY
        - INVALID_EMAIL
        - INVALID_PAYLOAD
        - INVALID_REQUEST
        - LIQUID_RENDER_ERROR
        - METHOD_NOT_ALLOWED
        - MISSING_EMAIL
        - NO_ELIGIBLE_RECIPIENTS
        - NO_PUBLISHED_AUTOMATION
        - NOT_FOUND
        - NOT_IMPLEMENTED
        - ORG_SCOPE_REQUIRED
        - PAYLOAD_SCHEMA_EMAIL_REQUIRED
        - PAYLOAD_TOO_LARGE
        - PREVIEW_NOT_FOUND
        - PUBLISH_VALIDATION_FAILED
        - RATE_LIMITED
        - RECIPIENT_UNSUBSCRIBED
        - REFERENCE_EMAIL_NOT_FOUND
        - RESUBSCRIBE_NOT_ALLOWED
        - RUN_IN_PROGRESS
        - RUN_NOT_CANCELLABLE
        - RUN_NOT_PAUSABLE
        - RUN_NOT_PAUSED
        - RUN_NOT_RESUMABLE
        - RUN_START_FAILED
        - RUN_STOP_FAILED
        - SEND_NOT_CANCELLABLE
        - SEND_NOT_FOUND
        - SEND_NOT_PAUSABLE
        - SEND_NOT_RESUMABLE
        - SEND_QUOTA_EXCEEDED
        - SERVICE_UNAVAILABLE
        - TEMPLATE_NOT_FOUND
        - TRIGGER_ALREADY_EXISTS
        - TRIGGER_EVENT_NOT_FOUND
        - TRIGGER_HAS_DEPENDENT_AUTOMATIONS
        - TRIGGER_IMMUTABLE
        - TRIGGER_INSTANCE_NOT_FOUND
        - TRIGGER_LIMIT_REACHED
        - UPLOAD_IN_PROGRESS
        - UPLOAD_NOT_FOUND
        - UPLOAD_NOT_RECEIVED
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: 'Send your Brew API key as `Authorization: Bearer brew_xxx`.'
      x-default: Bearer brew_your_api_key
    apiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: 'Send your Brew API key as `X-API-Key: brew_xxx`.'
      x-default: brew_your_api_key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.