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

# Audit an email

> Lint raw email content for production readiness. `emailHtml` is capped at 5,000,000 UTF-8 bytes and the complete JSON body at 6 MiB; `subject` and `previewText` each have a 1,000-character transport cap. Omitted preview text is extracted from the authored preheader, while an explicit empty string stays empty. Omitted `sendingPurpose` defaults to marketing and is reported as defaulted. Independent checks run in parallel across unsubscribe compliance, links and images, total loaded size, accessibility, markup, subject line, and preview text. The stable versioned response reports every check, up to 100 normalized findings, exact totals, metrics, and a nested `completion` discriminator. A complete result has a 0–100 score and costs 5 credits (`X-Credit-Cost: 5`). If a required lane is unavailable, the endpoint returns a partial result with `score: null`, never establishes readiness, costs 0 credits (`X-Credit-Cost: 0`), and releases the idempotency key so the same key can retry. Admission is limited to 6 requests per minute per credential or session and 20 per minute across the organization, shared by public API, MCP, and agent calls. Brew runs at most 4 audits concurrently per organization and 16 globally; capacity rejections return `429 RATE_LIMITED` with `Retry-After` and do not run or charge the audit.



## OpenAPI

````yaml /api-reference/openapi-public-v1.yaml post /v1/emails/audit
openapi: 3.1.0
info:
  title: Brew Public API v1
  version: 1.0.0
  description: >-
    Generated from the Brew app Zod contracts (`lib/<domain>/contracts.ts`).
    This file is the source of truth for the public API documentation.


    ## Resource paths


    Identity lives in the URL path (`/v1/analytics/sends/{sendId}`) — never in a
    query param or request body. Collections are plural top-level segments
    (`/v1/emails`); query params exist only for collection pagination + simple
    filters. Relationships are sub-resources (`/v1/emails/{emailId}/sends`), and
    non-CRUD operations are explicit action sub-paths
    (`/v1/automations/{automationId}/test`).


    ## Response envelopes


    - Lists: `{ data: Row[], pagination: { limit, cursor: string | null, hasMore
    } }` — loop `while (cursor !== null)`.

    - Get-one and writes: the bare resource (creates return `201`; async sends
    `202`).

    - Deletes: `{ <idField>, deleted: boolean }` — idempotent (already-gone ids
    resolve with `deleted: false`).

    - Errors: `{ error: { code, type, message, param?, suggestion, docs } }` —
    branch on the stable `code`.

    - ONE exception: `POST /v1/automations/triggers/{triggerEventId}/fire`
    responds with the legacy fire envelope `{ success, status, code, message,
    receivedAt, details }` (shared with internal webhook infrastructure).


    ## Brand scoping


    A credential is scoped either to ONE BRAND or to the whole ORGANIZATION.


    A brand-scoped credential resolves its brand automatically — send nothing.
    An organization-scoped credential must name the brand per request with the
    `X-Brand-Id` header; omitting it returns `400 BRAND_ID_REQUIRED`, because
    there is deliberately no default brand. Discover ids with `GET /v1/brands`.


    **No public endpoint accepts a `brandId` field** in its request body or
    query string — sending one returns `400 INVALID_REQUEST`. `X-Brand-Id` is a
    header, and the only way to name a brand.


    Resources that exist in a different brand surface as `404` (never `403`), so
    the API does not leak cross-brand existence; the same is true of an
    `X-Brand-Id` naming a brand outside your organization. `GET /v1/templates`
    is organization-wide. `/v1/brands` manages brands themselves and is
    organization-level, so it takes no `X-Brand-Id`.


    ## Idempotency


    Send an `Idempotency-Key` header (≤ 100 chars) on any POST your code might
    retry. Same key + same body within 24h returns the original response; same
    key + different body returns `409 IDEMPOTENCY_CONFLICT`.
  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 (list, status, per-send
      event feeds) live under Analytics (`/v1/analytics/sends`).
  - 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 analytics: lifetime per-campaign KPIs, windowed
      automation performance, the unified event feed, send reads
      (`/v1/analytics/sends`), and the fired-trigger audit log
      (`/v1/analytics/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: >-
      Read a brand-scoped digest of a Brew chat — referenced
      emails/automations/triggers + a trimmed transcript — so an external agent
      can resume the conversation.
  - 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. `POST` body `brandId` is the new key's
      binding (the only v1 body field named `brandId`). Organization-wide keys
      need an org-admin session or an organization-scoped credential.
  - name: Meta
    description: >-
      Public discovery surface (no auth): the machine-readable API catalog
      (`/v1/help`).
paths:
  /v1/emails/audit:
    post:
      tags:
        - Emails
      summary: Audit an email
      description: >-
        Lint raw email content for production readiness. `emailHtml` is capped
        at 5,000,000 UTF-8 bytes and the complete JSON body at 6 MiB; `subject`
        and `previewText` each have a 1,000-character transport cap. Omitted
        preview text is extracted from the authored preheader, while an explicit
        empty string stays empty. Omitted `sendingPurpose` defaults to marketing
        and is reported as defaulted. Independent checks run in parallel across
        unsubscribe compliance, links and images, total loaded size,
        accessibility, markup, subject line, and preview text. The stable
        versioned response reports every check, up to 100 normalized findings,
        exact totals, metrics, and a nested `completion` discriminator. A
        complete result has a 0–100 score and costs 5 credits (`X-Credit-Cost:
        5`). If a required lane is unavailable, the endpoint returns a partial
        result with `score: null`, never establishes readiness, costs 0 credits
        (`X-Credit-Cost: 0`), and releases the idempotency key so the same key
        can retry. Admission is limited to 6 requests per minute per credential
        or session and 20 per minute across the organization, shared by public
        API, MCP, and agent calls. Brew runs at most 4 audits concurrently per
        organization and 16 globally; capacity rejections return `429
        RATE_LIMITED` with `Retry-After` and do not run or charge the audit.
      operationId: auditEmail
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >-
            Optional idempotency key for safe retries. Reusing the same key with
            the same request body returns the original response for 24 hours.
          schema:
            type: string
            minLength: 1
            maxLength: 100
          example: api-request-2026-04-08-001
        - 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
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmailAuditRequest'
            example:
              emailHtml: >-
                <!doctype html><html lang="en"><body><a
                href="https://example.com/account">View account</a><a href="{{
                unsubscribe_url }}">Unsubscribe</a></body></html>
              subject: Your August account update
              previewText: A quick look at what changed this month.
              sendingPurpose: marketing
      responses:
        '200':
          description: >-
            A complete or partial audit. Branch on `completion.status`; only
            `complete` carries a numeric score. A partial response is not cached
            under its idempotency key and may be retried with the same key.
          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.
            X-Credit-Cost:
              schema:
                type: integer
                minimum: 0
                description: Credits charged for this completed operation.
                example: 5
              required: true
              description: Credits charged for this completed operation.
            X-Credits-Remaining:
              schema:
                type: integer
                minimum: 0
                description: Credits remaining after this operation.
                example: 95
              required: true
              description: Credits remaining after this operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmailAuditResponse'
              example:
                schemaVersion: 1
                rulesetVersion: 2026-08-24.3
                auditId: 00000000-0000-4000-8000-000000000001
                contentHash: >-
                  sha256:0000000000000000000000000000000000000000000000000000000000000000
                auditedAt: '2026-08-23T00:00:00.000Z'
                expiresAt: '2026-08-23T00:15:00.000Z'
                policy:
                  purpose: marketing
                  source: provided
                  unsubscribe: required
                summary:
                  blockers: 0
                  errors: 0
                  warnings: 1
                  info: 0
                  total: 1
                checks:
                  - id: preflight
                    status: issues
                    durationMs: 0
                    findingCount: 1
                metrics:
                  htmlBytes: 2134
                  linkCount: 2
                  imageCount: 0
                  gifCount: 0
                  loadedSize:
                    status: exact
                    htmlBytes: 2134
                    remoteAssetBytes: 0
                    totalBytes: 2134
                    assetCount: 0
                findings:
                  - id: copy.subject.long:subject
                    ruleId: copy.subject.long
                    category: copy
                    severity: warning
                    impact: advisory
                    message: The subject may truncate on smaller inboxes.
                    remediation: Shorten it while keeping the main benefit clear.
                    sources:
                      - preflight
                    target:
                      kind: subject
                totalFindings: 1
                findingsTruncated: false
                completion:
                  status: complete
                  readiness: needs_review
                  score: 97
        '400':
          description: >-
            Invalid raw email content, an unknown field, or an invalid
            sendingPurpose.
          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'
              example:
                error:
                  code: INVALID_REQUEST
                  type: invalid_request
                  message: Request validation failed.
                  suggestion: Fix the field reported in `param` and retry.
                  docs: https://docs.brew.new/api-reference/api/errors
                  param: emailHtml
        '401':
          description: The API key was missing, invalid, or revoked.
          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'
              example:
                error:
                  code: INVALID_API_KEY
                  type: authentication_error
                  message: The provided API key is invalid.
                  suggestion: Check the API key format and retry with a valid active key.
                  docs: https://docs.brew.new/api-reference/api/authentication
        '402':
          description: >-
            The org's remaining credit balance is below what this operation
            requires. Credit cost is published PER-OPERATION (see `GET
            /v1/help`): content/media operations charge a flat cost, while AI
            generation (email generate/edit, image generation) is usage-metered
            — charged by actual model usage rather than a flat price.
            `details.cost` carries the amount the runtime required for THIS
            call. Check your balance up front via `GET /v1/usage`. No
            `Retry-After` — credits reset at the billing-period boundary.
          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'
              example:
                error:
                  code: INSUFFICIENT_CREDITS
                  type: payment_required
                  message: >-
                    This operation required more credits than the 0 remaining on
                    the 'free' plan. See the per-operation cost in GET /v1/help.
                  suggestion: >-
                    Upgrade your plan or wait for the next billing period to
                    reset. Check your balance up front with GET /v1/usage.
                  docs: https://docs.brew.new/api-reference/api/credits
                  details:
                    cost: 5
                    remaining: 0
                    planKey: free
        '403':
          description: The caller does not have the required `emails` permission.
          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'
              example:
                error:
                  code: INSUFFICIENT_PERMISSIONS
                  type: authorization_error
                  message: The caller does not have the required permission.
                  suggestion: Use an API key or session with the required permission.
                  docs: https://docs.brew.new/api-reference/api/authentication
                  param: emails
        '409':
          description: The same `Idempotency-Key` was reused with a different request body.
          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'
              example:
                error:
                  code: IDEMPOTENCY_CONFLICT
                  type: conflict
                  message: >-
                    The same idempotency key was reused with a different request
                    payload.
                  suggestion: Reuse the original payload or send a new idempotency key.
                  docs: https://docs.brew.new/api-reference/api/idempotency
        '413':
          description: The complete JSON request body exceeds the 6 MiB transport limit.
          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'
              example:
                error:
                  code: PAYLOAD_TOO_LARGE
                  type: invalid_request
                  message: Request body must not exceed 6291456 bytes.
                  suggestion: Reduce the payload size and retry.
                  docs: https://docs.brew.new/api-reference/api/errors
        '429':
          description: The request hit the rolling rate limit window.
          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'
              example:
                error:
                  code: RATE_LIMITED
                  type: rate_limit
                  message: Too many requests.
                  suggestion: Wait for the retry window before sending another request.
                  docs: https://docs.brew.new/api-reference/api/rate-limits
                  retryAfter: 42
        '500':
          description: Unexpected internal error.
          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'
              example:
                error:
                  code: INTERNAL_ERROR
                  type: internal_error
                  message: An unexpected error occurred.
                  suggestion: Retry the request. If it keeps failing, contact support.
                  docs: https://docs.brew.new/api-reference/api/errors
        '503':
          description: >-
            The credit balance could not be verified (a transient billing
            dependency outage). The gate fails closed rather than do paid work
            it cannot meter. Retryable — `Retry-After` indicates when.
          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'
              example:
                error:
                  code: SERVICE_UNAVAILABLE
                  type: service_unavailable
                  message: >-
                    Your credit balance could not be verified because a billing
                    dependency is temporarily unavailable.
                  suggestion: Retry the request after a short delay.
                  docs: https://docs.brew.new/api-reference/api/credits
                  retryAfter: 2
components:
  schemas:
    EmailAuditRequest:
      type: object
      properties:
        emailHtml:
          type: string
          minLength: 1
        subject:
          type: string
          maxLength: 1000
        previewText:
          type: string
          maxLength: 1000
        sendingPurpose:
          type: string
          enum:
            - marketing
            - transactional
      required:
        - emailHtml
      additionalProperties: false
    EmailAuditResponse:
      type: object
      properties:
        schemaVersion:
          type: number
          enum:
            - 1
        rulesetVersion:
          type: string
          minLength: 1
          maxLength: 100
        auditId:
          type: string
          format: uuid
        contentHash:
          type: string
          pattern: ^sha256:[0-9a-f]{64}$
        auditedAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
        policy:
          type: object
          properties:
            purpose:
              type: string
              enum:
                - marketing
                - transactional
                - unknown
            source:
              type: string
              enum:
                - provided
                - defaulted
                - trusted_adapter
            unsubscribe:
              type: string
              enum:
                - required
                - not_required
                - not_evaluated
          required:
            - purpose
            - source
            - unsubscribe
          additionalProperties: false
        summary:
          type: object
          properties:
            blockers:
              type: integer
              minimum: 0
            errors:
              type: integer
              minimum: 0
            warnings:
              type: integer
              minimum: 0
            info:
              type: integer
              minimum: 0
            total:
              type: integer
              minimum: 0
          required:
            - blockers
            - errors
            - warnings
            - info
            - total
          additionalProperties: false
        checks:
          type: array
          items:
            oneOf:
              - type: object
                properties:
                  id:
                    type: string
                    minLength: 1
                    maxLength: 128
                  status:
                    type: string
                    enum:
                      - passed
                  durationMs:
                    type: integer
                    minimum: 0
                  findingCount:
                    type: number
                    enum:
                      - 0
                required:
                  - id
                  - status
                  - durationMs
                  - findingCount
                additionalProperties: false
              - type: object
                properties:
                  id:
                    type: string
                    minLength: 1
                    maxLength: 128
                  status:
                    type: string
                    enum:
                      - issues
                  durationMs:
                    type: integer
                    minimum: 0
                  findingCount:
                    type: integer
                    exclusiveMinimum: 0
                required:
                  - id
                  - status
                  - durationMs
                  - findingCount
                additionalProperties: false
              - type: object
                properties:
                  id:
                    type: string
                    minLength: 1
                    maxLength: 128
                  status:
                    type: string
                    enum:
                      - not_applicable
                  reason:
                    type: string
                    enum:
                      - no_remote_links
                      - no_remote_images
                      - no_remote_assets
                      - missing_copy
                      - requires_sending_domain
                      - send_transport_owned
                      - requires_audience_context
                      - separate_deliverability_test
                      - transactional_purpose
                      - unknown_purpose
                required:
                  - id
                  - status
                  - reason
                additionalProperties: false
              - type: object
                properties:
                  id:
                    type: string
                    minLength: 1
                    maxLength: 128
                  status:
                    type: string
                    enum:
                      - unavailable
                  reason:
                    type: string
                    enum:
                      - timeout
                      - upstream
                      - invalid_response
                  retryable:
                    type: boolean
                  durationMs:
                    type: integer
                    minimum: 0
                required:
                  - id
                  - status
                  - reason
                  - retryable
                  - durationMs
                additionalProperties: false
          maxItems: 32
        metrics:
          type: object
          properties:
            htmlBytes:
              type: integer
              minimum: 0
            linkCount:
              type: integer
              minimum: 0
            imageCount:
              type: integer
              minimum: 0
            gifCount:
              type: integer
              minimum: 0
            loadedSize:
              oneOf:
                - type: object
                  properties:
                    status:
                      type: string
                      enum:
                        - exact
                    htmlBytes:
                      type: integer
                      minimum: 0
                    remoteAssetBytes:
                      type: integer
                      minimum: 0
                    totalBytes:
                      type: integer
                      minimum: 0
                    assetCount:
                      type: integer
                      minimum: 0
                  required:
                    - status
                    - htmlBytes
                    - remoteAssetBytes
                    - totalBytes
                    - assetCount
                  additionalProperties: false
                - type: object
                  properties:
                    status:
                      type: string
                      enum:
                        - lower_bound
                    htmlBytes:
                      type: integer
                      minimum: 0
                    knownRemoteAssetBytes:
                      type: integer
                      minimum: 0
                    knownTotalBytes:
                      type: integer
                      minimum: 0
                    unknownAssetCount:
                      type: integer
                      exclusiveMinimum: 0
                  required:
                    - status
                    - htmlBytes
                    - knownRemoteAssetBytes
                    - knownTotalBytes
                    - unknownAssetCount
                  additionalProperties: false
          required:
            - htmlBytes
            - linkCount
            - imageCount
            - gifCount
            - loadedSize
          additionalProperties: false
        findings:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                minLength: 1
                maxLength: 512
              ruleId:
                type: string
                minLength: 1
                maxLength: 128
              category:
                type: string
                enum:
                  - compliance
                  - links
                  - images
                  - accessibility
                  - compatibility
                  - copy
                  - size
                  - markup
              severity:
                type: string
                enum:
                  - blocker
                  - error
                  - warning
                  - info
              impact:
                type: string
                enum:
                  - block
                  - confirm
                  - advisory
              message:
                type: string
                minLength: 1
                maxLength: 1000
              remediation:
                type: string
                minLength: 1
                maxLength: 1000
              occurrenceCount:
                type: integer
                exclusiveMinimum: 0
              sources:
                type: array
                items:
                  type: string
                  minLength: 1
                  maxLength: 64
                minItems: 1
                maxItems: 16
              standards:
                type: array
                items:
                  type: object
                  properties:
                    id:
                      type: string
                      minLength: 1
                      maxLength: 128
                    url:
                      type: string
                      maxLength: 2048
                      pattern: ^https?:\/\/
                  required:
                    - id
                  additionalProperties: false
                maxItems: 8
              target:
                oneOf:
                  - type: object
                    properties:
                      kind:
                        type: string
                        enum:
                          - email
                    required:
                      - kind
                    additionalProperties: false
                  - type: object
                    properties:
                      kind:
                        type: string
                        enum:
                          - subject
                    required:
                      - kind
                    additionalProperties: false
                  - type: object
                    properties:
                      kind:
                        type: string
                        enum:
                          - preview_text
                    required:
                      - kind
                    additionalProperties: false
                  - type: object
                    properties:
                      kind:
                        type: string
                        enum:
                          - link
                      index:
                        type: integer
                        minimum: 0
                      displayUrl:
                        type: string
                        maxLength: 300
                    required:
                      - kind
                      - index
                      - displayUrl
                    additionalProperties: false
                  - type: object
                    properties:
                      kind:
                        type: string
                        enum:
                          - image
                      index:
                        type: integer
                        minimum: 0
                      displayUrl:
                        type: string
                        maxLength: 300
                    required:
                      - kind
                      - index
                      - displayUrl
                    additionalProperties: false
                  - type: object
                    properties:
                      kind:
                        type: string
                        enum:
                          - element
                      selector:
                        type: string
                        minLength: 1
                        maxLength: 300
                    required:
                      - kind
                      - selector
                    additionalProperties: false
            required:
              - id
              - ruleId
              - category
              - severity
              - impact
              - message
              - remediation
              - sources
              - target
            additionalProperties: false
          maxItems: 100
        totalFindings:
          type: integer
          minimum: 0
        findingsTruncated:
          type: boolean
        completion:
          oneOf:
            - type: object
              properties:
                status:
                  type: string
                  enum:
                    - complete
                readiness:
                  type: string
                  enum:
                    - ready
                    - needs_review
                    - not_ready
                score:
                  type: integer
                  minimum: 0
                  maximum: 100
              required:
                - status
                - readiness
                - score
              additionalProperties: false
            - type: object
              properties:
                status:
                  type: string
                  enum:
                    - partial
                readiness:
                  type: string
                  enum:
                    - unknown
                    - not_ready
                score:
                  type: 'null'
              required:
                - status
                - readiness
                - score
              additionalProperties: false
      required:
        - schemaVersion
        - rulesetVersion
        - auditId
        - contentHash
        - auditedAt
        - expiresAt
        - policy
        - summary
        - checks
        - metrics
        - findings
        - totalFindings
        - findingsTruncated
        - completion
      additionalProperties: false
    ApiErrorEnvelope:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              minLength: 1
            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
  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

````