asyncapi: 3.0.0
info:
  title: Simli WebSockets
  version: 1.0.0
servers:
  Simli WSS:
    host: "api.simli.ai"
    protocol: wss
    variables:
      session_token:
        description: Session token generated by https://api.simli.ai/compose/token

defaultContentType: text/plain
channels:
  peer_to_peer:
    summary: |
      Peer to Peer WebRTC 

      [Compose Session Token](/api-reference/compose-session-token) is sent as query parameter string

      enableSFU is a query parameter boolean to opt into sending the traffic through cloudflare network for improved reliability

      First message must be offer. Offer must be provided within 30 seconds of opening the websocket. Once offer is sent, a corresponding answer will be sent from the server which can be used as described [here](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/setRemoteDescription)
    address: /compose/webrtc/peer_to_peer
    messages:
      offer:
        $ref: "#/components/messages/rtc_offer"
        description: "RTC Offer. Must be first message"
      answer:
        $ref: "#/components/messages/rtc_answer"
        description: "RTC Offer. Must be first message"
      audio_chunk:
        $ref: "#/components/messages/audio_chunk"
      clear_buffer:
        $ref: "#/components/messages/clear_buffer"
      final_message:
        $ref: "#/components/messages/final_message"
      event:
        $ref: "#/components/messages/event"
      error:
        $ref: "#/components/messages/error"

  livekit:
    summary: |
      Livekit Backed WebRTC

      [Compose Session Token](/api-reference/compose-session-token) is sent as query parameter string

      No first message is required. However, You must successfully join the livekit room within 30 seconds
    address: /compose/webrtc/livekit
    messages:
      livekit_join_info:
        $ref: "#/components/messages/livekit_join_info"
      audio_chunk:
        $ref: "#/components/messages/audio_chunk"
      clear_buffer:
        $ref: "#/components/messages/clear_buffer"
      final_message:
        $ref: "#/components/messages/final_message"
      event:
        $ref: "#/components/messages/event"
      error:
        $ref: "#/components/messages/error"

operations:
  sendOffer:
    action: send
    channel:
      $ref: "#/channels/peer_to_peer"
    messages:
      - $ref: "#/channels/peer_to_peer/messages/offer"
  recvAnswer:
    action: receive
    messages:
      - $ref: "#/channels/peer_to_peer/messages/answer"
    channel:
      $ref: "#/channels/peer_to_peer"
  sendAudioChunk_peer_to_peer:
    action: send
    channel:
      $ref: "#/channels/peer_to_peer"
    messages:
      - $ref: "#/channels/peer_to_peer/messages/audio_chunk"
  clearBuffer_peer_to_peer:
    action: send
    channel:
      $ref: "#/channels/peer_to_peer"
    messages:
      - $ref: "#/channels/peer_to_peer/messages/clear_buffer"
  finalMessage_peer_to_peer:
    action: send
    channel:
      $ref: "#/channels/peer_to_peer"
    messages:
      - $ref: "#/channels/peer_to_peer/messages/final_message"
  event_peer_to_peer:
    action: receive
    channel:
      $ref: "#/channels/peer_to_peer"
    messages:
      - $ref: "#/channels/peer_to_peer/messages/event"
  error_peer_to_peer:
    action: receive
    channel:
      $ref: "#/channels/peer_to_peer"
    messages:
      - $ref: "#/channels/peer_to_peer/messages/error"

  recvLivekitInfo:
    action: receive
    channel:
      $ref: "#/channels/livekit"
    messages:
      - $ref: "#/channels/livekit/messages/livekit_join_info"
  sendAudioChunk_livekit:
    action: send
    channel:
      $ref: "#/channels/livekit"
    messages:
      - $ref: "#/channels/livekit/messages/audio_chunk"

  clearBuffer_livekit:
    action: send
    channel:
      $ref: "#/channels/livekit"
    messages:
      - $ref: "#/channels/livekit/messages/clear_buffer"
  finalMessage_livekit:
    action: send
    channel:
      $ref: "#/channels/livekit"
    messages:
      - $ref: "#/channels/livekit/messages/final_message"
  event_livekit:
    action: receive
    channel:
      $ref: "#/channels/livekit"
    messages:
      - $ref: "#/channels/livekit/messages/event"
  error_livekit:
    action: receive
    channel:
      $ref: "#/channels/livekit"
    messages:
      - $ref: "#/channels/livekit/messages/error"

components:
  messages:
    rtc_offer:
      description: RTC Offer as generated by https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createOffer or compatible
      payload:
        schema:
          type: object
          properties:
            type:
              type: string
              const: "offer"
            sdp:
              type: string
    audio_chunk:
      description: Binary stream of the audio that the Avatar will lipsync to. Sent as a binary stream (in JS/TS UInt8Arr)
      contentType: application/octet-stream
      payload:
        schema:
          type: string
          format: bytestring
    clear_buffer:
      description: "Client Signal to make the avatar stop speaking immediately and ignore all previously sent audio"
      payload:
        schema:
          type: string
          const: SKIP
    final_message:
      description: "Client Signal to tell the server that the final segment of audio has been already sent and that it should close after playing it back"
      payload:
        schema:
          type: string
          const: DONE

    event:
      description: "Server sent signals"
      payload:
        schema:
          type: string
          enum:
            - START
            - ACK
            - STOP
            - SPEAK
            - SILENT
    rtc_answer:
      description: RTC Answer as generated by https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/createAnswer or compatible. Will be received AFTER an offer has been sent
      payload:
        schema:
          type: object
          properties:
            type:
              type: string
              const: "answer"
            sdp:
              type: string
    livekit_join_info:
      description: Room info and join token that are used by the client to join a livekit room that hosts the avatar session
      payload:
        schema:
          type: object
          properties:
            livekit_url:
              type: string
            livekit_token:
              type: string
    error:
      description: Termination messages that specify the reason for immediate termination
      payload:
        schema:
          type: string # Wrapping in an object often fixes the 'properties' read error
          oneOf:
            - pattern: '^(?i)ERROR\s+.+$'
              description: "Matches 'ERROR' regardless of casing."
            - pattern: '^(?i)RATE\s+.+$'
              description: "Matches 'RATE' followed by a space."
            - pattern: '^(?i)CLOSING\s+.+$'
              description: "Matches 'CLOSING' followed by a space."
        example: "ERROR: INVALID_API_KEY"
