---
title: "S22 bindings"
description: "Data-binding map of Compress · Second take · round 2: every field to its source, and what each required state shows."
source: "design/hifi/S22/bindings.md"
---

> Part of [Brand and design](/design/llms.txt). Every page: [llms.txt](/llms.txt).

# Data-binding map: S22 Compress · Second take · round 2

> Generated by `botgumi-design index` from `../../screens.json`. Edit that file, not this page.

Every field [S22](/design/hifi/s22/index.md) shows and where it comes from.

| Field | Source (endpoint or local state) | Format | Fallback |
|---|---|---|---|
| step | `api GET /sessions/{session_id} stage` · `session.stage` → 2/3 or 3/3 | — | — |
| focus chip (R1) | `api GET /sessions/{session_id} previous_focus.title` · `session.previous_focus.title` (Accept-Language) | `record.focus.round1` | hide when null |
| previous advice card (R2) | `api GET /sessions/{session_id} round1_assessment.one_action.instruction` · `session.round1_assessment.one_action.instruction / example_sentence` (article language) | ink card | — |
| previous advice card (R2) | `api GET /sessions/{session_id} round1_assessment.one_action.example_sentence` |  |  |
| last duration (R2) | `api GET /sessions/{session_id} attempts[].duration_ms` · `session.attempts[round=1, status=assessed].duration_ms` | `mm:ss` | hide clause |
| timer | `local recorder.effectiveMs` · `local:recorder.effectiveMs` | `mm:ss`, tabular | — |
| max | `api GET /sessions/{session_id} max_round1_ms` · `session.max_round1_ms` / `max_round2_ms` | `/ mm:ss` | config limits |
| max | `api GET /sessions/{session_id} max_round2_ms` |  |  |
| min | `api GET /sessions/{session_id} min_valid_ms` · `session.min_valid_ms` | gates CTA; "N more seconds" | 20 000 |
| waveform | `local recorder.levelDb` · `local:recorder.levelDb` (30 Hz) | 34 bars | flat bars |
| indicator | `local recorder.state` · `local:recorder.state` | recording / paused | — |
| interrupted sheet | `device audio_interruption` · `AVAudioSession.interruptionNotification` / route change + `local:recorder.fileURL` | `record.interrupted.*` | — |
| interrupted sheet | `local recorder.fileURL` |  |  |
| permission | `device microphone_permission` · `AVAudioApplication.recordPermission` | denied card + Settings deep link | — |
| submit | `call POST /sessions/{id}/attempts` · `POST /v1/sessions/{id}/attempts {round, duration_ms, content_type, size_bytes, sha256, pause_count}` → PUT → `POST /v1/attempts/{id}/submit` | — | offline: persist + queue (processing--offline) |
| submit | `call POST /attempts/{id}/submit` |  |  |
| `UIApplication.openSettingsURLString` | `device app_settings` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `derived: recorder.effectiveMs ≥ session.min_valid_ms` | `derived recording_long_enough` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `local discard (or POST /v1/attempts/{id}/discard if registered)` | `call POST /attempts/{attempt_id}/discard` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `session.attempts[round=1].duration_ms` | `api GET /sessions/{session_id} attempts[].duration_ms` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `session.max_round2_ms` | `api GET /sessions/{session_id} max_round2_ms` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `session.round1_assessment.one_action.example_sentence` | `api GET /sessions/{session_id} round1_assessment.one_action.example_sentence` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `session.stage → derived step` | `api GET /sessions/{session_id} stage` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |

## Derived values

Computed on device; each from the sources it names.

| Value | From | How |
|---|---|---|
| `recording_long_enough` | `local recorder.effectiveMs`, `api GET /sessions/{session_id} min_valid_ms` | Stop is enabled once the recording reaches the minimum valid length. |

## Binds on the boards

The `data-bind` values across every state.

- `UIApplication.openSettingsURLString`
- `derived: recorder.effectiveMs ≥ session.min_valid_ms`
- `local discard (or POST /v1/attempts/{id}/discard if registered)`
- `local:recorder.effectiveMs`
- `local:recorder.state`
- `session.attempts[round=1].duration_ms`
- `session.max_round2_ms`
- `session.min_valid_ms`
- `session.round1_assessment.one_action.example_sentence`
- `session.round1_assessment.one_action.instruction`
- `session.stage → derived step`

## States

- Loading: From [states.md](/design/states/index.md) (Record R1 / R2): —
- Empty: From [states.md](/design/states/index.md) (Record R1 / R2): —
- Error (retryable / not): From [states.md](/design/states/index.md) (Record R1 / R2): — · fatal: —
- Offline: From [states.md](/design/states/index.md) (Record R1 / R2): records normally; banner via [offline--toast](/design/gallery/global/offline--toast.html)
