Home Playground Docs hub

Guide G-62 · Translation & Voice

Verifying audio provenance

Verified against local · 2026-09-27 · speech-service 3.0.1 (workspace HEAD)
Journey. As a developer, I need to know whether an audio clip —
submitted by a user, scraped from social media, or received through a
moderation queue — was generated by my deployment, and ideally *which
request* produced it, without exposing anyone's biometric data.

Every clip synthesized by the Speech service (:40161, prefix /v3) carries an invisible dual watermark, embedded automatically before encoding — there is nothing to enable per-request:

  1. A robust detection gate (AudioSeal) answers *"was this audio

produced here?"*. It survives MP3/AAC distribution, resampling, loudness normalization, and quiet recordings down to −20 dB, because detection peak-normalizes the audio before scoring.

  1. A 128-bit payload (WavMark) answers "who and which request": a

privacy-safe 4-byte HMAC hint of the voice print — the raw voice print UUID is never exposed — plus the low 64 bits of the request ID, CRC-verified.

One multipart POST to /v3/audio/provenance returns both layers.

Prerequisites

and the four ONNX model paths resolvable (defaults live under services-reusable/speech-service/assets/models/; regenerate them with speech-service/wm-exporter). When the engine is off, the check returns 503 — an explicit, honest answer rather than a fake negative.

mono before detection. ffmpeg on PATH is required by the service.

user JWT (Step 1). In sandbox/lower environments any or no JWT passes.

Concepts

(boolean gate verdict), voice_print_hint and request_id_lo (payload layer — both null when the 128-bit payload didn't survive the journey, even if the gate fired), and confidence in [0, 1], computed as 0.4 * gate_score + 0.6 * payload_bit_accuracy (so gate-only hits land in [0.4, 1); clean audio scores near 0).

processing (MP3 ≥ 96 kbps, telephone band-pass, −20 dB quiet recordings, EBU R128 normalization); the payload is pickier — expect it intact on lossless and light MP3, dropped on aggressive codecs. A produced_by_service: true with null payload fields is a verified but untraceable clip.

clipping that squashes the waveform body, and cropping away most of the clip — severe nonlinear damage is not invertible, and the service says false rather than guess.

agents (/agent/v1/mcp/speech/* SSE endpoint).

lets your back office correlate clips with voice prints, but cannot be reversed into the voice print or the user.

Step 1 — Get a user JWT

bash
USER_TOKEN=$(curl -sf -X POST "${AUTH_URL}/v3/auth/qa/login" \
  -H "Content-Type: application/json" \
  -H "x-api-key: ${INAYA_API_KEY}" \
  -H "x-client-app-key: ${INAYA_APP_KEY}" \
  -d "{\"device_id\": \"g62-demo\", \"locale\": \"en-US\", \"tenant_id\": \"${TENANT_ID}\", \"username\": \"g62-checker\"}" \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')
test -n "${USER_TOKEN}" && echo "JWT acquired (${#USER_TOKEN} chars)"

Expected — a 24 h PS512 JWT (qa/login provisions the user on first use; device_id must be ≥ 8 chars).

Step 2 — Produce a watermarked clip

Any synthesis is automatically watermarked. If you don't have a voice print yet, register one first — the full enrollment walk-through is Cloning a voice and speaking; the short version:

bash
export WORKDIR=$(mktemp -d /tmp/g62-prov.XXXXXX)
say -o "${WORKDIR}/voice.wav" --data-format=LEI16@24000 \
  "Hello, this is my voice reference for the provenance check guide."
VP_PATH=$(curl -s -X POST "${SPEECH_URL}/v3/voice-prints" \
  -H "x-api-key: ${INAYA_API_KEY}" \
  -H "x-client-app-key: ${INAYA_APP_KEY}" \
  -H "Authorization: Bearer ${USER_TOKEN}" \
  -F "file=@${WORKDIR}/voice.wav" \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["voice_print_wav_path"])')
echo "voice print: ${VP_PATH}"

curl -s -o "${WORKDIR}/spoken.mp3" -w 'HTTP %{http_code} | %{size_download} bytes\n' \
  -X POST "${SPEECH_URL}/v3/text-to-speech" \
  -H "Content-Type: application/json" \
  -H "x-api-key: ${INAYA_API_KEY}" \
  -H "x-client-app-key: ${INAYA_APP_KEY}" \
  -H "Authorization: Bearer ${USER_TOKEN}" \
  -d "{\"input\": \"This sentence carries an invisible watermark.\", \"reference_voiceprint_path\": \"${VP_PATH}\", \"locale\": \"en\", \"output_format\": \"wav\"}"
file "${WORKDIR}/spoken.mp3" | grep -qi audio && echo "synthesized clip ready (watermarked at synthesis time)"

Expected — 200 with a WAV body. Synthesis is the slow call (~20–45 s warm; a first-call-after-boot can hit the internal deadline and return 504 tts_synthesis_failed — retry). The watermark is embedded before encoding — re-encoding the result to MP3 yourself does not remove it.

Re-speak output verifies identically. POST /v3/respeak
(multipart payload JSON + file audio) re-renders a recording in the
registered voice — transcribe → optional enhancement → synthesize — and
watermarks the result on the same path. Verified live: a re-spoken clip
returns produced_by_service: true with confidence: 1.0, the same
voice-print hint as TTS output for that voice print, and a fresh
request_id_lo per request.

Step 3 — Check the synthesized clip (positive verdict)

bash
curl -s -X POST "${SPEECH_URL}/v3/audio/provenance" \
  -H "x-api-key: ${INAYA_API_KEY}" \
  -H "x-client-app-key: ${INAYA_APP_KEY}" \
  -H "Authorization: Bearer ${USER_TOKEN}" \
  -F "audio=@${WORKDIR}/spoken.mp3" \
  -o "${WORKDIR}/verdict.json"
python3 - "${WORKDIR}/verdict.json" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
assert d["produced_by_service"] is True, d
assert d["voice_print_hint"], "payload fields should be present on a fresh WAV"
assert d["request_id_lo"] is not None, d
print(f'produced_by_service={d["produced_by_service"]} confidence={d["confidence"]:.4f} hint={d["voice_print_hint"]} request_id_lo={d["request_id_lo"]}')
PY

Expected response — 200: {"produced_by_service":true,"request_id_lo":<u64>,"voice_print_hint":"<8 hex chars>","confidence":~0.99}. Detection takes a few seconds (decode + two ONNX model families). The multipart field must be audio (or file); a missing field → 400.

Step 4 — Check audio the platform did not produce (negative verdict)

bash
say -o "${WORKDIR}/not-ours.wav" --data-format=LEI16@24000 \
  "This recording was never touched by the speech service."
curl -s -X POST "${SPEECH_URL}/v3/audio/provenance" \
  -H "x-api-key: ${INAYA_API_KEY}" \
  -H "x-client-app-key: ${INAYA_APP_KEY}" \
  -H "Authorization: Bearer ${USER_TOKEN}" \
  -F "audio=@${WORKDIR}/not-ours.wav" \
  -o "${WORKDIR}/verdict-negative.json"
python3 - "${WORKDIR}/verdict-negative.json" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
assert d["produced_by_service"] is False, d
assert d["voice_print_hint"] is None and d["request_id_lo"] is None, d
print(f'produced_by_service={d["produced_by_service"]} confidence={d["confidence"]:.4f}')
PY

Expected response — 200 with produced_by_service:false, payload fields null, and confidence near 0 (typically < 0.05). Note the check is a positive proof only: false means "no platform watermark detected", not "this audio is authentic human speech".

Step 5 — What survives a realistic distribution chain

Re-encode and re-check the same clip — the gate holds even where the payload starts dropping:

bash
ffmpeg -y -loglevel error -i "${WORKDIR}/spoken.mp3" -codec:a libmp3lame -b:a 128k "${WORKDIR}/spoken-128k.mp3"
ffmpeg -y -loglevel error -i "${WORKDIR}/spoken.mp3" -af volume=-14dB "${WORKDIR}/spoken-quiet.wav"
for f in spoken-128k.mp3 spoken-quiet.wav; do
  echo -n "${f}: "
  curl -s -X POST "${SPEECH_URL}/v3/audio/provenance" \
    -H "x-api-key: ${INAYA_API_KEY}" \
    -H "x-client-app-key: ${INAYA_APP_KEY}" \
    -H "Authorization: Bearer ${USER_TOKEN}" \
    -F "audio=@${WORKDIR}/${f}" \
    -o "${WORKDIR}/verdict-${f}.json"
  python3 - "${WORKDIR}/verdict-${f}.json" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
print(f'produced_by_service={d["produced_by_service"]} confidence={d["confidence"]:.4f} hint={d["voice_print_hint"]}')
PY
done

Expected — both stay produced_by_service:true: the 128 kbps MP3 typically keeps the full payload (hint present), while the −14 dB file detects via the normalized gate — the quiet-recording scenario that peak-normalization in the detect path exists for.

Where to go next

enrollment and synthesis journey this guide checks.

denoise chains for your media pipeline.

and the check_audio_provenance tool in the service's MCP surface.