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

# WebSocket event stream

> Upgrade to a WebSocket. Events are filtered by the same scope as REST, so a panel never receives an organisation it could not query. The server pings every 30 seconds; reconnect on close. Events travel through Redis pub/sub, so a panel connected to one instance still sees calls handled by another.

AUTHENTICATION FROM A BROWSER. `new WebSocket(url)` cannot set headers, so a browser presents its token as a subprotocol instead: `new WebSocket(url, ["firetone.bearer." + token])`. The server selects and echoes that protocol back. A token in the query string is deliberately NOT accepted -- URLs reach access logs, Referer headers and browser history. Any client that can set headers should keep using `Authorization: Bearer`.

THE STREAM IS BEST-EFFORT AND MUST NOT BE TREATED AS COMPLETE. A client whose buffer is full has events DROPPED rather than queued, so anything driven only by this stream will drift and never recover. Keep a slow poll of the authoritative endpoint alongside it; the stream is a latency optimisation, not a source of truth.



## OpenAPI

````yaml /api-reference/openapi-public.json get /events
openapi: 3.0.3
info:
  description: >-
    The API your own systems use: place and follow calls, have an AI agent call
    someone, run campaigns and get their results, keep contacts in step with
    your CRM, and receive signed webhooks. Authenticate with an integration key
    (Authorization: Bearer ft_...), used only from the IP addresses it allows.
  title: FireTone API
  version: 0.1.0
servers:
  - description: Your platform's API host
    url: https://{host}/api/v1
    variables:
      host:
        default: api.firet.one
security:
  - bearerAuth: []
tags:
  - name: Auth
  - description: >-
      Live calls and what can be done to them: hang up, hold, transfer, park,
      merge, monitor, whisper; the Desk's own call.
    name: Calls
  - description: 'Outbound campaigns: contacts, attempts, outcomes.'
    name: Campaigns
  - description: 'Customers: who called, what is known about them, and their memory.'
    name: Contacts
  - description: What was said on an AI call, and the review of it.
    name: Conversations
  - description: >-
      Your own systems: HTTP connections an IVR calls mid-call, and webhooks for
      call events. Tenant URLs must be public https addresses.
    name: Integrations
  - name: Live
  - name: Provisioning
  - name: Reporting
  - description: Tickets raised by people, agents and the API.
    name: Tickets
  - description: Messages left for an extension or a queue.
    name: Voicemail
paths:
  /events:
    get:
      tags:
        - Live
      summary: WebSocket event stream
      description: >-
        Upgrade to a WebSocket. Events are filtered by the same scope as REST,
        so a panel never receives an organisation it could not query. The server
        pings every 30 seconds; reconnect on close. Events travel through Redis
        pub/sub, so a panel connected to one instance still sees calls handled
        by another.


        AUTHENTICATION FROM A BROWSER. `new WebSocket(url)` cannot set headers,
        so a browser presents its token as a subprotocol instead: `new
        WebSocket(url, ["firetone.bearer." + token])`. The server selects and
        echoes that protocol back. A token in the query string is deliberately
        NOT accepted -- URLs reach access logs, Referer headers and browser
        history. Any client that can set headers should keep using
        `Authorization: Bearer`.


        THE STREAM IS BEST-EFFORT AND MUST NOT BE TREATED AS COMPLETE. A client
        whose buffer is full has events DROPPED rather than queued, so anything
        driven only by this stream will drift and never recover. Keep a slow
        poll of the authoritative endpoint alongside it; the stream is a latency
        optimisation, not a source of truth.
      operationId: getEvents
      responses:
        '101':
          description: Switching protocols
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  responses:
    Unauthorized:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Missing, invalid or expired token
  schemas:
    Error:
      properties:
        error:
          properties:
            code:
              enum:
                - invalid_request
                - invalid_credentials
                - unauthenticated
                - forbidden
                - not_found
                - conflict
                - internal
              type: string
            message:
              type: string
          required:
            - code
            - message
          type: object
      required:
        - error
      type: object
  securitySchemes:
    bearerAuth:
      bearerFormat: JWT or API key
      description: >-
        Every request sends `Authorization: Bearer <token>`. The token is either
        a panel session (a JWT from /auth/login, 12 hours) or an API key
        `ft_<id>_<secret>`. An API key is accepted only from an address on its
        IP allowlist (403 ip_not_allowed otherwise; 403 ip_allowlist_required
        for an old key that has none), is limited to its rate per minute (429
        rate_limited with Retry-After; X-RateLimit-Limit/Remaining/Reset on
        every response), and at most 60 call placements a minute.
      scheme: bearer
      type: http

````