What it is — and what it is not
- One IPA hub, two forms. dots: 8 is the canonical WIA Braille (one IPA symbol = one cell, table 8.0.0, for refreshable displays and screens). dots: 6 (default, for compatibility) is the v2 paper / embosser form (table 2.0.0, BRF-compatible). The two convert into each other losslessly and decode to the same IPA.
- Grade 1 only. There are no contractions. A grade parameter is accepted, ignored and noted.
- Measured, not claimed. Language support is whatever pronunciation source exists on the host: the Korean 正音 rules, a WIHP pronunciation dictionary, an espeak-ng voice, or spelling rules for a few languages without braille of their own. /languages and /health report the counts as computed at start-up. A language with no source gets 422 no_g2p — nothing is invented.
- Not a national braille. Output is not UEB, 한국 점자, 日本点字 or any other national code and must not be presented as such. /verify checks only the BRF file format.
Language support right now
Source: — · the numbers above are read from /languages when the page loads; they are not typed into this page.
Authentication & limits
- Rate limit: 5,000 requests per 15 minutes per IP (HTTP 429 with Retry-After).
- text up to 50,000 characters per request (413 text_too_long); JSON body up to 10 MB (413 payload_too_large).
- Server deadline 60 s → 503 timeout (retryable).
- Internal errors return internal_*_error without internal text.
Endpoints
{
"status": "ok", "healthy": true, "version": "0.9.2",
"defaultDots": 6, "canonicalDots": 8, "availableDots": [6, 8], "grade": 1,
"tableVersion": "2.0.0",
"tables": { "6": { "version": "2.0.0", "valid": true, ... }, "8": { "version": "8.0.0", "valid": true, "base": { "version": "2.0.0" } } },
"languages": { "total": 165, "dictionary": 55, "espeakVoices": 130, "rules": 11, "koRules": "1.1.1" },
"isp": { "lexicon": { "version": "1.1.0", "pinned": true, "sha256": "..." } }
}503 when degraded (table invalid). Values are examples from one host; read them, do not copy them.
| Field | Type | Description |
|---|---|---|
| text | string, required | Up to 50,000 characters. |
| language | string | Language code (ko, en, zh-CN, tpi …; alias lang). Guessed from the script when omitted (Latin-only → weak guess, lowQuality). |
| dots | 6 | 8 (JSON number) | 6 = v2 paper form (default), 8 = canonical form. "8" as a string → 400. |
| allowLetterFallback | boolean | Instead of 422 for a language without any source, return a clearly labelled non-phonetic letters-only output (confidence 0). |
Example — Korean (正音 rules, both forms identical for everyday Korean)
curl -s -X POST https://api-wialive.smilestory.ai/braille/api/convert \
-H "Content-Type: application/json" \
-d '{"text":"안녕하세요","language":"ko","dots":8}'
{
"input": "안녕하세요", "language": "ko",
"ipa": "an.njʌŋ.ha.se.jo", "surface": "안녕하세요",
"braille": "⠁⠝⠝⠬⠖⠿⠓⠁⠎⠑⠬⠕", "dots": 8, "cells8": 12,
"quality": "ko-rules", "confidence": 1, "grade": 1,
"tableVersion": "8.0.0", "table": { "version": "8.0.0", "schema": "v8", "base": { "version": "2.0.0" } },
"tokens": { "total": 1, "rules": 1, "g2p": 0, "untranscribed": 0 }, "dropped": 0, "unmapped": [],
"engine": "0.9.2"
}Example — English, 8-dot vs 6-dot
{"text":"hello","language":"en","dots":8} → "ipa": "həɫoʊ", "braille": "⠓⠜⠗⠕⡥", "tableVersion": "8.0.0", "quality": "dictionary"
{"text":"hello","language":"en","dots":6} → "ipa": "həɫoʊ", "braille": "⠓⠜⠗⠕⠐⠥", "tableVersion": "2.0.0", "quality": "dictionary"Example — a language without braille of its own (spelling rules)
{"text":"Gude, yu stap gut?","language":"tpi"}
→ "ipa": "ɡude, ju stap ɡut?", "braille": "⠛⠥⠙⠑⠂⠀⠬⠥⠀⠎⠞⠁⠏⠀⠛⠥⠞⠦",
"quality": "g2p-rules", "sources": { "rules": { "code": "tpi", "braille_status": { "has_national_braille": false, "evidence_level": "medium" } } }quality values
ko-rules · dictionary · g2p-espeak · g2p-espeak-low · g2p-rules · mixed · partial · numerals · letters-only · untranscribed · none. confidence (0–1) is computed from the token sources, never a constant.
Errors
| HTTP | error | When |
|---|---|---|
| 400 | empty_text · invalid_text · invalid_dots · invalid_language · invalid_json | bad parameters (type-checked) |
| 413 | text_too_long · payload_too_large | split the text |
| 422 | no_g2p | no dictionary, voice or rules for the language — nothing is invented |
| 501 | dots_not_available | dots: 8 while the 8-dot table is switched off |
| 503 | timeout | retry later |
Body: braille (cells, spaces, newlines), cellsPerLine 20–80 (40), linesPerPage 10–30 (25), download, strict8. BRF/NABCC is a 6-dot format: 8-dot input is converted to the 6-dot form losslessly and the answer says so (converted: {from: 8, to: 6, …}). Non-braille characters → 422 non_braille_input (never written as "?" into a file).
{"braille":"⠓⠜⠗⠕⡥"} → { "brf": "H>RO\"U\r\n…", "converted": { "from": 8, "to": 6, "cells8": 5, "cells6": 6, "table": "8.0.0", "baseTable": "2.0.0" }, "standard": "NABCC", "stats": { "pageCount": 1, ... } }Body: braille (required, ≤ 100,000 chars), original / text, language, title, download (default true → application/pdf; download=0 → JSON with pdfBase64 and dots). 8-dot cells are drawn as they are. Non-braille input → 422.
Body: brf (NABCC ASCII, ≤ 200,000 chars), cellsPerLine, linesPerPage. Checks NABCC mapping, line length, lines per page, CRLF, form feeds, round-trip, 6/8-dot consistency, letter distribution and an embosser-compatibility simulation. It verifies the file format only — it does not claim conformance to any national braille code, and paper embossing quality cannot be verified.
Forward: one of text+language, ipa, or braille (6- or 8-dot) → IPA → one unit per sound with its 5-component code from the WIA Talk lexicon (v1.1.0, pinned). A sound the lexicon lacks gets code: null — never invented. Reverse: codes → IPA → both braille forms; unknown codes are listed in missing.
{"text":"안녕하세요","language":"ko"}
→ "ipa": "annjʌŋhasejo", "codes": ["HS02-LC08-MV10-OR03-MM04", …12 codes],
"coverage": { "units": 12, "withCode": 12, "complete": true }, "braille": { "6": "⠁⠝⠝⠬⠖⠿⠓⠁⠎⠑⠬⠕", "8": "⠁⠝⠝⠬⠖⠿⠓⠁⠎⠑⠬⠕" }Also GET /isp/coverage (how much of the bridge is covered: 86 of 114 braille sound letters have an ISP code, 28 do not; tones have none yet) and /isp/demo (five demonstration phrases both ways). 501 when the 8-dot table is off, 503 when the lexicon file is missing.
{
"total": 165, "dictionary": 55, "dictionaryFiles": 29, "espeakVoices": 130, "rules": 11,
"rulesLanguages": ["ay","bm","ee","fj","ln","sg","sm","so","to","tpi","wo"], "koRules": "1.1.1",
"languages": [
{ "code": "ko", "sources": ["ko-rules","dictionary","espeak"], "koRules": { "version": "1.1.1", "primary": true } },
{ "code": "en", "sources": ["dictionary","espeak"], "dictionary": true, "espeakVoice": "en-us" },
{ "code": "tpi", "sources": ["rules"], "rulesActive": true, "rules": { "name": "Tok Pisin", "hasNationalBraille": false, "evidenceLevel": "medium" } },
...
]
}?source=dictionary|espeak|rules|ko-rules filters. 404 for a code with no source at all. These counts change with the host (dictionaries, espeak voices); they are measured at start-up, which is why this page does not print a fixed number.
Code examples
// JavaScript
const r = await fetch('https://api-wialive.smilestory.ai/braille/api/convert', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: 'Hello world', language: 'en', dots: 8 })
});
const d = await r.json();
if (r.status === 422) console.log('no pronunciation source for', d.lang);
else console.log(d.braille, d.quality, d.tableVersion); // ⠓⠜⠗⠕⡥⠀⠺⠜⢜⠗⠙ dictionary 8.0.0# Python
import requests
r = requests.post('https://api-wialive.smilestory.ai/braille/api/convert',
json={'text': '안녕하세요', 'language': 'ko', 'dots': 6}, timeout=60)
d = r.json()
print(d['braille'], d['quality'], d['tableVersion']) # ⠁⠝⠝⠬⠖⠿⠓⠁⠎⠑⠬⠕ ko-rules 2.0.0
brf = requests.post('https://api-wialive.smilestory.ai/braille/api/brf', json={'braille': d['braille']}).json()['brf']Versions & honesty notes
- Engine 0.9.2 (2026-10-01): 8-dot canonical form, Korean 正音 rules 1.1.1, spelling rules for 11 languages, ISP bridge, type-checked parameters, honest quality, default-ignorable handling.
- Tables are frozen files with sha256: 6-dot 2.0.0 (paper), 8-dot 8.0.0 (canonical). tableVersion in every answer tells you which produced the cells.
- Not yet done: tactile testing of the 8-dot form on real displays; native-speaker review of the spelling-rule languages; the ISP lexicon still lacks codes for 28 sounds. These are stated, not hidden.
- Source: github.com/WIA-Official/wia-braille (MIT).