---
title: "S17 bindings"
description: "Data-binding map of Library · Library: every field to its source, and what each required state shows."
source: "design/hifi/S17/bindings.md"
---

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

# Data-binding map: S17 Library · Library

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

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

| Field | Source (endpoint or local state) | Format | Fallback |
|---|---|---|---|
| language filter | `call GET /articles` · `local:library.filter.language` → `GET /v1/articles?language=`; options = `config.supported_practice_languages` | "All languages" + endonyms; default = all practice languages (no param) | — |
| language filter | `local library.filter.language` |  |  |
| language filter | `api GET /config supported_practice_languages` |  |  |
| topic chips + counts | `call GET /topics` · `GET /v1/topics → name (Accept-Language), article_count` | chip with count | hide counts if missing |
| status segment | `local library.filter.status` · `local:library.filter.status` → `?status=all\|unseen\|completed` | — | — |
| row tile | `api GET /articles items[].topic.glyph` · `articles.items[n].topic.glyph / color_token` (glyph Accept-Language: 产 / Pr) | 48pt square | — |
| row tile | `api GET /articles items[].topic.color_token` |  |  |
| row title | `api GET /articles items[].title` · `articles.items[n].title` (article language) | `lang=` | — |
| row language tag | `api GET /articles items[].language` · `articles.items[n].language` | `lang.short.<tag>` — only when the list mixes languages | hidden |
| row meta | `api GET /articles items[].topic.name` · `topic.name`, `length.label` (Accept-Language), `difficulty` | `topic · length · Level n` | — |
| row meta | `api GET /articles items[].length.label` |  |  |
| row meta | `api GET /articles items[].difficulty` |  |  |
| status tag | `api GET /articles items[].user_status` · `user_status`, `best_overall`, `locked` + **CCR-3** `unlock_hint` | New / In progress / Best N / lock | — |
| status tag | `api GET /articles items[].best_overall` |  |  |
| status tag | `api GET /articles items[].locked` |  |  |
| status tag | `api GET /articles items[].unlock_hint` |  |  |
| pagination | `api GET /articles next_cursor` · `next_cursor` | infinite scroll | — |
| empty | `api GET /articles items` · `items = []` for filter | `library.empty.*` | — |
| `GET /v1/articles?language=en` | `call GET /articles` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `GET /v1/articles?language=zh-Hans` | `call GET /articles` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `GET /v1/articles?status=all\|unseen\|completed` | `call GET /articles` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `GET /v1/articles?topic=tech → items = []` | `api GET /articles items` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `GET /v1/topics → topics[].name / article_count (Accept-Language)` | `call GET /topics` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[0].best_overall` | `api GET /articles items[].best_overall` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[0].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[0].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[0].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[0].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[0].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[0].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[0].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[0].user_status` | `api GET /articles items[].user_status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[1].best_overall` | `api GET /articles items[].best_overall` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[1].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[1].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[1].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[1].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[1].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[1].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[1].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[1].user_status` | `api GET /articles items[].user_status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[2].best_overall` | `api GET /articles items[].best_overall` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[2].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[2].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[2].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[2].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[2].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[2].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[2].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[3].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[3].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[3].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[3].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[3].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[3].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[3].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[3].user_status` | `api GET /articles items[].user_status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[4].best_overall` | `api GET /articles items[].best_overall` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[4].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[4].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[4].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[4].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[4].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[4].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[4].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[4].user_status` | `api GET /articles items[].user_status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[5].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[5].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[5].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[5].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[5].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[5].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[5].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[5].user_status` | `api GET /articles items[].user_status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[6].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[6].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[6].locked + unlock_hint (CCR-3)` | `api GET /articles items[].locked` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[6].locked + unlock_hint (CCR-3)` | `api GET /articles items[].unlock_hint` |  |  |
| `articles.items[6].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[6].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[6].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[6].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[6].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[6].user_status` | `api GET /articles items[].user_status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[7].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[7].language` | `api GET /articles items[].language` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[7].locked + unlock_hint (CCR-3)` | `api GET /articles items[].locked` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[7].locked + unlock_hint (CCR-3)` | `api GET /articles items[].unlock_hint` |  |  |
| `articles.items[7].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[7].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[7].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[7].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[7].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[7].user_status` | `api GET /articles items[].user_status` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[8].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[8].locked + unlock_hint (CCR-3)` | `api GET /articles items[].locked` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[8].locked + unlock_hint (CCR-3)` | `api GET /articles items[].unlock_hint` |  |  |
| `articles.items[8].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[8].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[8].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[8].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[8].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `articles.items[9].id` | `api GET /articles items[].id` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[9].locked + unlock_hint (CCR-3)` | `api GET /articles items[].locked` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[9].locked + unlock_hint (CCR-3)` | `api GET /articles items[].unlock_hint` |  |  |
| `articles.items[9].title` | `api GET /articles items[].title` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[9].topic` | `api GET /articles items[].topic` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[9].topic.name / length.label / difficulty` | `api GET /articles items[].topic.name` · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `articles.items[9].topic.name / length.label / difficulty` | `api GET /articles items[].length.label` |  |  |
| `articles.items[9].topic.name / length.label / difficulty` | `api GET /articles items[].difficulty` |  |  |
| `error.code` | `status GET /articles 5XX internal` · **pending CCR-14** · From the board's `data-bind`; no row in data-binding.md yet. |  |  |
| `error.message (Accept-Language)` | `status GET /articles 5XX internal` · **pending CCR-14** · From the board's `data-bind`; no row in data-binding.md yet. |  |  |

## Contract change requests

Fields above that no endpoint sends yet. Until each lands, the screen falls back as noted.

| Change | Request | Fallback until it lands |
|---|---|---|
| CCR-14 | Document the error responses on every operation the screens read: `4XX` and `5XX` → `#/components/responses/Error` (`ErrorEnvelope`), and `402` `entitlement_required` on the hard-paywalled reads the API description lists (`/home`, `/articles*`, `/topics`, `/sessions`, `/growth`, `/achievements`, `/weekly-quests`, `/plan`). Today only a few operations list any error, `GET /home` none. | The client decodes any non-2xx body as `ErrorEnvelope`, as the API description says. |

## Binds on the boards

The `data-bind` values across every state.

- `GET /v1/articles?language=en`
- `GET /v1/articles?language=zh-Hans`
- `GET /v1/articles?status=all|unseen|completed`
- `GET /v1/articles?topic=tech → items = []`
- `GET /v1/topics → topics[].name / article_count (Accept-Language)`
- `articles.items[0].best_overall`
- `articles.items[0].id`
- `articles.items[0].language`
- `articles.items[0].title`
- `articles.items[0].topic`
- `articles.items[0].topic.name / length.label / difficulty`
- `articles.items[0].user_status`
- `articles.items[1].best_overall`
- `articles.items[1].id`
- `articles.items[1].language`
- `articles.items[1].title`
- `articles.items[1].topic`
- `articles.items[1].topic.name / length.label / difficulty`
- `articles.items[1].user_status`
- `articles.items[2].best_overall`
- `articles.items[2].id`
- `articles.items[2].language`
- `articles.items[2].title`
- `articles.items[2].topic`
- `articles.items[2].topic.name / length.label / difficulty`
- `articles.items[3].id`
- `articles.items[3].language`
- `articles.items[3].title`
- `articles.items[3].topic`
- `articles.items[3].topic.name / length.label / difficulty`
- `articles.items[3].user_status`
- `articles.items[4].best_overall`
- `articles.items[4].id`
- `articles.items[4].language`
- `articles.items[4].title`
- `articles.items[4].topic`
- `articles.items[4].topic.name / length.label / difficulty`
- `articles.items[4].user_status`
- `articles.items[5].id`
- `articles.items[5].language`
- `articles.items[5].title`
- `articles.items[5].topic`
- `articles.items[5].topic.name / length.label / difficulty`
- `articles.items[5].user_status`
- `articles.items[6].id`
- `articles.items[6].language`
- `articles.items[6].locked + unlock_hint (CCR-3)`
- `articles.items[6].title`
- `articles.items[6].topic`
- `articles.items[6].topic.name / length.label / difficulty`
- `articles.items[6].user_status`
- `articles.items[7].id`
- `articles.items[7].language`
- `articles.items[7].locked + unlock_hint (CCR-3)`
- `articles.items[7].title`
- `articles.items[7].topic`
- `articles.items[7].topic.name / length.label / difficulty`
- `articles.items[7].user_status`
- `articles.items[8].id`
- `articles.items[8].locked + unlock_hint (CCR-3)`
- `articles.items[8].title`
- `articles.items[8].topic`
- `articles.items[8].topic.name / length.label / difficulty`
- `articles.items[9].id`
- `articles.items[9].locked + unlock_hint (CCR-3)`
- `articles.items[9].title`
- `articles.items[9].topic`
- `articles.items[9].topic.name / length.label / difficulty`
- `error.code`
- `error.message (Accept-Language)`

## States

- Loading: Skeleton rows. No import button in v1 (config.flags.import_enabled = false).
- Empty: From [states.md](/design/states/index.md) (Library): [library--filter-empty](/design/gallery/library/library--filter-empty.html)
- Error (retryable / not): Retryable load failure. No import button in v1 (config.flags.import_enabled = false).
- Offline: From [states.md](/design/states/index.md) (Library): cached first page + banner; filters on cached data only
