{
  "asyncapi": "3.0.0",
  "info": {
    "title": "Voice Relay Web-Socket",
    "version": "1.0.1",
    "description": "Enables real-time voice-to-text and text-to-speech communication between the Sinch platform and customer endpoints. \nThe Voice Relay service transcribes incoming audio to text, relays it to your AI chatbot or agent system for processing, \nand synthesizes text responses back into audio for playback on active voice channels."
  },
  "channels": {
    "outboundStream": {
      "messages": {
        "ConnectRequest": {
          "$ref": "#/components/messages/ConnectRequest"
        },
        "AnswerResponse": {
          "$ref": "#/components/messages/AnswerResponse"
        },
        "TextMessage": {
          "$ref": "#/components/messages/TextMessage"
        },
        "TextResponseMessage": {
          "$ref": "#/components/messages/TextResponseMessage"
        },
        "InterruptDetect": {
          "$ref": "#/components/messages/InterruptDetect"
        },
        "Interrupt": {
          "$ref": "#/components/messages/Interrupt"
        },
        "DtmfReceive": {
          "$ref": "#/components/messages/DtmfReceive"
        },
        "DtmfSend": {
          "$ref": "#/components/messages/DtmfSend"
        },
        "PlaybackEvent": {
          "$ref": "#/components/messages/PlaybackEvent"
        }
      }
    }
  },
  "operations": {
    "connect": {
      "summary": "Connection initiated from the Sinch Platform",
      "description": "This is the first message sent from the Sinch platform for all outgoing stream connections. This message carries information about the call, including call headers, the Call-ID, Service-ID, etc. Based on the information that this command carries, the customer can make a decision if the request should be accepted or not.",
      "action": "send",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/ConnectRequest"
        }
      ]
    },
    "answer": {
      "summary": "Server accepts the connection",
      "description": "Server response accepting the connection. \nSend this acknowledgment before the platform starts streaming transcription results or processing text-to-speech messages.",
      "action": "receive",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/AnswerResponse"
        }
      ]
    },
    "text": {
      "summary": "Sinch sends the transcribed text from the audio channel to the server",
      "description": "Speech from incoming audio channel is transcribed and the text is sent to the server.",
      "action": "send",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/TextMessage"
        }
      ]
    },
    "textResponse": {
      "summary": "Server sends a text message to be synthesized and played on the active voice channel.",
      "description": "Sent from the server to the Sinch platform to synthesize text into speech and play it\non the active voice channel.",
      "action": "receive",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/TextResponseMessage"
        }
      ]
    },
    "interruptDetect": {
      "summary": "TTS interruption due to detected speech",
      "description": "Speech detected while TTS is played on the audio channel.",
      "action": "send",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/InterruptDetect"
        }
      ]
    },
    "interrupt": {
      "summary": "Stop TTS playback and clear buffers",
      "description": "Active TTS playback will be interrupted on the audio channel.",
      "action": "receive",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/Interrupt"
        }
      ]
    },
    "dtmfReceived": {
      "summary": "DTMF received",
      "description": "A DTMF tone or sequence was received.",
      "action": "send",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/DtmfReceive"
        }
      ]
    },
    "dtmfSend": {
      "summary": "Send DTMF tone or sequence.",
      "description": "Sends a single DTMF tone or a sequence or tones over the audio channel.",
      "action": "receive",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/DtmfSend"
        }
      ]
    },
    "playbackEvent": {
      "summary": "Sinch platform reports the playback status of a TTS batch.",
      "description": "Sent from the Sinch platform to the server when the playback status of a TTS batch changes on the active voice channel.",
      "action": "send",
      "channel": {
        "$ref": "#/channels/outboundStream"
      },
      "messages": [
        {
          "$ref": "#/channels/outboundStream/messages/PlaybackEvent"
        }
      ]
    }
  },
  "components": {
    "messages": {
      "ConnectRequest": {
        "payload": {
          "$ref": "#/components/schemas/ConnectRequestPayload"
        }
      },
      "AnswerResponse": {
        "payload": {
          "$ref": "#/components/schemas/AnswerResponsePayload"
        }
      },
      "TextMessage": {
        "payload": {
          "$ref": "#/components/schemas/TextMessagePayload"
        }
      },
      "TextResponseMessage": {
        "payload": {
          "$ref": "#/components/schemas/TextResponseMessagePayload"
        }
      },
      "InterruptDetect": {
        "payload": {
          "$ref": "#/components/schemas/InterruptDetectPayload"
        }
      },
      "Interrupt": {
        "payload": {
          "$ref": "#/components/schemas/InterruptPayload"
        }
      },
      "DtmfReceive": {
        "payload": {
          "$ref": "#/components/schemas/DtmfReceivePayload"
        }
      },
      "DtmfSend": {
        "payload": {
          "$ref": "#/components/schemas/DtmfSendPayload"
        }
      },
      "PlaybackEvent": {
        "payload": {
          "$ref": "#/components/schemas/TextPlaybackEventPayload"
        }
      }
    },
    "schemas": {
      "ConnectRequestPayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Connection request message sent from the Sinch platform to establish a Voice Relay WebSocket session.\n\nContains session metadata including call identification, language settings, and TTS configuration.\nThe server must respond with an `answer` command to proceed, or disconnect to decline the connection.",
        "required": [
          "command",
          "version",
          "callId",
          "serviceId",
          "sttLanguage",
          "ttsVoice",
          "interruptionsEnabled",
          "callHeaders"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "connect",
            "description": "Connect message"
          },
          "version": {
            "type": "integer",
            "const": 1,
            "description": "Protocol version"
          },
          "callId": {
            "type": "string",
            "pattern": "^[0-9A-HJKMNP-TV-Z]{26}$",
            "description": "Unique Call ID"
          },
          "serviceId": {
            "type": "string",
            "format": "uuid",
            "description": "Service Unique ID"
          },
          "sttLanguage": {
            "type": "string",
            "description": "BCP-47 language tag used for speech-to-text transcription of the inbound audio.\n\nThis is the language set in the SVAML command when the voice relay session was initiated.",
            "example": "en-US"
          },
          "ttsVoice": {
            "type": "string",
            "description": "Name of the default voice that will be used when synthesizing speech.\n\nThis is the TTS voice name set in the SVAML command when the voice relay session was initiated."
          },
          "interruptionsEnabled": {
            "type": "boolean",
            "description": "Indicates if automatic interruption on detected speech is enabled for this session."
          },
          "callHeaders": {
            "type": "object",
            "description": "Arbitrary key/value pairs (header name -> header value).",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "example": {
          "command": "connect",
          "version": 1,
          "callId": "01KHKF468K4P1T3CZ4XJ1NYVN3",
          "serviceId": "5c90f98a-5002-4f8a-9b02-c1cb552bed95",
          "sttLanguage": "en-US",
          "ttsVoice": "Joanna",
          "interruptionsEnabled": true,
          "callHeaders": {
            "key1": "value1",
            "key2": "value2"
          }
        }
      },
      "AnswerResponsePayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Server response accepting the connection. \n\nMust be sent before the platform begins streaming transcribed text and processing text-to-speech messages.",
        "required": [
          "command"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "answer"
          }
        }
      },
      "TextMessagePayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Message sent from the Sinch platform to the server when incoming audio is detected and successfully transcribed to text.\n\nContains the transcribed text, language tag, and a flag indicating whether this message corrects a previous transcription.",
        "required": [
          "command",
          "text",
          "sttLanguage",
          "isCorrection"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "text"
          },
          "text": {
            "type": "string",
            "description": "Transcribed text from the voice channel. This is the speech-to-text output of the incoming audio."
          },
          "sttLanguage": {
            "type": "string",
            "description": "BCP-47 language tag used for speech-to-text transcription of the inbound audio."
          },
          "isCorrection": {
            "type": "boolean",
            "description": "Indicates whether this message corrects a previous transcription. \n\nThis occurs when additional speech is detected after a pause, refining the earlier transcription result."
          }
        },
        "example": {
          "command": "text",
          "text": "Hello there, can you help me with this task?",
          "sttLanguage": "en-US",
          "isCorrection": false
        }
      },
      "TextResponseMessagePayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Command sent from the server to the Sinch platform to synthesize text into speech\nand play it on the active voice channel.\n\nText can be streamed in chunks by sending multiple messages in sequence. Set `isLast`\nto `false` on intermediate messages, and `true` on the final message to signal the end\nof the batch and trigger an audio flush.\n\nThe TTS voice is determined by the first message in a batch. Any `ttsVoice` value on\nsubsequent messages in the same batch is ignored. If `ttsVoice` is omitted, the default\nvoice configured when the session was initiated is used.\n\nEven when `isInterruptible` is `false`, playback can still be stopped by explicitly\nsending an `interrupt` command over the WebSocket.",
        "required": [
          "command",
          "text",
          "isLast"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "text"
          },
          "text": {
            "type": "string",
            "description": "Plain text or SSML to synthesize and play on the active voice channel.\n\nWhen streaming, send this field in multiple messages with `isLast: false` until\nthe final chunk, which must have `isLast: true`.\n\nWhen using SSML, tags must not span across message boundaries within a batch.\nEach chunk must be independently valid XML if SSML is used."
          },
          "format": {
            "type": "string",
            "enum": [
              "TEXT",
              "SSML"
            ],
            "default": "TEXT",
            "x-enumDescriptions": {
              "TEXT": "Plain text format for synthesis",
              "SSML": "XML-based Speech Synthesis Markup Language for advanced control over prosody, phonemes, and audio effects"
            }
          },
          "ttsVoice": {
            "type": "string",
            "description": "Name of the TTS voice to use for this batch.\n\nOnly the value from the first message in a batch takes effect; this field is ignored\non subsequent messages in the same batch. If omitted, the default voice configured\nwhen the session was initiated is used."
          },
          "isLast": {
            "type": "boolean",
            "description": "Indicates whether this is the final message in the current TTS batch.\n\nSet to `false` on all intermediate chunks and `true` on the last chunk to signal\nthe end of the batch and trigger an audio flush. The next message with `isLast: false`\nbegins a new batch."
          },
          "isInterruptible": {
            "type": "boolean",
            "description": "Controls whether speech detected on the voice channel can automatically interrupt\nthis TTS batch.\n\nOverrides the `enableInterruptions` setting from the session for this batch only.\nEven when set to `false`, playback can still be stopped by explicitly sending an\n`interrupt` command over the WebSocket. Defaults to `true` when not specified."
          }
        },
        "example": {
          "command": "text",
          "text": "This is a test message",
          "ttsVoice": "Emma",
          "isLast": true,
          "isInterruptible": true
        }
      },
      "InterruptDetectPayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Sent from the Sinch platform when speech is detected during text-to-speech playback.\n\nIf the TTS message is marked as interruptible, playback stops automatically. \nUninterruptible messages continue playing but can be manually interrupted using an `interrupt` command.",
        "required": [
          "command",
          "reason"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "interrupt"
          },
          "reason": {
            "type": "string",
            "enum": [
              "speech-detected"
            ],
            "description": "The reason that triggered the interrupt message.",
            "x-enumDescriptions": {
              "speech-detected": "Indicates that the interrupt signal was triggered by the detection of active speech \non the voice channel. This typically occurs when the system detects audio input that \nexceeds the speech activity threshold, signaling potential user input."
            }
          }
        },
        "example": {
          "command": "interrupt",
          "reason": "speech-detected"
        }
      },
      "InterruptPayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Command sent from the server to the Sinch platform to interrupt text-to-speech playback.\n\nStops any ongoing TTS message batch immediately, regardless of whether the batch is marked as interruptible.\nUse this to allow manual interruption of non-interruptible messages or to cancel playback on demand.",
        "required": [
          "command"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "interrupt"
          }
        },
        "example": {
          "command": "interrupt"
        }
      },
      "DtmfReceivePayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Message sent from the Sinch platform when DTMF tones are detected on the voice channel.\n\nContains the sequence of DTMF digits received during the active call.",
        "required": [
          "command",
          "sequence"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "dtmf"
          },
          "sequence": {
            "type": "string",
            "description": "A single or multiple DTMF tones."
          }
        },
        "example": {
          "command": "dtmf",
          "sequence": "123"
        }
      },
      "DtmfSendPayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Command sent from the server to the Sinch platform to send DTMF tones to the voice channel.\n\nAllows playback of a sequence of DTMF digits on the active call.",
        "required": [
          "command",
          "sequence"
        ],
        "properties": {
          "command": {
            "type": "string",
            "const": "dtmf"
          },
          "sequence": {
            "type": "string",
            "description": "A single or multiple DTMF tones."
          }
        },
        "example": {
          "command": "dtmf",
          "sequence": "123"
        }
      },
      "TextPlaybackEventPayload": {
        "type": "object",
        "additionalProperties": false,
        "description": "Event sent from the Sinch platform to the server to report the playback status of a TTS batch\non the active voice channel.",
        "required": [
          "command",
          "batchSequence"
        ],
        "properties": {
          "command": {
            "type": "string",
            "description": "Identifies the type of playback event.",
            "enum": [
              "textPlaybackStart",
              "textPlaybackStop",
              "textPlaybackCancel"
            ],
            "x-enumDescriptions": {
              "textPlaybackStart": "Emitted when the Sinch platform begins playing the TTS batch on the voice channel.\nA `textPlaybackStop` or `textPlaybackCancel` event will always follow.",
              "textPlaybackStop": "Emitted when playback of the TTS batch has ended. This event is sent regardless of\nwhether the batch played to completion or was cut short by an `interrupt` command\nor detected speech.",
              "textPlaybackCancel": "Emitted when a queued TTS batch is discarded before playback begins. This occurs\nwhen an `interrupt` command is received while the batch is still waiting in the\nplayback queue."
            }
          },
          "batchSequence": {
            "type": "integer",
            "description": "Auto-incrementing sequence number that identifies the TTS batch this event relates to.\n\nThe sequence starts at `0` for the first batch sent after the session is accepted and\nincrements by `1` for each subsequent batch. Use this value to correlate playback\nevents with the `textResponse` messages sent from the server."
          }
        }
      }
    }
  }
}