Prep Eval API
Every speech- and language-scoring engine behind one host and one credential:
https://eval.flowb.ai. Each engine is a lane with its own
path prefix and its own native request format — the gateway adds shared
authentication and, for the team-hosted graders, holds their upstream credentials so
you never handle them.
| Lane | Engine | What it scores |
|---|---|---|
/pronvi | in-process | Vietnamese pronunciation: segmental CTC-GOP + speaker-relative F0 tone, per syllable |
/shadowscore | in-process | How closely a take MATCHES a model recording: intonation, rhythm, per-word forced-alignment GOP (en) |
/english | proxy → Prep VR Marking | IELTS speaking & writing band grading (async: enqueue, then poll) |
/chinese | proxy → CPA | Chinese scripted-speech analysis: per-character tone + segmental pronunciation |
Authentication
Send your eval key on every request:
X-API-Key: <your-eval-key>
Authorization: Bearer <key> is accepted in place of
X-API-Key. Failure is
401 {"detail":"missing or invalid credentials"}.
Only /health, the lane healths and this page are open.
Optionally send X-Sender-Id: <your-product-id> on the
/english lane — it is forwarded upstream as your identity with the
marking team. Without it the gateway's default identity is used.
Health
GET /health
→ {"ok": true, "version": "1.0.0", "lanes": ["pronvi","shadowscore","english","chinese"]}
Lane healths: GET /pronvi/health, GET /shadowscore/health
(both open, no key).
/pronvi — Vietnamese pronunciation
Segmental correctness (wav2vec2 CTC-GOP) + tone correctness (speaker-relative F0 against per-dialect calibrated profiles), combined into a 0–9 band. Audio is WAV (any sample rate; resampled to 16 kHz mono).
POST /pronvi/score — one syllable
Multipart form:
| Field | Type | Meaning |
|---|---|---|
audio | file (WAV) | the learner's take |
target | text | the target syllable, e.g. má |
speaker_median | number, optional | the learner's running median F0 (Hz); omit → within-syllable tone only |
dialect | text, default north | north or south (south merges hỏi≡ngã) |
→ {"target":"má","target_tone":"sac","dialect":"north","band":8.5,"verdict":"pass","percent":94,
"segmental":{"ok":true,"gop":…,"ratio":…,"transcript":"ma","base_match":true},
"tone":{"target":"sac","pred":"sac","conf":0.91,"ok":true},
"speaker_median":112.4, "tone_flags":["má"]?}
Verdicts: pass / almost / retry.
tone_flags appears only when the tone missed.
POST /pronvi/score_utterance — a whole turn
Same form fields as /score with target = the full text.
Returns utterance-level segmental plus per-syllable tone rows
(syllables) and tone_flags.
POST /pronvi/baseline — native-tongue F0 gauge
multipart: audio=<wav>
→ {"median":110.2,"floor":86.1,"ceiling":141.0} (422 if no voiced pitch)
POST /pronvi/detect — dialect detection
Form fields audio, target, optional
speaker_median. Ranks calibrated dialects by how well the take's tone
fits; accumulate across several productions before trusting it.
/shadowscore — shadowing match
Scores how closely a learner's take matches a fixed model recording (scripted repeat): utterance intonation + rhythm always (Praat F0, language-agnostic), plus per-word pronunciation / rhythm / intonation via CTC forced alignment (English). Audio is passed as fetchable public URLs.
POST /shadowscore/shadowing/{lang}
{
"model_url": "https://…/model.wav", // "" → GOP-only mode (drill pronunciation read)
"learner_url": "https://…/take.wav",
"script": "the text that was read", // enables per-word scoring (en)
"linking": [{…}], // authored linking spots — scored pass, never penalised
"rate": 1.0, // tier playback rate while recording (bleed cancellation)
"ref_clips": [{"url":"…","start_ms":0}] // clips that PLAYED during the take (bleed cancellation)
}
→ {
"intonation": 82, "rhythm": 76, // 0–100, vs the model
"pronunciation": 71, // omitted when the aligner can't run
"words": [{"word":"loaf","pron":68,"rhythm":80,"intonation":77,
"linked":false,"start":0.42,"end":0.71}, …],
"bleed_db": 9.6, // present when bleed cancellation ran
"cleaned_b64": "<16-bit WAV>" // the voice-only take — store it over the raw take
}
Audio URLs must be publicly fetchable https — the gateway refuses private/internal destinations.
/english — IELTS speaking & writing grading
A transparent proxy to the Prep VR Marking service: same paths, same JSON, under
the /english prefix. Grading is asynchronous — enqueue a session, then
poll its result. Speaking answers are fetchable WAV URLs; writing answers are text.
| Endpoint | Purpose |
|---|---|
POST /english/api/v1/ielts/speaking/grade-test | enqueue a speaking grading session |
GET /english/api/v1/ielts/speaking/result/{session_id} | poll a speaking result |
POST /english/api/v1/ielts/speaking/grade-question | grade a single speaking answer |
POST /english/api/v1/ielts/writing/grade-test | enqueue a writing grading session |
GET /english/api/v1/ielts/writing/result/{session_id} | poll a writing result |
POST /english/api/v1/ielts/writing/grade-question | grade a single writing answer |
POST /english/api/v1/ielts/speaking/grade-test
{"meta":{"submission_skill_id":"…"},
"responses":[{"question":{"id":1,"title":"…","part":1},"answer":"https://…/answer.wav"}]}
→ {"data":{"session_id":"…"}}
GET /english/api/v1/ielts/speaking/result/{session_id}
→ {"session_id":"…","status":3,"score":6.5,
"result":{"score":6.5,"pronunciation":{"score":6.0},"fluency_and_coherence":{"score":7.0},
"lexical_resource":{"score":6.5},"grammatical_range_and_accuracy":{"score":6.0}}}
Status: 1 pending · 2 in progress · 3 completed · 4 failed. Poll every ~2 s until terminal.
/chinese — Chinese pronunciation (CPA)
A transparent proxy to the team's Chinese Pronunciation Assessment. The upstream credential is injected by the gateway — send only your eval key. Synchronous multipart; audio is a fetchable URL.
POST /chinese/api/v1/scripted_speech_analysis
| Form field | Meaning |
|---|---|
audio_url | fetchable URL of the learner's clip |
script | the reference script the clip should match |
sender_id | your product identity (e.g. tp-virtual-room) |
user_id | your learner id, for the team's tracking |
accent_code | optional, e.g. zh-vi |
verify_script | "true" |
language | "chinese" |
request_high_resource | "false" |
→ {"has_speech":true,
"scores":{"pronunciation":87.2,"tone":78.5,"overall":83.9},
"utterances":[{"words":[{"text":"你","pinyin":"nǐ","syllables":[…]}]}]}
Errors
| Status | Meaning |
|---|---|
401 | missing or invalid eval key |
422 | malformed request (missing field, unusable audio) |
4xx/5xx on /english, /chinese | the upstream grader's own status, passed through verbatim |
503 | gateway key unconfigured (deploy-time misconfiguration) |