# The wire format stays flat and deliberately does not mirror the schema Status: accepted Storage splits a tracked series across two tables (ADR-0003), but `GET /bookmarks` and `PUT /bookmarks/{key}` keep emitting and accepting one **flat** JSON object with `title`, `cover`, `last_chapter` and `latest_chapter` as siblings — exactly the shape they had when there was one table. The server joins on the way out and decomposes on the way in. ## Why a future reader will find this surprising The obvious move after splitting a table is to nest the JSON to match. Don't "fix" this. **A nested payload would have broken every installed userscript instantly.** Scripts read `b.title` directly; moving it to `b.series.title` yields `undefined` — no error, just blank rows and a New Chapter signal that silently reports nothing forever. Because Violentmonkey updates roughly once a day per device, the migration relies on old scripts continuing to work during a 14-day grace window. A nested format and that grace window are mutually exclusive. **It is also the better contract independently of compatibility.** A client rendering one row needs the title and the reading position together; nesting exports the re-stitching to every browser to mirror a decision about disk layout it should not know about. Keeping them separate lets storage change again later without a client release — which is the whole reason this ADR is worth the paragraph. ## Consequence The flat shape is a contract, not an implementation detail. Changing the storage schema must not change it. It follows the rule already in force for `updated_at`: the server owns the truth and returns the row **as stored**, and clients adopt the response rather than their own payload.