# Reference

Generated from `components.schemas` and `components.headers`. Descriptions are the schema text.

## Operations

- `GET /health` `getHealth` Ready check
- `POST /v1/tutor` `scoreTutor` Score a Koine word or short verse
- `POST /mcp` `callMcp` MCP Streamable HTTP (JSON, not SSE)
- `PUT /v1/uploads/{id}` `putUpload` Store one WAV on the create_upload slot
- `GET /v1/uploads/{id}` `getUpload` Read a stored WAV

## Examples

| Name | Target | IPA | proximity | real_edits |
| --- | --- | --- | --- | --- |
| `exactLogos` | λόγος | `l o g o s` | 1.0 | 0 |
| `stressShift` | λόγος | `l o g ˈo s` | 0.9 | 0 |
| `thetaAsS` | θεός | `s e o s` | 0.75 | 1 |
| `acceptableTheta` | θεός | `tʰ e o s` | 0.988187 | 0 |
| `verseArche` | ἐν ἀρχῇ | `e n a r x i` | 1.0 | 0 |

Full responses are the matching examples on `POST /v1/tutor` in [openapi.json](/openapi.json).

## TutorRequest

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `target` | string | yes | Greek word or short verse. |
| `ipa` | string / array of string |  | Learner phones. A string is tokenized. Explicit ipa wins over audio_base64 and audio_url. |
| `audio_base64` | string |  | WAV bytes, base64. At most 256 KiB decoded. Larger clips use create_upload and audio_url. Resampled to 16 kHz mono. Under 0.12 s is unscored. |
| `audio_url` | string |  | audio_url from the MCP tool create_upload, or an https URL. An upload id is read from this process. Other URLs must be https. The worker rejects userinfo, IP literals that are private, loopback, link-local, or otherwise non-public, and names such as localhost. It follows at most 3 redirects and checks each hop the same way. The body is read until 256 KiB, the same cap as audio_base64, then aborted. Content-Type must be audio/wav, audio/x-wav, audio/wave, or audio/vnd.wave. Failures are problem+json with a stable `code` (`audio-url-not-https`, `audio-url-host`, `audio-url-redirects`, `audio-url-too-large`, `audio-url-type`, `audio-url-timeout`). |
| `scheme` | string |  |  |
| `different_word` | boolean |  |  |
| `calibrate` | boolean |  |  |
| `thresholds` | object |  | Map of name to minimum, or a list of minimums. |
| `posteriors` | array of array of number |  | CTC frame log-probabilities. With vocab, each target phone gets a confidence. |
| `vocab` | array of string |  |  |
| `blank_id` | integer |  |  |
| `recognizer` | string |  |  |

## TutorResponse

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `api` | enum | yes |  |
| `scheme` | string | yes |  |
| `target_text` | string |  |  |
| `target_words` | array of object |  |  |
| `observed_phones` | array of string |  |  |
| `proximity` | number | yes | Calibrated score after the stress penalty. Unchanged by coaching and heard_as. |
| `lenient_similarity` | number |  |  |
| `strict_similarity` | number |  |  |
| `real_edits` | integer | yes | Count of phones whose scoring status is incorrect, missing, or extra. |
| `phones` | array of `ScoredPhone` | yes |  |
| `different_word` | `DifferentWord` |  |  |
| `stress` | object | yes | Scoring stress object. The penalty is already inside proximity. |
| `gop` | object |  | Present when the request carried posteriors. |
| `thresholds` | array of object |  |  |
| `coaching` | `Coaching` | yes |  |
| `heard_as` | `HeardAs` | yes |  |
| `input_quality` | object |  | Present when the request carried audio. Measured before resampling. Does not change proximity. Absent on IPA examples. |

## ScoredPhone

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `target` | string or null |  |  |
| `observed` | string or null |  |  |
| `status` | enum | yes | Scoring label. This is what real_edits counts. It is not the learner label. |
| `cost` | number | yes |  |
| `confidence` | number or null |  | Calibrated GOP confidence. Null when the request has no posteriors. |
| `uncertain` | boolean |  | True when confidence is below 0.75 or alignment failed. |

## Coaching

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `summary` | `CoachingSummary` | yes |  |
| `sounds` | array of `CoachingSound` | yes |  |
| `stress` | `CoachingStress` | yes |  |

## CoachingSummary

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `proximity` | number | yes | Same rounded value as the response proximity. |
| `fixes` | array of `CoachingFix` | yes | Repairs with score_gain above 0, at most three. Ranked by score_gain descending, then a sound fix before a stress fix, then sound_index. |

## CoachingFix

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `rank` | integer | yes |  |
| `kind` | enum | yes |  |
| `sound_index` | integer |  | Present when kind is sound. Index into coaching.sounds and phones. |
| `word` | string or null |  |  |
| `letters` | string or null |  |  |
| `target` | string or null |  |  |
| `heard` | string or null |  |  |
| `status` | `LearnerStatus` |  |  |
| `target_syllable` | integer or null |  | Present when kind is stress. |
| `detected_syllable` | integer or null |  |  |
| `target_letters` | string or null |  | Greek letters of the target syllable. Present when kind is stress. |
| `detected_letters` | string or null |  |  |
| `error_code` | string | yes | Same code as the sound, or stress_shift. |
| `score_gain` | number | yes | Proximity restored by fixing this one sound, or by clearing the stress penalty. |

## CoachingSound

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `index` | integer | yes |  |
| `word` | string or null |  |  |
| `letters` | string or null |  | Greek letters that produced the target phone. Accents stay. Null on an extra. |
| `syllable` | integer or null |  | Syllable index inside the word. The stress object uses the verse-level index. |
| `target` | string or null |  | Lexicon phone, IPA. |
| `heard` | string or null |  | Aligned attempt phone, IPA. Null when the target phone is missing. |
| `status` | `LearnerStatus` | yes |  |
| `closeness` | number | yes | Articulatory closeness. Exact is 1. Missing and extra are 0. Vowels: 1 - min(1, height/6 + 0.5·backness/2 + 0.5·|rounding|). Consonants: 1 - min(1, place/8 + 0.5·manner/7 + 0.5·|voice|). A vowel against a consonant is 0. Length alone is 0.9. Aspiration alone is 0.85. Rounded to 6 decimal places. Low confidence does not change this number. |
| `low_confidence` | boolean | yes | True when the aligned phone row has uncertain true, or confidence is a number below 0.75. An IPA-only phone with null confidence is false. When this is true and the feature label would be far, status is close. The error code and the feature diff stay on the phones. An exact phone stays exact. |
| `error_code` | string or null | yes | Stable code for this phone difference. Null when status is exact. A consonant substitution names place and manner. Fricatives s, z, ʃ, and ʒ use sibilant in the code, so θ→s is dental_fricative_to_alveolar_sibilant. Voice, aspiration, and length words appear only when that feature differs. A vowel substitution names height, backness, and rounding on both sides. Missing and extra prefix the same token. Low confidence does not change the code. |
| `feature_diff` | array of `FeatureDiff` | yes | Features that differ, in order: kind, place, manner, voicing, aspiration, rounding, height, backness, length. Empty when status is exact. |

## FeatureDiff

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `feature` | enum | yes |  |
| `target` | string or null | yes | Label on the lexicon phone. Null on an extra, or when that side has no such feature. |
| `heard` | string or null | yes | Label on the attempt phone. Null when the target phone is missing. |

## CoachingStress

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `target_syllable` | integer or null | yes | Accented syllable, same index as stress.target_syllable. |
| `detected_syllable` | integer or null | yes | Attempt syllable, same index as stress.attempt_syllable. |
| `match` | boolean or null | yes | Null when stress abstains or the attempt has no accent mark. |
| `target_letters` | string or null | yes | Greek letters of the target syllable, accents kept. Null when the index is null. |
| `detected_letters` | string or null | yes | Greek letters of the detected syllable. Null when the index is null. |
| `error_code` | string or null | yes | stress_shift when match is false. Null when match is true or null. |

## LearnerStatus

exact matches scoring status correct.
acceptable_variant matches scoring status "acceptable variant" (the 0.02 taught-versus-historical pairs).
close is scoring status acceptable, or an incorrect substitution whose closeness is at least 0.5,
or a far substitution whose low_confidence flag is true.
far is an incorrect substitution whose closeness is below 0.5 and whose confidence is not low.
missing and extra match those scoring statuses.

Enum: `exact`, `acceptable_variant`, `close`, `far`, `missing`, `extra`

## HeardAs

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `scheme` | string | yes | The request scheme. Koine is the product path. The letter table also covers attic-greek, classical-latin, ecclesiastical-latin, living-koine, and lucian. The worker scores koine-greek. |
| `script` | enum | yes |  |
| `method` | enum | yes | posterior when at least one candidate received a CTC forward score from the request posteriors. distance-softmax otherwise. Both paths order candidates with the same softmax. Published confidence is the Platt map, not that mass. |
| `temperature` | number | yes | Softmax temperature used to order candidates. The shipped value is 0.1. Published confidence is a separate Platt map of the top-1 gap, the normalized tutor cost, and a non-word indicator. |
| `other_score` | number | yes | Rank score of the other bin before the softmax. The shipped value is 0.5. |
| `truncated` | boolean | yes | True when the one-edit neighborhood was capped at 1500 lexicon rows. |
| `candidates` | array of `HeardAsCandidate` | yes | Whole-string ranking. For one word this is that word's list. For a verse it is the spelling of the whole clause; read `words` for a lexicon hit on each target word. |
| `words` | array of `HeardAsWord` | yes | One ranking per target word, sliced by the tutor alignment. |

## HeardAsWord

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `word` | string | yes |  |
| `lemma` | string | yes |  |
| `target_phones` | array of string | yes |  |
| `observed_phones` | array of string | yes | Heard phones aligned to this target word. |
| `truncated` | boolean | yes |  |
| `candidates` | array of `HeardAsCandidate` | yes |  |

## HeardAsCandidate

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `rank` | integer | yes |  |
| `kind` | enum | yes |  |
| `is_word` | boolean | yes | True only for a lexicon row. |
| `word` | string or null |  | Lexicon orthography, or the generated spelling. Null for other. |
| `lemma` | string or null |  |  |
| `greek` | string or null |  | Greek spelling. The lexicon form for a word, the generated letters for a non-word. Null for Latin schemes and for other. |
| `latin` | string or null |  | ASCII transliteration of the candidate phones. Null for other. |
| `ipa` | string or null |  |  |
| `phones` | array or null |  |  |
| `similarity` | number or null |  | Guesser similarity of the candidate phones to the heard phones. A transliteration is one cheap edit below a perfect match (1 - 0.25/n - 0.01), not 1. Null for other. |
| `score` | number |  | Value that entered the softmax, before truncation of the confidence. |
| `ctc_log_prob` | number or null |  | CTC forward log-probability when method is posterior and this candidate was scored. Null otherwise. |
| `confidence` | number | yes | Platt confidence of the top row, truncated to 6 decimals. Later rows keep softmax proportions scaled to stay below it. The list sums to at most 1. |

## Problem

RFC 9457 problem details. `type` is a section of the errors guide.
`detail` is the same sentence earlier plain-text errors used.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `type` | string | yes | Problem type. A section of /docs/errors. |
| `title` | string | yes | Short HTTP reason. |
| `status` | integer | yes | HTTP status code. |
| `detail` | string | yes | One sentence. No pronunciation advice. |
| `instance` | string |  | Request path. |
| `code` | string |  | Stable token on rate-limit, body-size, upload-slot, and audio_url failures. Examples include `rate-limit-exceeded`, `body-too-large`, `upload-slots-full`, `audio-url-host`. |

## UnscoredResponse

HTTP 200 when the trimmed audio is under 0.12 s. proximity is null. This is not a problem document.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `api` | `tutor-v1` | yes |  |
| `scheme` | string | yes |  |
| `target_text` | string | yes |  |
| `proximity` | null | yes |  |
| `unscored` | `too_short` | yes |  |
| `duration_s` | number | yes | Trimmed duration in seconds, 6 decimal places. |
| `input_quality` | object |  | Present on audio responses. Does not change proximity. Absent on IPA responses. |

## DifferentWord

Closest one-edit lexicon neighbor. `detected` does not change proximity.
`different_word: false` on the request returns checked false and skips the search.
A verse returns reason `single-word targets only`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `checked` | boolean | yes |  |
| `detected` | boolean | yes |  |
| `reason` | string |  | Present when a verse skips the check. |
| `word` | string |  |  |
| `lemma` | string |  |  |
| `similarity` | number |  |  |

## JsonRpcRequest

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `jsonrpc` | `2.0` | yes |  |
| `id` | string / integer / null |  | String or number. Omit for a notification. |
| `method` | string | yes |  |
| `params` | object |  |  |

## JsonRpcResponse

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `jsonrpc` | `2.0` | yes |  |
| `id` | string / integer / null |  |  |
| `result` | object |  |  |
| `error` | object |  |  |

## Headers

### RateLimitPolicy

draft-ietf-httpapi-ratelimit-headers. Anonymous is `"tutor";q=10;w=60`.
A key is `"tutor";q=120;w=60`. The Workers bindings `TUTOR` and `TUTOR_KEY` are authoritative when present.
The in-memory window is the fallback.

### RateLimit

Remaining requests `r` and whole seconds `t` until the oldest counted request leaves the window.

### RetryAfter

Seconds to wait. Same value as RateLimit `t`, sent on 429.
