Ironfang Analytics Recordings API

Recordings of one tab within a session: search, what the visitor's browser reported, the batches to play them back from, live viewing and deletion. Every recording keeps the visitor's IP address and full user agent until it expires or is deleted; its session then keeps a note that it was there.

Operations 6

GET /v1/recordings Search recordings #
GET /v1/recordings/{recordingId} Get a recording #
DELETE /v1/recordings/{recordingId} Delete a recording #
GET /v1/recordings/{recordingId}/playback List a recording's playable batches #
POST /v1/recordings/{recordingId}/watch Take or renew the live-view lease #
GET /v1/recordings/{recordingId}/chunks/{epoch}/{sequence} Get one batch of events #

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/ironfang:ironfang-analytics-recordings-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

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

ironfang-analytics-recordings-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Ironfang Analytics Recordings API
  version: 0.1.0
  summary: Sites, origins, public keys and capture settings for session replay and web analytics.
  description: 'Ironfang Analytics follows each visit to your website as a session:

    the pages a visitor saw, in every tab, with how long they stayed, the

    errors and failed requests they met, the events your pages tracked, and

    the recordings that let you replay it, live or afterwards.'
  contact:
    name: Ironfang
    email: hello@ironfang.uk
    url: https://ironfang.com/contact
  termsOfService: https://ironfang.com/legal/terms
  license:
    name: Proprietary - use governed by the Ironfang terms of service
    url: https://ironfang.com/legal/terms
servers:
- url: https://api.ironfang.com/analytics
security:
- apiKey: []
- bearerAuth: []
tags:
- name: Recordings
  description: 'Recordings of one tab within a session: search, what the visitor''s browser reported, the batches to play them back from, live viewing and deletion. Every recording keeps the visitor''s IP address and full user agent until it expires or is deleted; its session then keeps a note that it was there.'
paths:
  /v1/recordings:
    get:
      tags:
      - Recordings
      operationId: listRecordings
      summary: Search recordings
      description: Returns the organisation's unexpired recordings across its sites, newest first, as a bounded keyset page. Every filter is optional and they combine. Needs analytics.sessions.read.
      parameters:
      - $ref: '#/components/parameters/Cursor'
      - $ref: '#/components/parameters/Limit'
      - name: site_id
        in: query
        required: false
        schema:
          type: string
          format: uuid
      - name: from
        in: query
        required: false
        description: Created at or after this time.
        schema:
          type: string
          format: date-time
      - name: to
        in: query
        required: false
        description: Created before this time.
        schema:
          type: string
          format: date-time
      - name: state
        in: query
        required: false
        description: One state, or several separated by commas.
        schema:
          type: string
          example: ready,partial
      - name: country
        in: query
        required: false
        description: Two-letter country code, upper case.
        schema:
          type: string
          pattern: ^[A-Z]{2}$
      - name: device
        in: query
        required: false
        schema:
          type: string
          example: mobile
      - name: browser
        in: query
        required: false
        schema:
          type: string
          example: chrome
      - name: os
        in: query
        required: false
        schema:
          type: string
          example: windows
      - name: ip
        in: query
        required: false
        description: One address, or a network in CIDR notation.
        schema:
          type: string
          example: 203.0.113.0/24
      - name: entry_path
        in: query
        required: false
        description: Entry paths starting with this.
        schema:
          type: string
          example: /apply
      - name: visited_path
        in: query
        required: false
        description: Recordings that showed this page at any point, by its path without a query string or fragment. Pages are indexed when a recording is processed for heatmaps, shortly after it ends; until then it does not match.
        schema:
          type: string
          example: /pricing
      - name: visitor_id
        in: query
        required: false
        description: One browser's recordings, by the visitor id on any of them.
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: A page of recordings
          content:
            application/json:
              schema:
                type: object
                required:
                - recordings
                - next_cursor
                properties:
                  recordings:
                    type: array
                    items:
                      $ref: '#/components/schemas/RecordingRow'
                  next_cursor:
                    type: string
                    description: Opaque cursor for the next page; empty on the last page.
        '400':
          $ref: '#/components/responses/InvalidQuery'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /v1/recordings/{recordingId}:
    parameters:
    - $ref: '#/components/parameters/RecordingId'
    get:
      tags:
      - Recordings
      operationId: getRecording
      summary: Get a recording
      description: One recording with everything kept about it and its epochs. Needs analytics.sessions.read.
      responses:
        '200':
          description: The recording
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordingDetail'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags:
      - Recordings
      operationId: deleteRecording
      summary: Delete a recording
      description: Deletes the recording's stored batches, then the recording with its IP address, user agent and every other detail. A recording deleted before it ended is not counted; one already counted stays counted. Needs analytics.sessions.delete.
      responses:
        '204':
          description: Recording deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          description: This deployment cannot delete recordings yet (`deletion_unavailable`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /v1/recordings/{recordingId}/playback:
    parameters:
    - $ref: '#/components/parameters/RecordingId'
    get:
      tags:
      - Recordings
      operationId: getPlayback
      summary: List a recording's playable batches
      description: 'The committed batches that can be played, in order: in each epoch, those before its first gap. Fetch each from the chunk endpoint. A live viewer passes `after`, the last batch it has, and gets only the batches since. Needs analytics.sessions.read.'
      parameters:
      - name: after
        in: query
        required: false
        description: Only batches after this one, as `<epoch>:<sequence>`.
        schema:
          type: string
          pattern: ^[0-9]+:[0-9]+$
          example: 0:12
      responses:
        '200':
          description: The batches
          content:
            application/json:
              schema:
                type: object
                required:
                - chunks
                properties:
                  chunks:
                    type: array
                    items:
                      $ref: '#/components/schemas/PlaybackChunk'
        '400':
          $ref: '#/components/responses/InvalidQuery'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/recordings/{recordingId}/watch:
    parameters:
    - $ref: '#/components/parameters/RecordingId'
    post:
      tags:
      - Recordings
      operationId: watchRecording
      summary: Take or renew the live-view lease
      description: 'Marks an open recording as watched for 30 seconds from now; renew it while the recording is on screen. While the lease holds, the collector tells the visitor''s recorder to upload every second instead of every ten, so new batches reach `playback` within a few seconds. That is all it changes: the visitor''s page shows nothing and behaves no differently. Needs analytics.sessions.read.'
      responses:
        '200':
          description: The lease
          content:
            application/json:
              schema:
                type: object
                required:
                - watched_until
                properties:
                  watched_until:
                    type: string
                    format: date-time
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: The recording has ended (`recording_ended`); replay it instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /v1/recordings/{recordingId}/chunks/{epoch}/{sequence}:
    parameters:
    - $ref: '#/components/parameters/RecordingId'
    - name: epoch
      in: path
      required: true
      schema:
        type: integer
        minimum: 0
    - name: sequence
      in: path
      required: true
      schema:
        type: integer
        minimum: 0
    get:
      tags:
      - Recordings
      operationId: getChunk
      summary: Get one batch of events
      description: 'The batch''s rrweb events as a JSON array, masked in the browser before they were sent. A gzip batch is served with Content-Encoding: gzip. The bytes are checked against the hash recorded when the batch was accepted. Needs analytics.sessions.read.'
      responses:
        '200':
          description: The events
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          description: This deployment cannot play recordings yet (`playback_unavailable`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  parameters:
    Cursor:
      name: cursor
      in: query
      required: false
      description: The next_cursor of the previous page.
      schema:
        type: string
    Limit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 50
    RecordingId:
      name: recordingId
      in: path
      required: true
      schema:
        type: string
        format: uuid
  responses:
    NotFound:
      description: No such object in this organisation (`not_found`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: The credential lacks the permission or scope, names another organisation, or belongs to a partner integration that is not active (`forbidden`, `insufficient_scope`, `integration_inactive`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: No credential, an invalid one, an expired partner integration key, or a delegated token where none is accepted (`unauthorized`, `invalid_api_key`, `api_key_expired`, `delegated_not_allowed`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InvalidQuery:
      description: An unknown, repeated or malformed query parameter (`invalid_query`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    PlaybackChunk:
      type: object
      required:
      - epoch_index
      - sequence
      - encoding
      - compressed_bytes
      - expanded_bytes
      - event_count
      - contains_full_snapshot
      - monotonic_start_ms
      - monotonic_end_ms
      - client_wall_time
      properties:
        epoch_index:
          type: integer
        sequence:
          type: integer
        encoding:
          type: string
          enum:
          - gzip
          - identity
        compressed_bytes:
          type: integer
        expanded_bytes:
          type: integer
        event_count:
          type: integer
        contains_full_snapshot:
          type: boolean
        monotonic_start_ms:
          type: number
        monotonic_end_ms:
          type: number
        client_wall_time:
          type:
          - string
          - 'null'
          format: date-time
          description: The browser's clock; for display only.
    Epoch:
      type: object
      required:
      - id
      - index
      - contiguous_through
      - max_sequence
      - has_checkpoint
      - bytes
      - created_at
      properties:
        id:
          type: string
          format: uuid
        index:
          type: integer
          description: A page load or a restart from a fresh checkpoint.
        contiguous_through:
          type: integer
          description: The highest sequence before the first gap; -1 when none.
        max_sequence:
          type: integer
        has_checkpoint:
          type: boolean
        bytes:
          type: integer
        created_at:
          type: string
          format: date-time
    RecordingDetail:
      allOf:
      - $ref: '#/components/schemas/RecordingRow'
      - type: object
        required:
        - config_version
        - sdk_version
        - rrweb_version
        - ip_source
        - user_agent
        - viewport
        - screen
        - pixel_ratio
        - language
        - timezone
        - referrer
        - epochs
        properties:
          config_version:
            type: integer
            description: The site configuration the recording was made under.
          sdk_version:
            type:
            - string
            - 'null'
          rrweb_version:
            type:
            - string
            - 'null'
          ip_source:
            type:
            - string
            - 'null'
            enum:
            - cloudflare
            - edge
            - peer
            - null
            description: cloudflare is the visitor's address as Cloudflare reported it; edge and peer are whoever connected, which is the visitor only when nothing sat in between.
          user_agent:
            type:
            - string
            - 'null'
            description: The full User-Agent header, at most 512 characters.
          viewport:
            oneOf:
            - $ref: '#/components/schemas/Size'
            - type: 'null'
          screen:
            oneOf:
            - $ref: '#/components/schemas/Size'
            - type: 'null'
          pixel_ratio:
            type:
            - number
            - 'null'
          language:
            type:
            - string
            - 'null'
          timezone:
            type:
            - string
            - 'null'
            description: IANA time zone the browser reported.
          referrer:
            type:
            - string
            - 'null'
            description: Origin and path only; the query and fragment are never kept.
          epochs:
            type: array
            items:
              $ref: '#/components/schemas/Epoch'
    Size:
      type: object
      required:
      - width
      - height
      properties:
        width:
          type: integer
        height:
          type: integer
    RecordingRow:
      type: object
      required:
      - id
      - site_id
      - site_name
      - state
      - end_reason
      - playable
      - origin
      - entry_path
      - browser
      - os
      - device
      - country
      - ip
      - bytes
      - chunks
      - events
      - duration_ms
      - created_at
      - first_received_at
      - last_received_at
      - ended_at
      - expires_at
      - last_seen_at
      - live
      - visitor_id
      - session_id
      properties:
        id:
          type: string
          format: uuid
        site_id:
          type: string
          format: uuid
        site_name:
          type: string
        session_id:
          type:
          - string
          - 'null'
          format: uuid
          description: The session the recording belongs to. Null only for a recording made before sessions that has not been placed in one yet.
        state:
          type: string
          enum:
          - pending
          - recording
          - ready
          - partial
          - unplayable
        end_reason:
          type:
          - string
          - 'null'
          enum:
          - finished
          - idle_timeout
          - duration_cap
          - byte_cap
          - site_disabled
          - null
        playable:
          type: boolean
          description: True once some epoch holds a checkpoint at its start.
        origin:
          type: string
        entry_path:
          type:
          - string
          - 'null'
        browser:
          type:
          - string
          - 'null'
          description: Coarse class from the user agent.
        os:
          type:
          - string
          - 'null'
        device:
          type:
          - string
          - 'null'
          enum:
          - desktop
          - mobile
          - tablet
          - null
        country:
          type:
          - string
          - 'null'
          description: From Cloudflare, when the request came through it.
        ip:
          type:
          - string
          - 'null'
          description: The visitor's IP address.
        bytes:
          type: integer
          description: Compressed bytes stored.
        chunks:
          type: integer
        events:
          type: integer
        duration_ms:
          type: integer
          description: 'The recorded activity, in milliseconds: in each page load, from its first batch''s start to its last batch''s end by the browser''s monotonic clock, summed.'
        created_at:
          type: string
          format: date-time
        first_received_at:
          type:
          - string
          - 'null'
          format: date-time
        last_received_at:
          type:
          - string
          - 'null'
          format: date-time
        last_seen_at:
          type: string
          format: date-time
          description: The last batch or heartbeat. An open page sends a heartbeat every 30 seconds.
        live:
          type: boolean
          description: 'Still open and heard from in the last 90 seconds: someone is on the page. Open but quiet means the visitor has most likely gone; the recording closes after 30 minutes without a word, because leaving a page cannot be told apart from moving to the next one.'
        visitor_id:
          type:
          - string
          - 'null'
          format: uuid
          description: 'The browser that made the recording: a random id the recorder (SDK 0.3.0 and later) keeps in the site''s localStorage. The same id on two recordings is the same browser, unless its storage was cleared in between. Null for recordings from older recorders.'
        ended_at:
          type:
          - string
          - 'null'
          format: date-time
        expires_at:
          type: string
          format: date-time
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          - docs
          properties:
            code:
              type: string
              description: Stable machine-readable code.
            message:
              type: string
              description: Human-readable explanation; do not parse.
            docs:
              type: string
              format: uri
        request_id:
          type: string
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: if_live_ platform API key
      description: 'A platform API key minted in the portal. Its scopes are its

        permissions - `analytics:sites:read`, `analytics:sites:write` and the

        other `analytics:` scopes, or `analytics:*` - and it acts only in the

        organisation it was minted for. Keys without the scope a route needs

        are refused with 403.

        '
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Warden JWT
      description: 'A Warden user token. Each route also requires a tenant permission in

        the acting tenant (`X-Ironfang-Tenant`), resolved live from Warden,

        which each operation names: sites need `analytics.sites.read` to read

        and `analytics.sites.write` to change; sessions, recordings and

        heatmaps need `analytics.sessions.read`, and deleting a session or a

        recording `analytics.sessions.delete`. A missing permission is 403; a

        permission lookup failure is 503.

        '
externalDocs:
  description: Ironfang Analytics documentation
  url: https://ironfang.com/analytics/docs