Papercups Realtime Chat API (WebSocket / Phoenix Channels)

Bidirectional realtime live chat over Phoenix WebSocket channels. Clients join conversation, conversation-lobby, and account-room channels and exchange shout (message), messages:seen, and presence events for two-way messaging.

AsyncAPI Specification

papercups-asyncapi.yml Raw ↑
asyncapi: '2.6.0'
id: 'urn:io:papercups:realtime:chat:websocket'
info:
  title: Papercups Realtime Chat (WebSocket / Phoenix Channels)
  version: '1.0.0'
  description: |
    AsyncAPI 2.6 description of Papercups' **realtime live chat** surface.

    Unlike many providers, Papercups exposes a genuine, bidirectional
    **WebSocket** transport: the chat widget and agent dashboard connect to a
    Phoenix Channels socket and exchange messages in both directions. This is
    documented at https://docs.papercups.io/chat and described in the
    open-source codebase at https://github.com/papercups-io/papercups.

    Phoenix Channels multiplex many logical topics over a single WebSocket
    connection at `/socket/websocket`. A customer typically joins three
    channels while interacting with live chat:

      * `conversation:{conversation_id}` - all messages for a given conversation
        are broadcast here; clients send/receive the `shout` event (a new
        message) and `messages:seen` (read receipts).
      * `conversation:lobby:{customer_id}` - notifies the customer of new
        conversations initiated by agents.
      * `room:{account_id}` - broadcasts agent availability via Phoenix
        Presence.

    Phoenix wraps every frame in a 5-element message envelope
    `[join_ref, ref, topic, event, payload]`. The lifecycle events
    `phx_join`, `phx_reply`, `phx_close`, `phx_error`, and `heartbeat`
    (on the `phoenix` topic) are part of the Phoenix Channels protocol.

    Papercups is in maintenance mode (community-maintained); the realtime
    surface reflects the documented and open-source behavior at time of review.
  contact:
    name: API Evangelist
    email: kin@apievangelist.com
    url: https://apievangelist.com
  license:
    name: MIT
    url: https://github.com/papercups-io/papercups/blob/master/LICENSE
  x-transport-notes:
    transport: WebSocket (Phoenix Channels)
    protocol: wss
    direction: bidirectional
    socketPath: /socket/websocket
    envelope: '[join_ref, ref, topic, event, payload]'
    heartbeat: 'event "heartbeat" on topic "phoenix" every ~30s'
    source: https://docs.papercups.io/chat
defaultContentType: application/json
servers:
  papercups-hosted:
    url: app.papercups.io/socket/websocket
    protocol: wss
    description: |
      Papercups hosted Phoenix Channels WebSocket endpoint. Self-hosted
      deployments expose the same `/socket/websocket` path on their own host.
      Connection params (e.g. customer or API token) are supplied as query
      params on the socket URL per Phoenix Channels conventions.
channels:
  'conversation:{conversation_id}':
    description: |
      Per-conversation channel. After `phx_join`, clients send new messages
      with the `shout` event and read receipts with `messages:seen`, and
      receive the same events broadcast from other participants (agent or
      customer).
    parameters:
      conversation_id:
        description: The conversation UUID.
        schema:
          type: string
    bindings:
      ws:
        bindingVersion: '0.1.0'
    publish:
      operationId: sendShout
      summary: Send a message (and read receipts) into the conversation.
      message:
        oneOf:
          - $ref: '#/components/messages/Shout'
          - $ref: '#/components/messages/MessagesSeen'
          - $ref: '#/components/messages/PhxJoin'
    subscribe:
      operationId: receiveConversationEvents
      summary: Receive broadcast messages and acknowledgements.
      message:
        oneOf:
          - $ref: '#/components/messages/Shout'
          - $ref: '#/components/messages/MessagesSeen'
          - $ref: '#/components/messages/PhxReply'
  'conversation:lobby:{customer_id}':
    description: |
      Lobby channel through which a customer is notified of new conversations
      started by agents.
    parameters:
      customer_id:
        description: The customer UUID.
        schema:
          type: string
    bindings:
      ws:
        bindingVersion: '0.1.0'
    subscribe:
      operationId: receiveLobbyEvents
      summary: Receive new-conversation notifications.
      message:
        $ref: '#/components/messages/Shout'
  'room:{account_id}':
    description: |
      Account room channel used to broadcast agent availability via Phoenix
      Presence.
    parameters:
      account_id:
        description: The account UUID.
        schema:
          type: string
    bindings:
      ws:
        bindingVersion: '0.1.0'
    subscribe:
      operationId: receivePresence
      summary: Receive Phoenix Presence state and diffs for agent availability.
      message:
        oneOf:
          - $ref: '#/components/messages/PresenceState'
          - $ref: '#/components/messages/PresenceDiff'
  phoenix:
    description: |
      Phoenix heartbeat topic. Clients send a `heartbeat` event roughly every
      30 seconds to keep the socket alive.
    bindings:
      ws:
        bindingVersion: '0.1.0'
    publish:
      operationId: sendHeartbeat
      summary: Keep the WebSocket connection alive.
      message:
        $ref: '#/components/messages/Heartbeat'
components:
  messages:
    Shout:
      name: shout
      title: New message (shout)
      summary: A new chat message broadcast to a conversation channel.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PhoenixEnvelope'
      examples:
        - name: customerMessage
          payload:
            - null
            - '3'
            - 'conversation:1a2b3c'
            - shout
            - body: 'Hi, I need help with my order'
              customer_id: 'cus_123'
              conversation_id: '1a2b3c'
    MessagesSeen:
      name: messages:seen
      title: Read receipt
      summary: Marks agent messages in the conversation as seen.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PhoenixEnvelope'
    PhxJoin:
      name: phx_join
      title: Join channel
      summary: Phoenix channel join request.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PhoenixEnvelope'
    PhxReply:
      name: phx_reply
      title: Channel reply
      summary: Phoenix acknowledgement reply (status ok/error).
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PhoenixEnvelope'
    PresenceState:
      name: presence_state
      title: Presence state
      summary: Full Phoenix Presence state for agent availability.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PhoenixEnvelope'
    PresenceDiff:
      name: presence_diff
      title: Presence diff
      summary: Incremental Phoenix Presence join/leave diff.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PhoenixEnvelope'
    Heartbeat:
      name: heartbeat
      title: Heartbeat
      summary: Phoenix socket keepalive on the `phoenix` topic.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/PhoenixEnvelope'
  schemas:
    PhoenixEnvelope:
      type: array
      description: >-
        Phoenix Channels v2 serializer envelope:
        [join_ref, ref, topic, event, payload].
      minItems: 5
      maxItems: 5
      items:
        - description: join_ref
          type: string
          nullable: true
        - description: ref
          type: string
          nullable: true
        - description: topic
          type: string
        - description: event
          type: string
        - description: payload
          type: object
          additionalProperties: true