Hoot Lens Sites API

The Sites API from Hoot Lens — 2 operation(s) for sites.

GitHub OpenAPI

Operations 3

GET /v1/projects List sites #
POST /v1/projects Create a site #
PATCH /v1/projects/{projectId} Update site settings #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/hootlens-com:hootlens-com-sites-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

hootlens-com-sites-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hoot Lens Sites API
  version: v1
  summary: Read website interaction evidence and log changes.
  description: 'The Hoot Lens REST API records website interaction evidence for the sites you add and returns it as visits, recordings, heatmaps, goals, funnels and change impact. This document describes the endpoints a person or an agent uses to read that evidence and to log changes. Everything returned is observed behavior with its evidence; the API makes no claims about visitors'' emotions or intent.


    Authentication. Send `Authorization: Bearer `. The credential is either a Firebase ID token (a signed-in person, whose role on each site decides what they may do) or a personal access token (`hl_pat_...`) made in the dashboard. A personal access token carries the scopes `read`, `replay` and `annotate`, belongs to one site, expires in 30, 60 or 90 days, and can do no more than its creator''s current role allows. Operations marked `x-hootlens-sign-in-only` need a signed-in person and answer 403 to a token.


    OAuth. The hosted MCP endpoint (`/mcp`, Streamable HTTP) signs assistants in with OAuth 2.1 authorization code and PKCE (S256), with discovery at `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`. Its access tokens (`hl_oat_...`) are bound to the MCP resource and are accepted only there; sending one to any route in this document answers 401. Use a personal access token or a Firebase ID token for these routes.


    Not described here. Collector intake, public tracker configuration, billing, OAuth, sharing links, issues and feedback, invitations, members, access tokens, notifications and the audit log also exist; see docs/API.md for them.


    Site (project) IDs are public. Authorization is enforced by the API on every request. Rate limits answer 429. A caller who is not a member of a site receives 404, so a site''s existence is not revealed.'
  contact:
    name: Parallel Platforms
    url: https://hootlens.com
servers:
- url: https://hootlens-api-368877148687.us-east1.run.app
  description: Production
security:
- bearerAuth: []
tags:
- name: Sites
paths:
  /v1/projects:
    get:
      tags:
      - Sites
      summary: List sites
      description: 'Sites (projects in the API) the caller owns or belongs to, each with its role and the permissions it gives there. A personal access token sees only its own site.


        Required token scopes are listed in x-hootlens-scopes. A signed-in person needs the matching permission on the site instead (the viewer and editor roles are presets of the permissions).'
      operationId: listProjects
      responses:
        '200':
          description: The sites.
          content:
            application/json:
              schema:
                type: object
                properties:
                  projects:
                    type: array
                    items:
                      $ref: '#/components/schemas/ProjectWithRole'
                required:
                - projects
                additionalProperties: true
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      security:
      - bearerAuth: []
      x-hootlens-scopes:
      - read
    post:
      tags:
      - Sites
      summary: Create a site
      description: 'Adds a website to the authenticated account, within its current site allowance. A verified signed-in account owner may create directly. An OAuth agent must have explicitly approved sites:create and all-project access; site-bound personal access tokens and demo readers receive 403. Third-party OAuth tokens call through the hosted MCP endpoint, while first-party local MCP/CLI sign-in can call REST directly. Agents start with balanced privacy (strict is also allowed) and required consent. Agents cannot request full privacy or implied consent. Origins reject credentials, query parameters and fragments. Account IDs and owner IDs are not accepted in the body. Idempotency-Key is required for OAuth agents and optional for people. For 24 hours the same account, key and normalized settings return the existing site without consuming another slot; different settings return 409. Expired keys may create a new site. Revocation and current email verification are checked before a replay. Idempotency-Replayed is true on replay and false on new creation. Site creation produces configuration and does not prove a tracking tag has been installed.


        Required token scopes are listed in x-hootlens-scopes. A signed-in person needs the matching permission on the site instead (the viewer and editor roles are presets of the permissions).'
      operationId: createProject
      parameters:
      - name: Idempotency-Key
        in: header
        required: false
        description: Required for OAuth agents. Reuse the same key and settings when retrying within 24 hours.
        schema:
          type: string
          minLength: 8
          maxLength: 128
          pattern: ^[!-~]{8,128}$
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectCreate'
      responses:
        '200':
          description: The existing site for this account and request key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  project:
                    $ref: '#/components/schemas/ProjectWithRole'
                required:
                - project
                additionalProperties: true
        '201':
          description: The new site.
          content:
            application/json:
              schema:
                type: object
                properties:
                  project:
                    $ref: '#/components/schemas/ProjectWithRole'
                required:
                - project
                additionalProperties: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      security:
      - bearerAuth: []
      x-hootlens-scopes:
      - sites:create
  /v1/projects/{projectId}:
    patch:
      tags:
      - Sites
      summary: Update site settings
      description: 'Changes the name, allowed origins, preview origin patterns, privacy policy, consent mode, sample rate, page targeting, per-path privacy presets (pathPresets), page groups, grouping, retention, AI explanations, JavaScript error recording or internal traffic setting. The owner may change every field; an editor may change only pageGroups and grouping. Personal access tokens and connected apps receive 403, whatever their scopes: agents use the settings changes below, which can only make privacy stricter.


        Required token scopes are listed in x-hootlens-scopes. A signed-in person needs the matching permission on the site instead (the viewer and editor roles are presets of the permissions).'
      operationId: updateProject
      parameters:
      - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectUpdate'
      responses:
        '200':
          description: The updated site.
          content:
            application/json:
              schema:
                type: object
                properties:
                  project:
                    $ref: '#/components/schemas/ProjectWithRole'
                required:
                - project
                additionalProperties: true
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
      security:
      - bearerAuth: []
      x-hootlens-scopes: []
      x-hootlens-sign-in-only: true
components:
  schemas:
    Goal:
      allOf:
      - oneOf:
        - oneOf:
          - type: object
            properties:
              kind:
                type: string
                const: click
              name:
                type: string
                minLength: 1
                maxLength: 60
              by:
                type: string
                const: selector
              match:
                type: string
                minLength: 1
                maxLength: 200
            required:
            - kind
            - name
            - by
            - match
            additionalProperties: false
          - type: object
            properties:
              kind:
                type: string
                const: click
              name:
                type: string
                minLength: 1
                maxLength: 60
              by:
                type: string
                const: hootId
              match:
                type: string
                pattern: ^[a-zA-Z0-9_-]{1,80}$
            required:
            - kind
            - name
            - by
            - match
            additionalProperties: false
          - type: object
            properties:
              kind:
                type: string
                const: click
              name:
                type: string
                minLength: 1
                maxLength: 60
              by:
                type: string
                const: label
              match:
                type: string
                minLength: 2
                maxLength: 200
            required:
            - kind
            - name
            - by
            - match
            additionalProperties: false
        - type: object
          properties:
            kind:
              type: string
              const: page
            name:
              type: string
              minLength: 1
              maxLength: 60
            match:
              type: string
          required:
          - kind
          - name
          - match
          additionalProperties: false
        - type: object
          properties:
            kind:
              type: string
              const: event
            name:
              type: string
              minLength: 1
              maxLength: 60
            match:
              type: string
              pattern: ^[a-z][a-z0-9_]{0,63}$
            confirmation:
              type: string
              enum:
              - browser
              - server
          required:
          - kind
          - name
          - match
          - confirmation
          additionalProperties: false
      - type: object
        properties:
          id:
            type: string
            pattern: ^[a-zA-Z0-9_-]{1,80}$
          since:
            type: integer
            minimum: 0
            maximum: 9007199254740991
        required:
        - id
        - since
        additionalProperties: false
    Error:
      type: object
      properties:
        error:
          type: string
          description: A short message safe to show to a person.
        code:
          type: string
          description: A stable machine-readable code, on some errors.
      required:
      - error
      additionalProperties: true
    ProjectCreate:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 80
        origins:
          minItems: 1
          maxItems: 20
          type: array
          items:
            type: string
            format: uri
        privacy:
          type: object
          properties:
            preset:
              type: string
              enum:
              - full
              - balanced
              - strict
          required:
          - preset
        consent:
          type: string
          enum:
          - implied
          - required
        replayAssets:
          type: object
          properties:
            preserve:
              default: true
              type: boolean
            origins:
              default: []
              maxItems: 20
              type: array
              items:
                type: string
                maxLength: 300
          additionalProperties: false
      required:
      - name
      - origins
      additionalProperties: false
    Funnel:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 60
        steps:
          minItems: 2
          maxItems: 8
          type: array
          items:
            type: object
            properties:
              goalId:
                type: string
                pattern: ^[a-zA-Z0-9_-]{1,80}$
            required:
            - goalId
            additionalProperties: false
        id:
          type: string
          pattern: ^[a-zA-Z0-9_-]{1,80}$
        createdAt:
          type: integer
          minimum: 0
          maximum: 9007199254740991
      required:
      - name
      - steps
      - id
      - createdAt
      additionalProperties: false
    PathPreset:
      type: object
      description: safe-form records clicks and steps but never what a visitor typed, even on the full preset. balanced and strict apply at least that preset on matching pages.
      properties:
        match:
          type: string
          description: A page path pattern in the page targeting syntax.
        preset:
          type: string
          enum:
          - safe-form
          - balanced
          - strict
      required:
      - match
      - preset
      additionalProperties: true
    ElementDisplayName:
      type: object
      properties:
        key:
          type: string
          description: The element's selector, as findings and heatmaps list it.
        name:
          type: string
          description: What the team calls the element.
        updatedAt:
          type: integer
          description: Epoch milliseconds.
          minimum: 0
        updatedBy:
          type: string
          description: The user who last set it.
      required:
      - key
      - name
      - updatedAt
      additionalProperties: true
    ProjectUpdate:
      type: object
      properties:
        name:
          type: string
          minLength: 1
          maxLength: 80
        origins:
          minItems: 1
          maxItems: 20
          type: array
          items:
            type: string
            format: uri
        previewOrigins:
          maxItems: 10
          type: array
          items:
            type: string
            maxLength: 300
        privacy:
          type: object
          properties:
            preset:
              type: string
              enum:
              - full
              - balanced
              - strict
            maskTextSelectors:
              default: []
              maxItems: 50
              type: array
              items:
                type: string
                minLength: 1
                maxLength: 200
            blockSelectors:
              default: []
              maxItems: 50
              type: array
              items:
                type: string
                minLength: 1
                maxLength: 200
            unmaskInputSelectors:
              default: []
              maxItems: 20
              type: array
              items:
                type: string
                minLength: 1
                maxLength: 200
            captureQueryStrings:
              default: false
              type: boolean
            keepCampaignDigits:
              type: boolean
          required:
          - preset
        replayAssets:
          type: object
          properties:
            preserve:
              default: true
              type: boolean
            origins:
              default: []
              maxItems: 20
              type: array
              items:
                type: string
                maxLength: 300
          additionalProperties: false
        consent:
          type: string
          enum:
          - implied
          - required
          - regional
        consentRegions:
          maxItems: 60
          type: array
          items:
            type: string
            maxLength: 8
        sampleRate:
          type: number
          minimum: 0
          maximum: 1
        targeting:
          type: object
          properties:
            include:
              default: []
              maxItems: 50
              type: array
              items:
                type: string
            exclude:
              default: []
              maxItems: 50
              type: array
              items:
                type: string
        pathPresets:
          maxItems: 20
          type: array
          items:
            type: object
            properties:
              match:
                type: string
              preset:
                type: string
                enum:
                - safe-form
                - balanced
                - strict
            required:
            - match
            - preset
        pageGroups:
          maxItems: 50
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                pattern: ^[a-z0-9][a-z0-9-]{0,39}$
              name:
                type: string
                minLength: 1
                maxLength: 60
              match:
                minItems: 1
                maxItems: 20
                type: array
                items:
                  type: string
            required:
            - id
            - name
            - match
        grouping:
          type: object
          properties:
            localePrefixes:
              maxItems: 300
              type: array
              items:
                type: string
                pattern: ^[a-z]{2,3}(?:-[a-z]{2,4})?$
            variantParams:
              maxItems: 20
              type: array
              items:
                type: string
                pattern: ^[A-Za-z0-9_.-]{1,40}$
            pageVersionMode:
              type: string
              enum:
              - release
              - content
        retentionDays:
          type: integer
          minimum: 1
          maximum: 90
        ai:
          type: object
          properties:
            explanations:
              type: boolean
          required:
          - explanations
        internalTraffic:
          type: object
          properties:
            browser:
              type: boolean
            ips:
              default: []
              maxItems: 30
              type: array
              items:
                type: string
                maxLength: 60
          required:
          - browser
        errorCapture:
          type: boolean
    Id:
      type: string
      description: 'An identifier: 1 to 80 letters, digits, underscores or hyphens.'
      pattern: ^[a-zA-Z0-9_-]{1,80}$
    ProjectWithRole:
      allOf:
      - $ref: '#/components/schemas/Project'
      - type: object
        properties:
          role:
            type: string
            enum:
            - owner
            - editor
            - viewer
            - custom
          permissions:
            type: array
            items:
              type: string
              enum:
              - read
              - replay
              - annotate
              - configure
              - tokens
              - member
              - invite
              - conversions:write
              - manage
        required:
        - role
        - permissions
        additionalProperties: true
    Project:
      type: object
      properties:
        id:
          $ref: '#/components/schemas/Id'
        name:
          type: string
        origins:
          type: array
          items:
            type: string
            format: uri
        ownerId:
          type: string
        createdAt:
          type: integer
          description: Epoch milliseconds.
          minimum: 0
        retentionDays:
          type: integer
        privacy:
          type: object
          description: The privacy policy. Absent on sites created before configurable privacy, which are strict.
          properties:
            preset:
              type: string
              enum:
              - full
              - balanced
              - strict
            keepCampaignDigits:
              type: boolean
              description: Keep digits in utm_ values. Off when absent.
          required:
          - preset
          additionalProperties: true
        internalTraffic:
          type: object
          description: How the site's own team is told apart from visitors. Absent when nobody is.
          properties:
            browser:
              type: boolean
              description: Visits from browsers that opened the internal link are internal.
            ips:
              type: array
              items:
                type: string
              description: Listed IP addresses or ranges. Only the owner sees them.
          required:
          - browser
          - ips
          additionalProperties: true
        consent:
          type: string
          enum:
          - implied
          - required
          - regional
          description: Absent on sites created before configurable consent, which require consent.
        previewOrigins:
          type: array
          items:
            type: string
          description: Host patterns for pull request previews, such as https://deploy-preview-*--mysite.netlify.app. They may send test visits only. Absent means none.
        consentRegions:
          type: array
          items:
            type: string
        sampleRate:
          type: number
          description: 0 to 1.
        ai:
          type: object
          properties:
            explanations:
              type: boolean
          required:
          - explanations
          additionalProperties: true
        errorCapture:
          type: boolean
          description: Whether the site records JavaScript errors. On for sites created since the setting was added; absent (off) on earlier sites until the owner turns it on.
        replayAssets:
          type: object
          description: Public asset preservation for new recordings. Existing sites without this field use live assets until an owner enables preservation.
          properties:
            preserve:
              type: boolean
            origins:
              type: array
              items:
                type: string
          required:
          - preserve
          - origins
          additionalProperties: true
        targeting:
          type: object
          description: Page paths to record. Absent records every path.
          properties:
            include:
              type: array
              items:
                type: string
            exclude:
              type: array
              items:
                type: string
          required:
          - include
          - exclude
          additionalProperties: true
        pathPresets:
          type: array
          items:
            $ref: '#/components/schemas/PathPreset'
          description: Per-path privacy presets. They only ever make capture stricter than the site preset. Absent means none.
        pageGroups:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              match:
                type: array
                items:
                  type: string
            additionalProperties: true
        grouping:
          type: object
          description: Locale prefix, experiment parameter and page version mode overrides.
          properties: {}
          additionalProperties: true
        goals:
          type: array
          items:
            $ref: '#/components/schemas/Goal'
        funnels:
          type: array
          items:
            $ref: '#/components/schemas/Funnel'
        savedSegments:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
            additionalProperties: true
        elementNames:
          type: array
          items:
            $ref: '#/components/schemas/ElementDisplayName'
        updatedAt:
          type: integer
          description: Epoch milliseconds.
          minimum: 0
      required:
      - id
      - name
      - origins
      - createdAt
      - retentionDays
      additionalProperties: true
  responses:
    Unauthorized:
      description: The credential is missing, expired or revoked.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PaymentRequired:
      description: The site (project) or account plan does not allow this.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Conflict:
      description: The request conflicts with the current state of the record.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: The site, or the record inside it, does not exist or the caller is not a member (a non-member cannot tell the two apart).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    TooManyRequests:
      description: A rate limit or a per-site limit was reached.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: The credential or the person's permissions on the site do not permit this action.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: The request fields are invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  parameters:
    projectId:
      name: projectId
      in: path
      required: true
      description: The project ID. Project IDs are public identifiers, not secrets.
      schema:
        $ref: '#/components/schemas/Id'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A Firebase ID token, or a personal access token (hl_pat_...) with the scopes read, replay, annotate, configure, conversions:write. OAuth access tokens (hl_oat_...) are for the hosted MCP resource only.
externalDocs:
  description: Full API reference, including endpoints not described here
  url: https://hootlens.com/docs/API.md