Base URL (public)
https://api-wialive.smilestory.ai/braille/api
Planned alias (after the api.wiabraille.com vhost is repointed to the engine)
https://api.wiabraille.com/api
OpenAPI 3.0: GET /docs (YAML) — the same contract as this page

What it is — and what it is not

Language support right now

…
languages with at least one pronunciation source
…
正音 rules (Korean)
…
pronunciation dictionary (WIHP)
…
G2P only (espeak-ng)
…
spelling rules (no braille of their own)

Source: — · the numbers above are read from /languages when the page loads; they are not typed into this page.

Authentication & limits

No authentication. No API key. No plans. The service is free (MIT).

Endpoints

GET/healthengine, tables, measured language counts
{
  "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.

POST/converttext → IPA → braille (also GET with query parameters)
FieldTypeDescription
textstring, requiredUp to 50,000 characters.
languagestringLanguage code (ko, en, zh-CN, tpi …; alias lang). Guessed from the script when omitted (Latin-only → weak guess, lowQuality).
dots6 | 8 (JSON number)6 = v2 paper form (default), 8 = canonical form. "8" as a string → 400.
allowLetterFallbackbooleanInstead 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

HTTPerrorWhen
400empty_text · invalid_text · invalid_dots · invalid_language · invalid_jsonbad parameters (type-checked)
413text_too_long · payload_too_largesplit the text
422no_g2pno dictionary, voice or rules for the language — nothing is invented
501dots_not_availabledots: 8 while the 8-dot table is switched off
503timeoutretry later
POST/brfUnicode braille → BRF (embosser file)

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, ... } }
POST/pdfbraille cells → PDF (visual, with the original text)

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.

POST/verifyBRF file-format checks (9 checks)

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.

POST/ispbraille ⇄ IPA ⇄ ISP (WIA Talk hand codes)

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.

GET/languagesmeasured language support · /languages/{code} · /languages/search/query?q=
{
  "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