ScamAI 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
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. Links are fetched for video only; see Detect from a link.

Parameters

Request parameters
FieldTypeMeaning
filebinaryThe media file. MP3, WAV, FLAC, M4A, AAC or OGG, at most 50 MB.
save"true" | "false"Keep a history row and the file. Default "true". With "false" nothing is stored and the run cannot be retrieved later.

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, audio or document), 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 model that answered, "Eva V1.6".
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.