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

# Ingest a Vercel Drain delivery

> Receives a Vercel Drain (formerly "Log Drain") delivery in VERCEL'S OWN format, so a Vercel-hosted customer needs no code: they paste this URL into a drain and add one `Authorization: Bearer <api-key>` custom header. We translate, classify the User-Agent through the same catalog the JS beacon uses, and store recognised AI-crawler requests with `channel=vercel`.

Accepts both delivery encodings — a JSON array or NDJSON — sniffed from the body rather than from Content-Type, which Vercel does not guarantee; gzip is detected by magic bytes. Records without a `proxy` object (build output, function stdout, and the sample events Vercel posts when the drain is created) are counted as skipped, not errors.

DELIBERATELY FORGIVING: a delivery we cannot parse, or one we fail to persist, still returns 200. Vercel publishes no retry policy and flags a drain (emailing the customer that the endpoint is broken) past 80% failed deliveries. Only a bad token, a foreign site_id, a signature mismatch or a spent quota return a non-2xx.

Requires the Growth plan or higher and is metered against the same monthly server-log event cap as `/agents/ingest`.



## OpenAPI

````yaml /api-reference/public-openapi.json post /api/public/v1/agents/ingest/vercel
openapi: 3.1.0
info:
  title: FixAEO Customer API
  version: 2026.05.23
  description: >-
    Bearer-authed read API over your FixAEO workspace. Create a key in Settings,
    then send it as `Authorization: Bearer <key>`. Every route is scoped to the
    key's own account.


    Generated from the server's OpenAPI spec — see
    scripts/build-public-openapi.py.
servers:
  - url: https://api.fixaeo.com
security:
  - bearerAuth: []
paths:
  /api/public/v1/agents/ingest/vercel:
    post:
      tags:
        - Public API
        - Agents
      summary: Ingest a Vercel Drain delivery
      description: >-
        Receives a Vercel Drain (formerly "Log Drain") delivery in VERCEL'S OWN
        format, so a Vercel-hosted customer needs no code: they paste this URL
        into a drain and add one `Authorization: Bearer <api-key>` custom
        header. We translate, classify the User-Agent through the same catalog
        the JS beacon uses, and store recognised AI-crawler requests with
        `channel=vercel`.


        Accepts both delivery encodings — a JSON array or NDJSON — sniffed from
        the body rather than from Content-Type, which Vercel does not guarantee;
        gzip is detected by magic bytes. Records without a `proxy` object (build
        output, function stdout, and the sample events Vercel posts when the
        drain is created) are counted as skipped, not errors.


        DELIBERATELY FORGIVING: a delivery we cannot parse, or one we fail to
        persist, still returns 200. Vercel publishes no retry policy and flags a
        drain (emailing the customer that the endpoint is broken) past 80%
        failed deliveries. Only a bad token, a foreign site_id, a signature
        mismatch or a spent quota return a non-2xx.


        Requires the Growth plan or higher and is metered against the same
        monthly server-log event cap as `/agents/ingest`.
      parameters:
        - name: site_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
          description: >-
            The brand's public site id (the same value used by the JS tag).
            Ownership-checked against the API key's account. May also be sent as
            an `X-FixAEO-Site` header.
        - name: verify
          in: query
          required: false
          schema:
            type: string
          description: Echoed back as `x-vercel-verify` on every response.
        - name: x-vercel-signature
          in: header
          required: false
          schema:
            type: string
          description: >-
            Hex HMAC-SHA1 of the raw body using the drain's signing secret.
            Vercel makes the secret optional; verification is enforced only when
            a secret has been saved for the brand, and then a missing or
            mismatched signature is a 403.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/VercelLogRecord'
          application/x-ndjson:
            schema:
              type: string
              description: One JSON log record per line.
      responses:
        '200':
          description: >-
            Delivery handled. Also returned for unparseable bodies and for
            deliveries containing no request records.
          content:
            application/json:
              schema:
                type: object
                properties:
                  accepted:
                    type: integer
                    description: Crawler hits stored
                  scanned:
                    type: integer
                    description: Records seen in the delivery
                  skipped:
                    type: integer
                    description: Records that were not AI-crawler requests
                  monthly_used:
                    type: integer
                  monthly_cap:
                    type: integer
        '400':
          description: Missing site_id.
        '401':
          description: Missing or invalid API key.
        '403':
          description: >-
            Site not owned by this account, signature mismatch, or the plan is
            not entitled to server-log ingestion.
        '404':
          description: Unknown site_id.
        '429':
          description: Monthly server-log event cap reached.
      security:
        - bearerAuth: []
components:
  schemas:
    VercelLogRecord:
      type: object
      description: >-
        One record from Vercel's log schema. Only the fields we read are listed.
        A record is treated as an HTTP request ONLY when `proxy` is present with
        a method — `source: lambda|edge` also covers function stdout, which
        carries no proxy object.
      properties:
        id:
          type: string
        source:
          type: string
          enum:
            - build
            - edge
            - lambda
            - static
            - external
            - firewall
            - redirect
        host:
          type: string
          description: Deployment hostname; prefer proxy.host
        timestamp:
          type: integer
          format: int64
          description: Unix MILLISECONDS
        proxy:
          type: object
          description: Present only for records describing a served HTTP request.
          properties:
            timestamp:
              type: integer
              format: int64
              description: Unix MILLISECONDS
            method:
              type: string
            host:
              type: string
              description: The customer-facing hostname
            path:
              type: string
              description: Request path INCLUDING the query string
            userAgent:
              type: array
              items:
                type: string
              description: An ARRAY, not a string.
            referer:
              type: string
            region:
              type: string
              description: Vercel edge region (e.g. sfo1) — NOT a country
            statusCode:
              type: integer
              description: '-1 is a sentinel (crash / background revalidation), not a status'
            clientIp:
              type: string
              description: Absent when the team hides IPs in Drains
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: fxa_
      description: |
        API keys generated via `POST /api/v1/me/api-keys`. Format:
        `Authorization: Bearer fxa_<32-char-secret>`. Only accepted on
        `/api/public/v1/*`. Cookie auth is rejected on that surface.

````