Modulate · AsyncAPI Specification

Velma 2 STT Streaming API

Version 0.0.0

Real-time speech-to-text over WebSocket. Provides live multilingual transcription with automatic per-utterance language detection, delivering each utterance as it is completed. Optional capabilities - speaker diarization, emotion detection, accent detection, synthetic voice (deepfake) detection, PII/PHI tagging, and partial (interim) results - are selected via query parameters.

View Spec View on GitHub CompanyAiVoice AISpeech to TextTranscriptionDeepfake DetectionContent ModerationTrust and SafetyAudioMachine LearningPII RedactionVoice IntelligenceAsyncAPIWebhooksEvents

Channels

velma2SttStreaming
Establish a WebSocket connection for real-time transcription with automatic language detection. The service automatically detects the language for each utterance in real-time. Different utterances within the same stream may be identified as different languages if code-switching occurs. ## Connection Flow 1. Connect with `api_key` and optional query parameters 2. Connection will be accepted if authentication succeeds and limits are not exceeded 3. Optionally send a JSON configuration as the first text frame, before any audio. If the first frame is binary it is treated as the first audio chunk and the query-parameter defaults apply. 4. Stream audio data as binary WebSocket frames 5. Receive utterance results as JSON messages 6. Send empty text frame ("") when audio stream ends 7. Receive final "done" message with total audio duration 8. Connection closes automatically after completion ## Error Handling If an error occurs during transcription, the server sends an error message and closes the connection. Connection rejections during handshake use WebSocket close codes. ## WebSocket Close Codes - `1000`: Normal closure after the `done` message, or after the connection completes. - `1003`: Invalid connection parameters (e.g. unsupported `audio_format`, invalid `sample_rate` or `num_channels`, an invalid `language`, or a raw format missing `sample_rate`/`num_channels`), or an invalid first-frame configuration (malformed JSON or a value outside the documented configuration schema). An `error` message is sent before the close. - `4003`: The request could not be validated, or the request is not permitted. An `error` message is sent before the close. - `4029`: The request could not be completed due to insufficient credits. An `error` message is sent before the close.

Messages

AudioChunk
Audio Data Chunk
Raw audio data chunk for real-time transcription
EndOfStream
End of Audio Stream
Empty text frame signaling end of audio stream
ConfigFrame
Stream Configuration
Optional JSON configuration sent as the FIRST WebSocket text frame, before any audio. When the first frame is text it is parsed as this configuration; when the first frame is binary it is treated as the first audio chunk and the query-parameter defaults apply. Backward compatible: binary-first clients need not send it. A field present here overrides the matching query parameter.
UtteranceResult
Utterance Result
A completed utterance with detected language
PartialUtteranceResult
Partial Utterance Result
In-progress transcription text (interim, not yet finalized)
ErrorResponse
Error
An error occurred during transcription
DoneResult
Transcription Complete
Transcription completed successfully

Servers

wss
production
Modulate STT Streaming Server

AsyncAPI Specification

Raw ↑
asyncapi: 3.0.0
info:
  title: Velma 2 STT Streaming API
  version: 0.0.0
  description: |
    Real-time speech-to-text over WebSocket. Provides live multilingual
    transcription with automatic per-utterance language detection, delivering
    each utterance as it is completed. Optional capabilities - speaker
    diarization, emotion detection, accent detection, synthetic voice (deepfake)
    detection, PII/PHI tagging, and partial (interim) results - are selected via
    query parameters.
servers:
  production:
    host: platform.modulate.ai
    protocol: wss
    description: Modulate STT Streaming Server
channels:
  velma2SttStreaming:
    address: /api/velma-2-stt-streaming
    description: >
      Establish a WebSocket connection for real-time transcription with
      automatic language

      detection. The service automatically detects the language for each
      utterance in

      real-time. Different utterances within the same stream may be identified
      as different

      languages if code-switching occurs.


      ## Connection Flow

      1. Connect with `api_key` and optional query parameters

      2. Connection will be accepted if authentication succeeds and limits are
      not exceeded

      3. Optionally send a JSON configuration as the first text frame, before
      any audio.
         If the first frame is binary it is treated as the first audio chunk and the
         query-parameter defaults apply.
      4. Stream audio data as binary WebSocket frames

      5. Receive utterance results as JSON messages

      6. Send empty text frame ("") when audio stream ends

      7. Receive final "done" message with total audio duration

      8. Connection closes automatically after completion


      ## Error Handling

      If an error occurs during transcription, the server sends an error message
      and closes

      the connection. Connection rejections during handshake use WebSocket close
      codes.


      ## WebSocket Close Codes

      - `1000`: Normal closure after the `done` message, or after the
        connection completes.
      - `1003`: Invalid connection parameters (e.g. unsupported `audio_format`,
        invalid `sample_rate` or `num_channels`, an invalid `language`, or a raw
        format missing `sample_rate`/`num_channels`), or an invalid first-frame
        configuration (malformed JSON or a value outside the documented
        configuration schema). An `error` message is sent before the close.
      - `4003`: The request could not be validated, or the request is not
        permitted. An `error` message is sent before the close.
      - `4029`: The request could not be completed due to insufficient credits.
        An `error` message is sent before the close.
    bindings:
      ws:
        query:
          type: object
          required:
            - api_key
          properties:
            api_key:
              type: string
              description: >
                Your API key for authentication. Required. Must be provided as a
                query

                parameter when establishing the WebSocket connection; the
                connection is

                rejected during the handshake if it is missing.
            speaker_diarization:
              type: boolean
              default: true
              description: >
                Enable speaker diarization to identify different speakers in the
                audio stream.

                When enabled, each utterance includes a speaker identifier
                (1-indexed integer).
            emotion_signal:
              type: boolean
              default: false
              description: >
                Enable emotion detection for each utterance. When enabled, the
                `emotion` field

                in each utterance will contain the detected emotional tone of
                the speaker.
            accent_signal:
              type: boolean
              default: false
              description: >
                Enable accent detection for each utterance. When enabled, the
                `accent` field

                in each utterance will contain the detected accent of the
                speaker.
            deepfake_signal:
              type: boolean
              default: false
              description: >
                Enable synthetic voice (deepfake) detection for each utterance.
                When enabled,

                the `deepfake_score` field in each utterance will contain a
                score from 0.0

                (likely natural) to 1.0 (likely synthetic).
            pii_phi_tagging:
              type: boolean
              default: false
              description: >
                Enable PII/PHI tagging. When enabled, personally identifiable
                information and

                personal health information in the transcribed text will be
                wrapped with

                appropriate tags.
            audio_format:
              type: string
              description: >
                Audio encoding format. Determines how the service interprets
                incoming audio data.


                **Self-describing formats** (`wav`, `mp3`, `ogg`, `flac`,
                `webm`, `aac`, `aiff`):

                These include container formats (AIFF, OGG, WAV, WebM) and
                codecs whose streams

                carry per-frame headers (AAC/ADTS, FLAC, MP3). When omitted
                (default), the service

                auto-detects the format from these headers. You may specify the
                format explicitly

                to override auto-detection. Do not specify `sample_rate` or
                `num_channels` for

                self-describing formats - the headers already carry this
                metadata.


                **Raw/headerless formats** (`s8`..`f64be`, `mulaw`, `alaw`):

                These formats are raw sample data with no headers. When using a
                raw format, you

                must also specify `sample_rate` and `num_channels` so the
                service knows how to

                interpret the byte stream. Omitting either will result in an
                error.
              enum:
                - s8
                - s16le
                - s16be
                - s24le
                - s24be
                - s32le
                - s32be
                - u8
                - u16le
                - u16be
                - u24le
                - u24be
                - u32le
                - u32be
                - f32le
                - f32be
                - f64le
                - f64be
                - mulaw
                - alaw
                - wav
                - mp3
                - ogg
                - flac
                - webm
                - aac
                - aiff
            sample_rate:
              type: integer
              description: >
                Audio sample rate in Hz. Required when `audio_format` is a
                raw/headerless format

                (PCM, mu-law, A-law). Do not specify for self-describing formats
                (WAV, MP3, OGG,

                etc.) - their headers already include this metadata. Cannot be
                specified without

                `audio_format`.
              enum:
                - 8000
                - 11025
                - 16000
                - 22050
                - 32000
                - 44100
                - 48000
                - 96000
            num_channels:
              type: integer
              minimum: 1
              maximum: 8
              description: >
                Number of audio channels (1-8). Required when `audio_format` is
                a raw/headerless

                format (PCM, mu-law, A-law). Do not specify for self-describing
                formats (WAV, MP3,

                OGG, etc.) - their headers already include this metadata. Use 1
                for mono, 2 for

                stereo. Cannot be specified without `audio_format`.
            partial_results:
              type: boolean
              default: false
              description: >
                Enable partial (interim) transcription results. When enabled,
                the server sends

                `partial_utterance` messages containing in-progress text as
                speech is being

                recognized, in addition to the finalized `utterance` messages.
                Each partial also

                carries the latest interim `emotion`, `accent`, and
                `deepfake_score` for the

                utterance when those signals are enabled; each is null until a
                value is

                available. Each partial replaces the previous partial; when the
                finalized

                `utterance` arrives, it supersedes all preceding partials.
            language:
              type: string
              pattern: ^[A-Za-z]{2,3}([-_][A-Za-z0-9]+)*$
              description: >
                Optional language hint as a case-insensitive ISO 639-1 code
                (e.g. "en",

                "fr"). BCP 47 region/script subtags (e.g. "en-US") are accepted;
                only the

                primary language subtag is used. When omitted, the language is
                detected

                automatically for each utterance.
    messages:
      streamConfig:
        $ref: '#/components/messages/ConfigFrame'
      audioChunk:
        $ref: '#/components/messages/AudioChunk'
      endOfStream:
        $ref: '#/components/messages/EndOfStream'
      utteranceResult:
        $ref: '#/components/messages/UtteranceResult'
      errorResult:
        $ref: '#/components/messages/ErrorResponse'
      doneResult:
        $ref: '#/components/messages/DoneResult'
      partialUtteranceResult:
        $ref: '#/components/messages/PartialUtteranceResult'
operations:
  streamAudio:
    action: send
    channel:
      $ref: '#/channels/velma2SttStreaming'
    summary: Stream audio for real-time transcription with automatic language detection
    messages:
      - $ref: '#/channels/velma2SttStreaming/messages/streamConfig'
      - $ref: '#/channels/velma2SttStreaming/messages/audioChunk'
      - $ref: '#/channels/velma2SttStreaming/messages/endOfStream'
  receiveTranscription:
    action: receive
    channel:
      $ref: '#/channels/velma2SttStreaming'
    summary: Receive transcription results from the server
    messages:
      - $ref: '#/channels/velma2SttStreaming/messages/utteranceResult'
      - $ref: '#/channels/velma2SttStreaming/messages/partialUtteranceResult'
      - $ref: '#/channels/velma2SttStreaming/messages/errorResult'
      - $ref: '#/channels/velma2SttStreaming/messages/doneResult'
components:
  messages:
    AudioChunk:
      name: audioChunk
      title: Audio Data Chunk
      summary: Raw audio data chunk for real-time transcription
      contentType: application/octet-stream
      payload:
        type: string
        format: binary
    EndOfStream:
      name: endOfStream
      title: End of Audio Stream
      summary: Empty text frame signaling end of audio stream
      contentType: text/plain
      payload:
        type: string
        const: ''
      examples:
        - name: endSignal
          payload: ''
    ConfigFrame:
      name: configFrame
      title: Stream Configuration
      summary: >
        Optional JSON configuration sent as the FIRST WebSocket text frame,

        before any audio. When the first frame is text it is parsed as this

        configuration; when the first frame is binary it is treated as the first

        audio chunk and the query-parameter defaults apply. Backward compatible:

        binary-first clients need not send it. A field present here overrides
        the

        matching query parameter.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/STTStreamConfig'
    UtteranceResult:
      name: utteranceResult
      title: Utterance Result
      summary: A completed utterance with detected language
      contentType: application/json
      payload:
        type: object
        required:
          - type
          - utterance
        properties:
          type:
            type: string
            const: utterance
          utterance:
            $ref: '#/components/schemas/StreamingUtterance'
      examples:
        - name: frenchUtterance
          payload:
            type: utterance
            utterance:
              utterance_uuid: b2c3d4e5-f6a7-8901-bcde-f12345678901
              text: Bonjour, comment allez-vous?
              start_ms: 0
              duration_ms: 2800
              speaker: 1
              language: fr
              emotion: null
              accent: null
              deepfake_score: null
    PartialUtteranceResult:
      name: partialUtteranceResult
      title: Partial Utterance Result
      summary: In-progress transcription text (interim, not yet finalized)
      contentType: application/json
      payload:
        type: object
        required:
          - type
          - partial_utterance
        properties:
          type:
            type: string
            const: partial_utterance
          partial_utterance:
            $ref: '#/components/schemas/PartialUtterance'
      examples:
        - name: partialExample
          payload:
            type: partial_utterance
            partial_utterance:
              text: Hello, how are
              start_ms: 0
              speaker: 1
              emotion: null
              accent: null
              deepfake_score: null
    ErrorResponse:
      name: errorResult
      title: Error
      summary: An error occurred during transcription
      contentType: application/json
      payload:
        type: object
        required:
          - type
          - error
        properties:
          type:
            type: string
            const: error
          error:
            type: string
            description: Human-readable error message
      examples:
        - name: internalError
          payload:
            type: error
            error: Internal server error
        - name: invalidInputAudio
          payload:
            type: error
            error: Invalid input audio
        - name: invalidConfig
          payload:
            type: error
            error: Invalid configuration.
        - name: notPermitted
          payload:
            type: error
            error: This request is not permitted.
        - name: insufficientCredits
          payload:
            type: error
            error: Insufficient credits.
        - name: validationUnavailable
          payload:
            type: error
            error: Unable to validate request. Please try again.
        - name: invalidAudioFormat
          payload:
            type: error
            error: >-
              Invalid audio_format='mp4'. Valid values: ['aac', 'aiff', 'alaw',
              'f32be', 'f32le', 'f64be', 'f64le', 'flac', 'mp3', 'mulaw', 'ogg',
              's16be', 's16le', 's24be', 's24le', 's32be', 's32le', 's8',
              'u16be', 'u16le', 'u24be', 'u24le', 'u32be', 'u32le', 'u8', 'wav',
              'webm']
        - name: rawFormatMissingParams
          payload:
            type: error
            error: >-
              Raw audio formats require sample_rate and num_channels query
              parameters
        - name: paramsWithoutFormat
          payload:
            type: error
            error: sample_rate and num_channels require audio_format to be specified
        - name: invalidSampleRate
          payload:
            type: error
            error: >-
              Invalid sample_rate=50000. Valid values: [8000, 11025, 16000,
              22050, 32000, 44100, 48000, 96000]
        - name: invalidNumChannels
          payload:
            type: error
            error: Invalid num_channels=12. Must be between 1 and 8
    DoneResult:
      name: doneResult
      title: Transcription Complete
      summary: Transcription completed successfully
      contentType: application/json
      payload:
        type: object
        required:
          - type
          - duration_ms
        properties:
          type:
            type: string
            const: done
          duration_ms:
            type: integer
            minimum: 0
            description: Total duration of audio processed in milliseconds
      examples:
        - name: completionExample
          payload:
            type: done
            duration_ms: 45000
  schemas:
    StreamingUtterance:
      type: object
      required:
        - utterance_uuid
        - text
        - start_ms
        - duration_ms
        - speaker
        - language
        - emotion
        - accent
        - deepfake_score
      properties:
        utterance_uuid:
          type: string
          format: uuid
          description: >
            Unique identifier for this utterance. Can be used for tracking and
            correlating

            utterances across systems.
          example: a1b2c3d4-e5f6-7890-abcd-ef1234567890
        text:
          type: string
          description: >
            The transcribed text for this utterance. Represents a single
            continuous segment

            of speech detected in the audio stream. May contain PII/PHI tags if
            tagging

            is enabled. Always present; may be an empty string.
          example: Hello, how are you today?
        start_ms:
          type: integer
          minimum: 0
          description: >
            The start time of this utterance in milliseconds, relative to the
            beginning

            of the audio stream. Allows precise synchronization with the
            original audio timing.
          example: 0
        duration_ms:
          type: integer
          minimum: 0
          description: The duration of this utterance in milliseconds.
          example: 2500
        speaker:
          type: integer
          description: >
            Speaker identifier for this utterance (1-indexed). When speaker
            diarization is

            enabled, this identifies which speaker produced this utterance.
            Speaker numbers

            are consistent within a single connection but may vary between
            different connections.
          example: 1
        language:
          type: string
          description: >
            The automatically detected language code for this utterance (e.g.,
            "en" for English,

            "fr" for French).
          example: en
        emotion:
          oneOf:
            - type: string
            - type: 'null'
          description: >
            The detected emotional tone of the speaker for this utterance. Only
            present when

            `emotion_signal` is enabled. Possible values include: Neutral, Calm,
            Happy, Amused,

            Excited, Proud, Affectionate, Interested, Hopeful, Frustrated,
            Angry, Contemptuous,

            Concerned, Afraid, Sad, Ashamed, Bored, Tired, Surprised, Anxious,
            Stressed,

            Disgusted, Disappointed, Confused, Relieved, Confident.
          example: Neutral
        accent:
          oneOf:
            - type: string
            - type: 'null'
          description: >
            The detected accent of the speaker for this utterance. Only present
            when

            `accent_signal` is enabled. Possible values include: American,
            British, Australian,

            Southern, Indian, Irish, Scottish, Eastern_European, African, Asian,

            Latin_American, Middle_Eastern, Unknown.
          example: American
        deepfake_score:
          oneOf:
            - type: number
              format: double
              minimum: 0
              maximum: 1
            - type: 'null'
          description: >
            Synthetic voice (deepfake) detection score for this utterance. Null
            if deepfake

            detection is disabled or if the utterance is too short for analysis
            (< 0.5 seconds).

            Values range from 0.0 (likely natural human speech) to 1.0 (likely
            AI-generated

            synthetic speech).
          example: 0.05
    PartialUtterance:
      type: object
      required:
        - text
        - start_ms
        - speaker
        - emotion
        - accent
        - deepfake_score
      properties:
        text:
          type: string
          description: >
            In-progress transcription text. This is a preview that may change as
            more

            audio is processed. Each new partial replaces the previous one. The
            finalized

            utterance supersedes all preceding partials. Always present; may be
            an empty string.
          example: Hello, how are you
        start_ms:
          oneOf:
            - type: integer
              minimum: 0
            - type: 'null'
          description: >
            Start time of the in-progress speech segment in milliseconds,
            relative to the

            beginning of the audio stream. May be null if timing data is not yet
            available.
          example: 0
        speaker:
          oneOf:
            - type: integer
            - type: 'null'
          description: >
            Speaker identifier (1-indexed). Matches the speaker numbering used
            in

            finalized utterances. May be null if speaker has not yet been
            identified.
          example: 1
        emotion:
          oneOf:
            - type: string
            - type: 'null'
          description: >
            Latest interim emotion label for the in-progress utterance, or null
            when the

            emotion signal is not enabled or no value is available yet.
        accent:
          oneOf:
            - type: string
            - type: 'null'
          description: >
            Latest interim accent label for the in-progress utterance, or null
            when the

            accent signal is not enabled or no value is available yet.
        deepfake_score:
          oneOf:
            - type: number
              format: double
              minimum: 0
              maximum: 1
            - type: 'null'
          description: >
            Latest interim deepfake score for the in-progress utterance, or null
            when the

            deepfake signal is not enabled or no value is available yet.
    STTStreamConfig:
      type: object
      description: >-
        Optional first-frame stream configuration. Every field is optional; a
        field present here overrides the matching query parameter.
      properties:
        speaker_diarization:
          type: boolean
        emotion_signal:
          type: boolean
        accent_signal:
          type: boolean
        deepfake_signal:
          type: boolean
        pii_phi_tagging:
          type: boolean
        partial_results:
          type: boolean
        language:
          oneOf:
            - type: string
              pattern: ^[A-Za-z]{2,3}([-_][A-Za-z0-9]+)*$
            - type: 'null'
        custom_terms:
          description: >-
            Optional custom vocabulary to bias transcription toward domain terms
            and names. Each entry is either a plain string (the term) or a
            CustomTerm object. Blank terms and blank pronunciations are dropped.
            At most 1000 entries, and the term strings serialized together as
            JSON must total under 8000 characters. Null or omitted means no
            biasing.
          oneOf:
            - type: 'null'
            - type: array
              maxItems: 1000
              items:
                oneOf:
                  - type: string
                  - $ref: '#/components/schemas/CustomTerm'
    CustomTerm:
      type: object
      required:
        - term
      properties:
        term:
          type: string
          minLength: 1
          description: The vocabulary term to bias transcription toward.
        definition:
          type: string
          maxLength: 256
          description: Optional short definition of the term.
        pronunciations:
          type: array
          maxItems: 16
          items:
            type: string
            maxLength: 128
          description: >-
            Optional pronunciation hints, as X-SAMPA notation or
            hyphen-separated ASCII respelling with the stressed syllable in
            capitals (e.g. GOO-guhl). Printable ASCII only.