Scam AI raised $2.6M to combat AI-powered scams→
scam.ai

~/docs cat detect-audio.md

Detect audio

Checks a recording for synthetic or cloned voice, and answers with one verdict for the recording plus a timeline with a value for every window of speech.

Send the file

curl -X POST \
  "https://api.scam.ai/v1/detections" \
  -H "x-api-key: <YOUR_API_KEY>" \
  -F "file=@/path/to/voicemail.mp3"

Recordings are sent as a file. A link to a recording is fetched too; see Detect from a link.

Parameters

Request parameters
FieldTypeMeaning
filebinaryThe media file. MP3, WAV, FLAC, M4A, AAC or OGG, at most 50 MB.

The response

idstring
The detection id. Pass it to GET /v1/detections/{id} to read the run back.
statusstring
Always "completed". The answer arrives in the same request.
created_atstring
When the run finished. RFC 3339, millisecond precision, always Z.
mediaobject
What was checked: type (image, video or audio), filename, mime_type and bytes. The last three can be null when they were not known. mime_type is what the upload declared, so a client that sends no content type sees application/octet-stream here; type comes from the bytes themselves.
modelstring
The Eva version that answered this run, "Eva V1.5" or "Eva V1.6". Which version answers is set per account.
verdictstring
LIKELY_REAL, ALERT or LIKELY_AI. Branch on this.
scorenumber | null
0 to 1, the probability the media is AI-made or manipulated. null when nothing could be scored.
summarystring
One sentence a person can read, matching the verdict.
credits_usednumber
What this run cost, an integer. 0 on a sandbox key, or when the same file was sent byte for byte from this account within 30 days.
zero_charge_reasonstring
Only when credits_used is 0: "sandbox" or "duplicate".
duration_msnumber
The end of the last window in milliseconds, scored or not. Audio is billed by length.
segmentsarray
One entry per window: start_ms, end_ms and value (0 to 1, the probability that window is synthetic, or null when the window was not scored).
segments[].statusstring
scored when the window has a value, abstained when it does not.
segments[].reason_codesstring[]
Only on an abstained window: why it was not scored, for example no_voice_content.
response · 200
{  "id": "c3a9e1d0-7f24-4b6a-8e15-9d0c2b7a4f61",  "status": "completed",  "created_at": "2026-09-15T10:21:40.088Z",  "media": {    "type": "audio",    "filename": "voicemail.mp3",    "mime_type": "audio/mpeg",    "bytes": 131072  },  "model": "Eva V1.6",  "verdict": "LIKELY_AI",  "score": 0.96,  "summary": "This recording shows strong signs of synthetic or cloned voice.",  "credits_used": 1,  "duration_ms": 8000,  "segments": [    { "start_ms": 0, "end_ms": 4000, "value": 0.9612, "status": "scored" },    {      "start_ms": 4000,      "end_ms": 8000,      "value": null,      "status": "abstained",      "reason_codes": ["no_voice_content"]    }  ]}

Questions

What is a window?
The recording is scored in fixed-length windows. Each entry in segments[] is one window, with its start and end in milliseconds and its own value. The score at the top of the response is the verdict for the whole recording.
Why is a window's value null?
Nothing was scored there: status is abstained and reason_codes says why, for example no_voice_content when the window holds no speech. A null is never a 0.
Why does audio never answer ALERT?
A recording is judged on one line, so it answers LIKELY_REAL or LIKELY_AI. Your code should still handle all three words, because the same integration will see ALERT on images and videos.

Start building

Create an API key in the dashboard and send your first file. To go through an integration or an on-prem deployment with our team, request a demo.