https://api.echonova.aiDevelopers
Custom terminology
Send your own names, products and acronyms with a transcription and get the transcript back spelled your way, with every correction listed, near misses to review, and the same terms on a live stream.
Speech to Text writes what it hears. A bank name, a product or an acronym
often comes out spelled the way it sounds: امريتس ان بي دي instead of
Emirates NBD, or kay why see instead of KYC. Send your terms in the
terminology field and the result carries a corrected copy of the
transcript, with every change listed.
The words as heard are never lost. transcription stays exactly as it was
transcribed; the corrected text is in terminology.corrected.
When to use it
- Your callers say brand, product or place names that a general model spells its own way.
- You need acronyms written as letters:
KYC,IBAN,SWIFT. - The same name is said in Arabic and English, and you want one spelling.
Terminology does not change what the model hears. It corrects the text after transcription, so a word the model missed entirely cannot be added.
Quick example
Send terminology as a JSON string beside file and language. It works
on POST /v1/speech-to-text/sync and POST /v1/speech-to-text.
curl -X POST "https://api.echonova.ai/v1/speech-to-text/sync" \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@call.mp3;type=audio/mpeg" \
-F "language=ar" \
-F 'terminology={"keywords": ["KYC", {"term": "Emirates NBD", "variants": ["امريتس ان بي دي"]}]}'import json
import requests
terminology = {
"keywords": [
"KYC",
{"term": "Emirates NBD", "variants": ["امريتس ان بي دي"]},
{"term": "SWIFT", "context_words": ["transfer", "تحويل"]},
]
}
with open("call.mp3", "rb") as audio:
response = requests.post(
"https://api.echonova.ai/v1/speech-to-text/sync",
headers={"Authorization": "Bearer YOUR_API_KEY"},
files={"file": ("call.mp3", audio, "audio/mpeg")},
data={"language": "ar", "terminology": json.dumps(terminology)},
)
data = response.json()["data"]
print(data["transcription"]) # as heard
print(data["terminology"]["corrected"]) # spelled your wayThe response adds a terminology object:
{
"transcription": "حولت المبلغ عبر السويفت إلى امريتس ان بي دي أمس.",
"terminology": {
"raw": "حولت المبلغ عبر السويفت إلى امريتس ان بي دي أمس.",
"corrected": "حولت المبلغ عبر الـSWIFT إلى Emirates NBD أمس.",
"edits": [
{ "start": 16, "end": 23, "original": "السويفت", "replacement": "الـSWIFT", "stage": "phonetic", "score": 1 },
{ "start": 28, "end": 43, "original": "امريتس ان بي دي", "replacement": "Emirates NBD", "stage": "exact", "score": 1 }
],
"nearMisses": [],
"warnings": [],
"mapperStatus": "applied",
"mapperVersion": "3.0.0"
}
}With the asynchronous endpoint, the same object is on the job from
GET /v1/speech-to-text/jobs/{jobId} once it completes, and in the
job.completed webhook.
Keyword fields
terminology is {"keywords": [...]}. Each keyword is a plain string, or
an object when you want more control.
| Field | Type | What it does |
|---|---|---|
term |
string, required | The spelling you want in the output. A plain string keyword is the same as {"term": "..."}. |
variants |
list of strings, up to 10 | Ways the term is misheard that you have seen. Each one is matched exactly. |
blocked |
list of strings, up to 20 | Phrases that must never become this term, for example swift delivery for SWIFT. |
context_words |
list of strings, up to 20 | Words that, when one appears within eight words, let a less certain match apply. |
needs_context |
boolean | true applies the term only next to a context word or another keyword. Leave it out and it is decided for you when the term is an everyday word. |
output_script |
latin, arabic, as_typed |
The script to write the term in. The term is always written as you typed it. |
Terms, variants and other phrases are up to 100 characters each. Fields not in this table are ignored.
How matching works
Matching ignores case, Arabic hamza forms (أ إ آ), ى and ة, and
diacritics. A match is always whole words and never crosses a comma or the
end of a sentence.
stage |
What matched |
|---|---|
exact |
The term itself, one of your variants, or an acronym spelled out (kay why see, كي واي سي → KYC). |
phonetic |
Words that sound the same as the term, in either script. |
fuzzy |
Words spelled close to the term. |
score runs from 0 to 1. Exact and phonetic matches apply. A fuzzy match
applies when it is close enough, or when it is a little less close but one
of your context_words, or another keyword, is nearby. Short acronyms such
as IT or API match exactly only, so they never replace an everyday word.
Arabic prefixes stay on the word: السويفت becomes الـSWIFT, and
بالكي واي سي becomes بالـKYC. Digits, e-mail addresses and URLs are
never changed.
Read the result
| Field | What it holds |
|---|---|
raw |
The transcript as heard: the same text as transcription. |
corrected |
raw with your terms applied. Outside the edits it is identical to raw. |
edits |
Every replacement, in order. start and end are character offsets in raw, end-exclusive. |
nearMisses |
Close matches that were not applied. See below. |
warnings |
Anything that limited the mapping, each with a code and a detail. |
mapperStatus |
applied, no_keywords, or skipped when the mapping could not run. corrected then equals raw. |
mapperVersion |
The version of the matcher that answered. |
Use the offsets to highlight corrections in your own interface:
raw.slice(edit.start, edit.end) is always edit.original.
Without the terminology field the response has no terminology key at
all, and the endpoint behaves exactly as before.
Downloads stay as heard
GET /v1/speech-to-text/jobs/{jobId}/export builds TXT, SRT and JSON from
transcription and its timed segments, so downloads keep the words as
heard. Take terminology.corrected from the job for the corrected text.
Near misses
A near miss is a match the service was not sure enough to apply, for
example اونلاين بانكنق for Online Banking at a score of 0.81. It is
listed in nearMisses with the words heard, the candidate term and a
score, and the transcript is left unchanged there.
Nothing is remembered between requests. To have a near miss fixed next
time, show it to a person, and when they confirm it, add the heard words to
that term's variants in the list you send.
Live streams
On wss://api.echonova.ai/v1/speech-to-text/stream, send a terminology
message at any time. It applies to every segment after it:
{
"type": "terminology",
"terminology": { "keywords": ["KYC", { "term": "Emirates NBD", "variants": ["امريتس ان بي دي"] }] }
}The server answers {"type": "terminology.set", "active": true}, and each
transcript message then carries a terminology object for its segment,
with offsets into that segment's text. Send it again to replace the list,
or with "terminology": null to clear it.
A list that is not valid gets an error message with code
validation_failed and is ignored; the session continues with the terms it
had. The message must fit in one 64 KiB frame, which holds a few hundred
keywords.
Limits
| Limit | Value |
|---|---|
| Keywords | 500 per request |
| Field size | 256 KB on the file endpoints; one 64 KiB frame on a stream |
| Term, variant, phrase | 100 characters |
| Variants | 10 per keyword |
| Blocked and context words | 20 each per keyword |
Terminology is free: the request costs the same with or without it.
Errors
A terminology field that cannot be read answers 400 before anything is
transcribed or charged.
| Status | errorMessage |
Fix |
|---|---|---|
400 |
terminology must be a JSON object with a keywords list |
Send a JSON string such as {"keywords": ["KYC"]}. |
400 |
each terminology keyword needs a term of 1 to 100 characters |
Give every keyword a term of 1 to 100 characters. |
400 |
terminology variants, blocked and context_words must be lists of text of up to 100 characters; at most 10 variants and 20 of the others |
Send lists of short phrases within the limits. |
400 |
terminology needs_context must be true or false, and output_script latin, arabic or as_typed |
Fix the value, or leave the field out. |
400 |
terminology allows at most 500 keywords |
Send the terms this audio is likely to contain. |
400 |
terminology must be at most 256 KB |
Send a shorter list. |
If the matching itself fails or takes too long, the transcription still
completes: mapperStatus is skipped, corrected equals raw, and
warnings says why.
FAQ
Does terminology make transcription slower?
Barely. A list of a few thousand keywords on a five-minute call adds well under a second.
Can I store a list and refer to it by name?
No. Send the full list with every request. Keep it in your own system and add confirmed near misses to it over time.
Why did a term not apply?
Check nearMisses first: the match may have been too uncertain. Then add
the heard words as a variant, or add context_words so a close match can
apply when they are nearby.
Is the Speech to Text playground the same feature?
Yes. The playground's Terminology option sends the same field and shows the corrected transcript with each change highlighted.