Noesis
Recorded calls
Upload a recorded call as a WAV file and get back the answers a live call would have received.
Upload a recorded call as a WAV file and get back the answers a live call would have received. Use it to test Noesis on your own calls before you connect live ones.
POST https://api.noesis.detectif.ai/v1/detect/fileThe recording goes through the same analysis as a live call, in real time, so the labels and their timing are what a live call would have given. The request therefore takes about as long as the recording.
Send a file
Send the file as a multipart field named file, with an Idempotency-Key so a retry does not analyse it twice:
curl https://api.noesis.detectif.ai/v1/detect/file \
-H "Authorization: Bearer $DETECTIF_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F "file=@call.wav" \
-F "call_id=test-1"# analyse_call.py: send a recorded call to Noesis and print the answers
import os, sys, uuid
import requests
with open(sys.argv[1], "rb") as f:
r = requests.post(
"https://api.noesis.detectif.ai/v1/detect/file",
headers={
"Authorization": f"Bearer {os.environ['DETECTIF_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
data={"call_id": "test-1"},
files={"file": f},
timeout=60, # between bytes: a newline arrives every 5 s
)
result = r.json()
if r.status_code != 200 or result.get("type") == "error":
sys.exit(result) # not a WAV file, balance too low, ...
for e in result["timeline"]:
print(f'{e["t"]:6.1f}s {e["label"]} ({e["category"]})')
print("final:", result["final"]["label"] if result["final"] else "no speech heard")// analyse-call.mjs: send a recorded call to Noesis and print the answers (Node.js 18+)
import fs from "node:fs";
import { randomUUID } from "node:crypto";
const form = new FormData();
form.append("file", new Blob([fs.readFileSync(process.argv[2])]), "call.wav");
form.append("call_id", "test-1");
const r = await fetch("https://api.noesis.detectif.ai/v1/detect/file", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DETECTIF_API_KEY}`, "Idempotency-Key": randomUUID() },
body: form,
});
const result = await r.json();
if (!r.ok || result.type === "error") { console.error(result); process.exit(1); }
for (const e of result.timeline) console.log(`${e.t.toFixed(1).padStart(6)}s ${e.label} (${e.category})`);
console.log("final:", result.final ? result.final.label : "no speech heard");The Python and Node.js samples print:
3.2s ivr (machine)
13.0s hold (machine)
23.4s human (human)
final: humanFields
| Field | Values | Default |
|---|---|---|
file |
The recording (required) | |
call_id |
Your reference, up to 200 characters, returned in the result | none |
remote_channel |
For a stereo file, the channel of the person you called: 0 or 1 |
0 |
language |
Optional hint, en or hi. Leave it out for automatic (English, Hindi, Hinglish) |
automatic |
transcripts |
false to leave the transcript out of the result |
true |
record |
false to not keep the recording and its transcript. See Data and recording |
true |
Accepted files
| File | Accepted |
|---|---|
| Format | WAV with 8, 16, 24 or 32-bit PCM, 32 or 64-bit float, or G.711 μ-law or A-law samples |
| Sample rate | Any. Rates other than 8, 16, 22.05, 24, 44.1 and 48 kHz are converted to 16 kHz |
| Channels | Mono (the person you called), or stereo with your agent on the other channel |
| Length | 1 second to 15 minutes, up to 50 MB |
Convert other recordings, such as MP3, M4A or Opus, first: ffmpeg -i call.mp3 -ac 1 -ar 16000 -sample_fmt s16 call.wav.
Response
{
"type": "result",
"session_id": "9f2c4e7a-1b3d-4c5e-8f60-2a7b9c1d3e4f",
"call_id": "test-1",
"file": {"name": "call.wav", "duration_s": 26.6, "format": "pcm_s16le", "sample_rate": 8000, "channels": 1},
"first": {"label": "ivr", "category": "machine", "confidence": 0.9, "t": 3.2},
"final": {"label": "human", "category": "human", "confidence": 0.97},
"timeline": [
{"seq": 1, "t": 3.2, "label": "ivr", "category": "machine", "confidence": 0.9, "previous_label": "unknown", "change": "initial"},
{"seq": 2, "t": 13.0, "label": "hold", "category": "machine", "confidence": 0.8, "previous_label": "ivr", "change": "transition"},
{"seq": 3, "t": 23.4, "label": "human", "category": "human", "confidence": 0.95, "previous_label": "hold", "change": "transition"}
],
"transcript": [{"t": 3.0, "text": "For credit cards, press one."}]
}| Field | Meaning |
|---|---|
first |
The first answer, and when it came: t is seconds into the recording |
final |
The answer at the end of the recording. first and final are null when no speech was heard |
timeline |
Every answer in order, with the fields of a live call's classification events |
transcript |
What the callee said, with times. Left out when transcripts=false |
file |
What was analysed |
session_id |
Look the call up again later; see Sessions and usage |
The full schema is in the API Reference.
While it runs
A 30-second recording takes about 30 seconds. The response starts straight away and a newline is sent every 5 seconds until the result, so the connection stays open however long the recording is; JSON parsers skip the newlines. If your HTTP client limits the total time of a request, set it above the recording's length plus a minute.
If the analysis fails after it has started, the response (status 200) ends with {"type": "error", ...} instead of the result.
Credits
A recording uses credits for its length, like a live call of the same length. A file that is refused uses none, and nothing is billed if the analysis fails. Send the same Idempotency-Key when you retry: the retry returns the stored result instead of running again.
Errors
| Status | Meaning |
|---|---|
400 |
No file was sent, it is not a WAV file, it is shorter than 1 second or has more than 2 channels, or remote_channel=1 was set for a mono file |
401 |
The API key is missing, invalid or revoked |
402 |
Your credit balance does not cover the recording |
403 |
The key is not allowed to use Noesis, or your email address is not verified |
413 |
The file is larger than 50 MB or longer than 15 minutes |
429 |
Too many requests or recordings at once |
503 |
Busy; try again after the Retry-After seconds |
See Errors for the error body and codes.