Modulate · AsyncAPI Specification

Velma 2 Synthetic Voice Detection Streaming API

Version 0.0.0

Real-time synthetic voice detection for single-speaker audio over WebSocket. The client streams audio and receives per-frame verdicts as they become available.

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

Channels

streaming-detection
## WebSocket Close Codes - `1000`: Normal closure after the `done` message, or after the connection completes. - `1003`: Invalid or missing query parameters (unknown `audio_format`, invalid `sample_rate` or `num_channels`, a raw format missing `sample_rate`/`num_channels`, `sample_rate`/`num_channels` supplied without `audio_format`, or `audio_format` omitted). An `error` message is sent before the close. - `4002`: The audio could not be decoded, or does not match the declared raw `audio_format`. 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

audioData
Audio Data
A binary frame containing audio samples.
endOfStream
End of Stream
An empty text frame signalling that the client has finished sending audio.
frameResult
Frame Result
A single frame detection result.
done
Done
Analysis complete - all frames have been delivered.
error
Error
An error occurred during connection or analysis.

Servers

wss
production
Modulate Synthetic Voice Detection Streaming Server

AsyncAPI Specification

Raw ↑
asyncapi: 3.0.0
info:
  title: Velma 2 Synthetic Voice Detection Streaming API
  version: 0.0.0
  description: >
    Real-time synthetic voice detection for single-speaker audio over WebSocket.

    The client streams audio and receives per-frame verdicts as they become
    available.
servers:
  production:
    host: platform.modulate.ai
    protocol: wss
    description: Modulate Synthetic Voice Detection Streaming Server
channels:
  streaming-detection:
    address: /api/velma-2-synthetic-voice-detection-streaming
    description: |
      ## WebSocket Close Codes
      - `1000`: Normal closure after the `done` message, or after the
        connection completes.
      - `1003`: Invalid or missing query parameters (unknown `audio_format`,
        invalid `sample_rate` or `num_channels`, a raw format missing
        `sample_rate`/`num_channels`, `sample_rate`/`num_channels` supplied
        without `audio_format`, or `audio_format` omitted). An `error` message
        is sent before the close.
      - `4002`: The audio could not be decoded, or does not match the
        declared raw `audio_format`. 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
            - audio_format
          properties:
            api_key:
              type: string
              description: API key used for authentication and usage tracking.
            audio_format:
              type: string
              description: >
                Audio encoding format. Required for all connections. Raw formats

                (headerless samples) also require `sample_rate` and

                `num_channels`; container formats carry that metadata in the

                stream, so those parameters must not be supplied. The MP4-family

                values (`mp4`, `m4a`, `3gp`, `3gpp`) must be sent in a
                streamable

                layout; otherwise the connection ends with an audio-processing

                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
                - au
                - wma
                - opus
                - amr
                - mp4
                - m4a
                - 3gp
                - 3gpp
              examples:
                - s16le
                - f32le
                - mulaw
                - webm
            sample_rate:
              type: integer
              description: |
                Sample rate in Hz. **Required** when `audio_format` is a raw
                format. Must not be specified without `audio_format`.
              enum:
                - 8000
                - 11025
                - 16000
                - 22050
                - 32000
                - 44100
                - 48000
                - 96000
              examples:
                - 16000
                - 48000
            num_channels:
              type: integer
              description: |
                Number of audio channels. **Required** when `audio_format` is
                a raw format. Must be between 1 and 8. Must not be specified
                without `audio_format`.
              minimum: 1
              maximum: 8
              examples:
                - 1
                - 2
    messages:
      audioData:
        $ref: '#/components/messages/audioData'
      endOfStream:
        $ref: '#/components/messages/endOfStream'
      frameResult:
        $ref: '#/components/messages/frameResult'
      done:
        $ref: '#/components/messages/done'
      error:
        $ref: '#/components/messages/error'
operations:
  sendAudio:
    action: send
    channel:
      $ref: '#/channels/streaming-detection'
    summary: Stream audio data to the server.
    description: |
      Send binary frames containing audio data in the format declared by
      the `audio_format` query parameter. Frames can be any size; the
      server buffers and windows internally.
    messages:
      - $ref: '#/channels/streaming-detection/messages/audioData'
  signalEnd:
    action: send
    channel:
      $ref: '#/channels/streaming-detection'
    summary: Signal end of audio stream.
    description: |
      Send an empty text frame (`""`) to indicate all audio has been sent.
      The server will process any remaining buffered audio, deliver final
      results, and send a `done` message.
    messages:
      - $ref: '#/channels/streaming-detection/messages/endOfStream'
  receiveFrameResult:
    action: receive
    channel:
      $ref: '#/channels/streaming-detection'
    summary: Receive a per-frame detection result.
    description: |
      Emitted each time a sliding window of audio has been analysed.
      Results arrive in chronological order but are streamed incrementally
      as inference completes.
    messages:
      - $ref: '#/channels/streaming-detection/messages/frameResult'
  receiveDone:
    action: receive
    channel:
      $ref: '#/channels/streaming-detection'
    summary: Receive the final summary when analysis is complete.
    description: |
      Sent after all frames have been delivered, in response to the client's
      end-of-stream signal. The server closes the connection after this message.
    messages:
      - $ref: '#/channels/streaming-detection/messages/done'
  receiveError:
    action: receive
    channel:
      $ref: '#/channels/streaming-detection'
    summary: Receive an error notification.
    description: |
      Sent if the audio format is invalid, inference fails, or the server
      encounters an internal error. The server closes the connection after
      this message.
    messages:
      - $ref: '#/channels/streaming-detection/messages/error'
components:
  messages:
    audioData:
      name: audioData
      title: Audio Data
      summary: A binary frame containing audio samples.
      contentType: application/octet-stream
      payload:
        type: string
        format: binary
        description: |
          Audio data in the format declared by the `audio_format` query
          parameter. For raw formats this is headerless samples; for
          container formats this is the encoded byte stream. Frames may
          be any size; the server buffers internally.
    endOfStream:
      name: endOfStream
      title: End of Stream
      summary: >-
        An empty text frame signalling that the client has finished sending
        audio.
      contentType: text/plain
      payload:
        type: string
        const: ''
    frameResult:
      name: frameResult
      title: Frame Result
      summary: A single frame detection result.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/FrameResultMessage'
    done:
      name: done
      title: Done
      summary: Analysis complete - all frames have been delivered.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/DoneMessage'
    error:
      name: error
      title: Error
      summary: An error occurred during connection or analysis.
      contentType: application/json
      payload:
        $ref: '#/components/schemas/ErrorResponse'
  schemas:
    FrameResultMessage:
      type: object
      required:
        - type
        - frame
      properties:
        type:
          type: string
          const: frame
        frame:
          $ref: '#/components/schemas/Frame'
    Frame:
      type: object
      required:
        - start_time_ms
        - end_time_ms
        - verdict
        - confidence
      properties:
        start_time_ms:
          type: integer
          minimum: 0
          description: Frame start time in the original audio stream (ms).
          examples:
            - 0
        end_time_ms:
          type: integer
          minimum: 0
          description: Frame end time in the original audio stream (ms).
          examples:
            - 4000
        verdict:
          type: string
          enum:
            - synthetic
            - non-synthetic
            - no-content
          description: |
            - `synthetic` - the frame likely contains AI-generated speech.
            - `non-synthetic` - the frame likely contains natural human speech.
            - `no-content` - the frame is silent or contains no usable audio.
          examples:
            - synthetic
        confidence:
          type: number
          format: double
          minimum: 0
          maximum: 1
          description: Confidence in the stated verdict (0-1).
          examples:
            - 0.9732
    DoneMessage:
      type: object
      required:
        - type
        - duration_ms
        - frame_count
      properties:
        type:
          type: string
          const: done
        duration_ms:
          type: integer
          minimum: 0
          description: Total duration of the streamed audio in milliseconds.
          examples:
            - 12500
        frame_count:
          type: integer
          minimum: 0
          description: Total number of frames analysed.
          examples:
            - 10
    ErrorResponse:
      type: object
      required:
        - type
        - error
      properties:
        type:
          type: string
          const: error
        error:
          type: string
          description: Human-readable error description.
          examples:
            - Internal server error
            - This request is not permitted.
            - Insufficient credits.
            - Unable to validate request. Please try again.
            - Audio data does not match the declared audio_format.
            - >-
              The audio could not be processed. It may be corrupted or in an
              unsupported format.
            - >-
              Invalid audio_format='mp2'. Valid values: ['3gp', '3gpp', 'aac',
              'aiff', 'alaw', 'amr', 'au', 'f32be', 'f32le', 'f64be', 'f64le',
              'flac', 'm4a', 'mp3', 'mp4', 'mulaw', 'ogg', 'opus', 's16be',
              's16le', 's24be', 's24le', 's32be', 's32le', 's8', 'u16be',
              'u16le', 'u24be', 'u24le', 'u32be', 'u32le', 'u8', 'wav', 'webm',
              'wma']
            - >-
              Raw audio formats require sample_rate and num_channels query
              parameters
            - sample_rate and num_channels require audio_format to be specified
            - >-
              Invalid sample_rate=50000. Valid values: [8000, 11025, 16000,
              22050, 32000, 44100, 48000, 96000]
            - Invalid num_channels=12. Must be between 1 and 8
            - >-
              audio_format query parameter is required. Example:
              audio_format=s16le&sample_rate=16000&num_channels=1