{"openapi":"3.1.0","info":{"title":"NewTwos API","version":"1.0.0","description":"Read and write your NewTwos lists and things. A list holds things. A thing is a `todo` (a checkbox) or a plain line, and can carry a url, tags, an indent depth, and a long-form markdown note.\n\nAuthenticate with a personal API key created at https://writethingsdown.com/settings/api-keys and sent as `Authorization: Bearer twos_…`. A key only ever reaches its own owner's data, so no request takes a user id.\n\n**Resolve ids before you use them.** Every get, update, delete, and reminder operation takes an id. Call `GET /search` to turn a name into an id, or `GET /lists` to enumerate. Ids are opaque, and a guessed one returns 404.\n\n**Day lists come from `GET /today`.** The date-titled daily list is the center of the product. `GET /today` resolves or creates the list for today, yesterday, tomorrow, or any date. Scanning `GET /lists` for a date-titled one instead will find duplicates or nothing.\n\n**Write in bulk.** `POST /things/bulk` creates up to 500 things in one call, and `PATCH`/`DELETE /things/bulk` change many at once. Looping the single-item routes burns the rate limit and reorders nothing faster.\n\n**Every write is real.** There is no sandbox and no test mode. A delete removes a thing from a person's account. Confirm destructive calls with whoever asked for them.","contact":{"name":"NewTwos Support","email":"help@twosapp.com","url":"https://writethingsdown.com/developers"},"termsOfService":"https://writethingsdown.com/terms","license":{"name":"NewTwos Terms of Service","url":"https://writethingsdown.com/terms"}},"externalDocs":{"description":"Full documentation, plus /llms.txt for the same contract as plain text.","url":"https://writethingsdown.com/developers"},"servers":[{"url":"https://writethingsdown.com/api/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Lists","description":"Your lists. A list holds things."},{"name":"Things","description":"The todos and notes inside your lists."},{"name":"Reminders","description":"Reminders set on your things. A thing has at most one."},{"name":"Search","description":"Turn a name into an id. Start here when you only know what something is called."},{"name":"Tags","description":"The tags you use on your things."}],"paths":{"/lists":{"get":{"operationId":"list-lists","summary":"List lists","description":"Returns your lists (non-hidden, non-archived), most recently modified first, 50 per page.\n\nRequires the `read:lists` scope.","tags":["Lists"],"x-scope":"read:lists","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer"},"description":"Zero-based page index.","example":0}],"responses":{"200":{"description":"List lists","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"lists":{"type":"array","items":{"$ref":"#/components/schemas/List"}},"page":{"type":"integer"},"page_size":{"type":"integer"},"has_more":{"type":"boolean"}}},"example":{"lists":[{"id":"64f0c2a1e4b0a1234567890a","title":"Groceries","emoji":"🛒","today":false,"favorited":false,"archived":false,"sort":"chronological","created":"2026-06-01T12:00:00.000Z","updated":"2026-06-02T08:30:00.000Z"}],"page":0,"page_size":50,"has_more":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"operationId":"create-list","summary":"Create a list","description":"Creates a new list. Fields you omit fall back to your account defaults (e.g. list sort). Pass `things` to create the list already filled, in one call (returns `created`/`skipped` counts alongside the list).\n\nRequires the `write:lists` scope.","tags":["Lists"],"x-scope":"write:lists","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string","description":"The list name."},"emoji":{"type":"string","description":"An emoji shown next to the list."},"things":{"type":"array","items":{"type":"object"},"description":"Optional initial things (max 500), in order. Each item: { text, type?, url?, list_ref?, tags?, photos?, completed?, tabs?, created?, and formatting: bold?, italic?, underline?, header?, subheader?, quote?, code? }."}}},"example":{"title":"Groceries","emoji":"🛒","things":[{"text":"Oat milk","type":"todo"}]}}}},"responses":{"201":{"description":"Create a list","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"list":{"$ref":"#/components/schemas/List"},"created":{"type":"integer"},"skipped":{"type":"integer"}}},"example":{"list":{"id":"64f0c2a1e4b0a1234567890a","title":"Groceries","emoji":"🛒","today":false,"favorited":false,"archived":false,"sort":"chronological","created":"2026-06-01T12:00:00.000Z","updated":"2026-06-02T08:30:00.000Z"},"created":1,"skipped":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/lists/import":{"post":{"operationId":"import-markdown","summary":"Import a list from markdown","description":"Create a NEW list from a markdown document in one call. The title comes from `title`, or the document’s first heading (`# …`) when omitted; the rest becomes the list’s things (checkboxes → todos, `1.` → numbered, `-`/`*` → bullets, `##` → heading, `###` → subheading, indentation → nesting).\n\nRequires the `write:lists` scope.","tags":["Lists"],"x-scope":"write:lists","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["markdown"],"properties":{"markdown":{"type":"string","description":"The markdown document to import."},"title":{"type":"string","description":"The new list’s name. Omit to use the markdown’s first \"# Heading\"."},"emoji":{"type":"string","description":"An emoji for the new list."}}},"example":{"title":"Trip plan","markdown":"# Trip plan\n- [ ] Book flights\n- [ ] Pack"}}}},"responses":{"201":{"description":"Import a list from markdown","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"list":{"$ref":"#/components/schemas/List"},"created":{"type":"integer"},"skipped":{"type":"integer"}}},"example":{"list":{"id":"64f0c2a1e4b0a1234567890a","title":"Groceries","emoji":"🛒","today":false,"favorited":false,"archived":false,"sort":"chronological","created":"2026-06-01T12:00:00.000Z","updated":"2026-06-02T08:30:00.000Z"},"created":2,"skipped":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/lists/{id}":{"get":{"operationId":"get-list","summary":"Get a list","description":"Returns a single list you own, its things (in the app’s display order, per the list’s `sort`), and any reminders on those things.\n\nRequires the `read:lists` scope.","tags":["Lists"],"x-scope":"read:lists","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The list id.","example":"64f0c2a1e4b0a1234567890a"},{"name":"max_text","in":"query","required":false,"schema":{"type":"string"},"description":"Truncate each thing’s text to this many characters (… marks a cut). For cheap reads of long lists.","example":"120"}],"responses":{"200":{"description":"Get a list","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"list":{"$ref":"#/components/schemas/List"},"things":{"type":"array","items":{"$ref":"#/components/schemas/Thing"}},"reminders":{"type":"array","items":{"$ref":"#/components/schemas/Reminder"}}}},"example":{"list":{"id":"64f0c2a1e4b0a1234567890a","title":"Groceries","emoji":"🛒","today":false,"favorited":false,"archived":false,"sort":"chronological","created":"2026-06-01T12:00:00.000Z","updated":"2026-06-02T08:30:00.000Z"},"things":[{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":false,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"}],"reminders":[{"id":"64f0c2a1e4b0a1234567890c","thing_id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","text":"Buy oat milk","at":"2026-06-03T17:00:00.000Z","all_day":false,"repeat":"none","every":1,"repeat_until":null,"days":[],"end":"2026-06-03T18:00:00.000Z","duration_minutes":60,"alert_minutes":10,"second_alert_minutes":null,"color":"#FF9500"}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"operationId":"update-list","summary":"Update a list","description":"Updates a list. Only the fields you send are changed. Renaming cascades to the list’s things.\n\nRequires the `write:lists` scope.","tags":["Lists"],"x-scope":"write:lists","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The list id.","example":"64f0c2a1e4b0a1234567890a"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"title":{"type":"string","description":"Rename the list."},"emoji":{"type":"string","description":"Change the emoji."},"favorited":{"type":"boolean","description":"Star or unstar the list."},"archived":{"type":"boolean","description":"Archive or unarchive the list."}}},"example":{"title":"Weekend groceries","emoji":"🥑"}}}},"responses":{"200":{"description":"Update a list","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"list":{"$ref":"#/components/schemas/List"}}},"example":{"list":{"id":"64f0c2a1e4b0a1234567890a","title":"Weekend groceries","emoji":"🥑","today":false,"favorited":false,"archived":false,"sort":"chronological","created":"2026-06-01T12:00:00.000Z","updated":"2026-06-02T08:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"delete-list","summary":"Delete a list","description":"Permanently deletes a list and all of its things. Idempotent.\n\nRequires the `write:lists` scope.","tags":["Lists"],"x-scope":"write:lists","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The list id.","example":"64f0c2a1e4b0a1234567890a"}],"responses":{"200":{"description":"Delete a list","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}},"example":{"success":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/today":{"get":{"operationId":"get-today","summary":"Get a day list","description":"Returns the user’s date-titled day list for a date — resolved in the user’s timezone — creating it if it doesn’t exist yet (like opening that day in the app). Same response shape as Get a list. Use it for the daily-list loop without hunting for an id.\n\nRequires the `read:lists` scope.","tags":["Lists"],"x-scope":"read:lists","parameters":[{"name":"date","in":"query","required":false,"schema":{"type":"string"},"description":"\"today\" (default), \"yesterday\", \"tomorrow\", or an ISO 8601 date (e.g. 2026-04-01).","example":"today"},{"name":"max_text","in":"query","required":false,"schema":{"type":"string"},"description":"Truncate each thing’s text to this many characters.","example":"120"}],"responses":{"200":{"description":"Get a day list","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"list":{"$ref":"#/components/schemas/List"},"things":{"type":"array","items":{"$ref":"#/components/schemas/Thing"}},"reminders":{"type":"array","items":{"$ref":"#/components/schemas/Reminder"}}}},"example":{"list":{"id":"64f0c2a1e4b0a1234567890a","title":"Tues Apr 1, 2026","emoji":"🛒","today":true,"favorited":false,"archived":false,"sort":"chronological","created":"2026-06-01T12:00:00.000Z","updated":"2026-06-02T08:30:00.000Z"},"things":[{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":false,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"}],"reminders":[{"id":"64f0c2a1e4b0a1234567890c","thing_id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","text":"Buy oat milk","at":"2026-06-03T17:00:00.000Z","all_day":false,"repeat":"none","every":1,"repeat_until":null,"days":[],"end":"2026-06-03T18:00:00.000Z","duration_minutes":60,"alert_minutes":10,"second_alert_minutes":null,"color":"#FF9500"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/things":{"get":{"operationId":"list-things","summary":"List things","description":"List your things newest-first across all lists (or one list). Built for automation polling: each thing has a stable `id` for dedup, and `since` + `sort` form an incremental cursor. Filter by list, completion, type, or tag.\n\nRequires the `read:things` scope.","tags":["Things"],"x-scope":"read:things","parameters":[{"name":"list_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only things in this list.","example":"64f0c2a1e4b0a1234567890a"},{"name":"completed","in":"query","required":false,"schema":{"type":"boolean"},"description":"Filter by completion (true/false).","example":false},{"name":"type","in":"query","required":false,"schema":{"type":"string"},"description":"Filter by type: todo, note, dash, number, or bullet.","example":"todo"},{"name":"tag","in":"query","required":false,"schema":{"type":"string"},"description":"Only things with this tag.","example":"errands"},{"name":"since","in":"query","required":false,"schema":{"type":"string"},"description":"Only things newer than this ISO 8601 time (by the chosen sort).","example":"2026-06-01T00:00:00.000Z"},{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"created (default) or updated.","example":"created"},{"name":"page","in":"query","required":false,"schema":{"type":"string"},"description":"Zero-based page (50 per page). Page until has_more is false to read everything.","example":"0"},{"name":"max_text","in":"query","required":false,"schema":{"type":"string"},"description":"Truncate each thing’s text to this many characters (… marks a cut).","example":"120"}],"responses":{"200":{"description":"List things","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"things":{"type":"array","items":{"$ref":"#/components/schemas/Thing"}},"page":{"type":"integer"},"page_size":{"type":"integer"},"has_more":{"type":"boolean"}}},"example":{"things":[{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":false,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"}],"page":0,"page_size":50,"has_more":false}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"post":{"operationId":"create-thing","summary":"Create a thing","description":"Creates a thing inside one of your lists. Target the list with `list_id` (from list_lists/search) or `list` (a list name, or \"today\"/\"tomorrow\"/an ISO date). Pick a `type` — `todo` (checkbox), `note`/`none` (plain), `dash`, `number`, or `bullet` — or omit it to use your default thing type. Add a `url` to make it a hyperlink, `tags` to categorize it, `list_ref` to link another list, or `created` to backdate it. To create many at once, send an `items` array to Bulk-create things instead.\n\nRequires the `write:things` scope.","tags":["Things"],"x-scope":"write:things","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"list_id":{"type":"string","description":"The id of the list to add the thing to. Omit if you pass `list`."},"list":{"type":"string","description":"Alternative to list_id: a list name, or \"today\"/\"yesterday\"/\"tomorrow\"/an ISO date (the day list, created if needed)."},"text":{"type":"string","description":"The thing’s text, in markdown (**bold**, *italic*, ~~strike~~, `code`, [label](url)) — the same dialect reads return, so text round-trips. An unpaired marker stays literal, and a code:true thing is never parsed."},"type":{"type":"string","enum":["todo","note","dash","number","bullet","none","photo"],"description":"Thing type: `todo` (checkbox), `note`/`none` (plain, no marker), `dash`, `number` (numbered), or `bullet`. Omit to use your default thing type."},"url":{"type":"string","description":"A hyperlink to attach to the thing."},"list_ref":{"type":"string","description":"The id of another list to reference (a navigable list link). Pass a list id, not a URL."},"tags":{"type":"array","items":{"type":"string"},"description":"Tag names (with or without a leading #)."},"photos":{"type":"array","items":{"type":"string"},"description":"Hosted image URLs to attach (must be http(s); max 10). The API does not host images — upload them elsewhere and pass the URLs."},"completed":{"type":"boolean","description":"Whether the thing starts completed."},"created":{"type":"string","description":"ISO 8601 creation time. Use it to preserve original capture-date order when importing; omit to stamp now."},"tabs":{"type":"integer","description":"Indent level: 0 = top level (default); 1+ nests this thing under the preceding one. Create the parent first, then the indented children."},"bold":{"type":"boolean","description":"Bold the whole thing."},"italic":{"type":"boolean","description":"Italicize the whole thing."},"underline":{"type":"boolean","description":"Underline the whole thing."},"header":{"type":"boolean","description":"Make the thing a heading (H1). Mutually exclusive with `subheader`; also bolds it unless you pass `bold` explicitly."},"subheader":{"type":"boolean","description":"Make the thing a subheading (H2). Mutually exclusive with `header`; also bolds it unless you pass `bold` explicitly."},"quote":{"type":"boolean","description":"Style the thing as a blockquote. Mutually exclusive with `code`."},"code":{"type":"boolean","description":"Style the thing as a code block. Mutually exclusive with `quote`."},"note":{"type":"string","description":"A long-form note attached to the thing, in **markdown**. Shown behind a note icon in the app, never inline with `text`. Pass `\"\"` to remove it. Reads return the same markdown, so a note round-trips unchanged."}}},"example":{"list_id":"64f0c2a1e4b0a1234567890a","text":"Buy oat milk","type":"todo","tags":["errands"]}}}},"responses":{"201":{"description":"Create a thing","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"thing":{"$ref":"#/components/schemas/Thing"}}},"example":{"thing":{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":false,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/things/bulk":{"post":{"operationId":"create-things","summary":"Bulk-create things","description":"Create many things in one call (one ordered insert) — use this instead of looping for imports or building a list. Send an `items` array; each item takes the same fields as Create a thing (`text`, optional `type`, `url`, `list_ref`, `tags`, `photos`, `completed`, `tabs`, `created`). Set a top-level `list_id` as the default target, or give each item its own. `skip_duplicates: true` skips items already present (by url, else text), making re-runs safe. Returns per-list counts, not the items. Max 500 per call.\n\nRequires the `write:things` scope.","tags":["Things"],"x-scope":"write:things","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"items":{"type":"array","items":{"type":"object"},"description":"The things to create (max 500). Each item: { text, list_id?, list?, type?, url?, list_ref?, tags?, photos?, completed?, tabs?, created?, and formatting: bold?, italic?, underline?, header?, subheader?, quote?, code? }."},"list_id":{"type":"string","description":"Default list for items that don’t set their own list_id."},"list":{"type":"string","description":"Alternative default target: a list name, or \"today\"/\"tomorrow\"/an ISO date."},"skip_duplicates":{"type":"boolean","description":"Skip items already in the target list (by url, else text)."}}},"example":{"list_id":"64f0c2a1e4b0a1234567890a","items":[{"text":"Oat milk","type":"todo"},{"text":"Sourdough","type":"todo"}]}}}},"responses":{"201":{"description":"Bulk-create things","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"integer"},"by_list":{"type":"array","items":{"type":"object","properties":{"list_id":{"type":"string"},"created":{"type":"integer"},"skipped":{"type":"integer"}}}}}},"example":{"created":2,"skipped":0,"by_list":[{"list_id":"64f0c2a1e4b0a1234567890a","created":2,"skipped":0}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"operationId":"update-things","summary":"Bulk-update things","description":"Apply one change set to many things at once — check them all off, move them to a list, star them. Send `ids` (the thing ids) and `set` (fields to apply to each, same shape as Update a thing). A move target (`list_id` or `list` name) is resolved once. Ids you no longer own are skipped, not errors. Max 500 ids per call.\n\nRequires the `write:things` scope.","tags":["Things"],"x-scope":"write:things","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids","set"],"properties":{"ids":{"type":"array","items":{"type":"string"},"description":"Ids of the things to change (max 500)."},"set":{"type":"object","description":"Fields to apply to every listed thing: { completed?, canceled?, favorited?, type?, tags?, tabs?, url?, list_ref?, list_id?, list?, and formatting: bold?, italic?, underline?, header?, subheader?, quote?, code? }."}}},"example":{"ids":["64f0c2a1e4b0a1234567890b"],"set":{"completed":true}}}}},"responses":{"200":{"description":"Bulk-update things","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"integer"},"skipped":{"type":"integer"}}},"example":{"updated":1,"skipped":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"delete-things","summary":"Bulk-delete things","description":"Permanently delete many things at once by id. This cannot be undone. Returns how many were deleted (unknown ids are ignored). Max 500 ids per call.\n\nRequires the `write:things` scope.","tags":["Things"],"x-scope":"write:things","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["ids"],"properties":{"ids":{"type":"array","items":{"type":"string"},"description":"Ids of the things to delete (max 500)."}}},"example":{"ids":["64f0c2a1e4b0a1234567890b"]}}}},"responses":{"200":{"description":"Bulk-delete things","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"integer"}}},"example":{"deleted":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/things/markdown":{"post":{"operationId":"append-markdown","summary":"Append markdown to a list","description":"Parse a markdown block into things and append them to a list in one call. Checkboxes (`- [ ]`/`- [x]`) become todos, `1.` numbered items, `-`/`*` bullets, `>` notes, `##` headings, `###` subheadings; leading indentation nests items. Target the list with `list_id` or `list` (name / \"today\"). `skip_duplicates: true` makes a re-run safe.\n\nRequires the `write:things` scope.","tags":["Things"],"x-scope":"write:things","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["markdown"],"properties":{"markdown":{"type":"string","description":"The markdown to parse into things."},"list_id":{"type":"string","description":"The list to append to."},"list":{"type":"string","description":"Alternative to list_id: a list name, or \"today\"/\"tomorrow\"/an ISO date."},"skip_duplicates":{"type":"boolean","description":"Skip items already present (by url, else text)."}}},"example":{"list_id":"64f0c2a1e4b0a1234567890a","markdown":"- [ ] Oat milk\n- [ ] Sourdough"}}}},"responses":{"201":{"description":"Append markdown to a list","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"integer"},"by_list":{"type":"array","items":{"type":"object","properties":{"list_id":{"type":"string"},"created":{"type":"integer"},"skipped":{"type":"integer"}}}}}},"example":{"created":2,"skipped":0,"by_list":[{"list_id":"64f0c2a1e4b0a1234567890a","created":2,"skipped":0}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/things/{id}":{"get":{"operationId":"get-thing","summary":"Get a thing","description":"Returns a single thing you own, plus its reminder (or null).\n\nRequires the `read:things` scope.","tags":["Things"],"x-scope":"read:things","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The thing id.","example":"64f0c2a1e4b0a1234567890b"}],"responses":{"200":{"description":"Get a thing","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"thing":{"$ref":"#/components/schemas/Thing"},"reminder":{"oneOf":[{"$ref":"#/components/schemas/Reminder"},{"type":"null"}]}}},"example":{"thing":{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":false,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"},"reminder":null}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"patch":{"operationId":"update-thing","summary":"Update a thing","description":"Updates a thing. Only the fields you send change. Set `completed`/`canceled` to complete or cancel a todo, `list_id` to move it to another list, or `list_ref` to reference another list. Content edits keep the thing’s position in its list — only moving it (new `list_id`) changes its order.\n\nRequires the `write:things` scope.","tags":["Things"],"x-scope":"write:things","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The thing id.","example":"64f0c2a1e4b0a1234567890b"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"text":{"type":"string","description":"New text for the thing, in markdown (same dialect as reads, so a read-modify-write keeps its formatting)."},"type":{"type":"string","enum":["todo","note","dash","number","bullet","none","photo"],"description":"Change the thing type: `todo`, `note`/`none`, `dash`, `number`, or `bullet`."},"url":{"type":"string","description":"Set or clear the hyperlink."},"list_ref":{"type":"string","description":"Set a referenced list id (a navigable list link), or pass an empty string to clear it."},"tags":{"type":"array","items":{"type":"string"},"description":"Replace the thing’s tags."},"photos":{"type":"array","items":{"type":"string"},"description":"Replace the attached image URLs (http(s); max 10). Pass [] to remove all photos."},"completed":{"type":"boolean","description":"Mark complete or incomplete."},"canceled":{"type":"boolean","description":"Cancel or un-cancel the thing."},"favorited":{"type":"boolean","description":"Star or unstar the thing."},"tabs":{"type":"integer","description":"Indent level: 0 = top level; 1+ nests under the preceding thing."},"list_id":{"type":"string","description":"Move the thing to another list you own."},"list":{"type":"string","description":"Alternative to list_id for a move: a list name, or \"today\"/\"tomorrow\"/an ISO date."},"bold":{"type":"boolean","description":"Bold the whole thing."},"italic":{"type":"boolean","description":"Italicize the whole thing."},"underline":{"type":"boolean","description":"Underline the whole thing."},"header":{"type":"boolean","description":"Make the thing a heading (H1). Mutually exclusive with `subheader`; also bolds it unless you pass `bold` explicitly."},"subheader":{"type":"boolean","description":"Make the thing a subheading (H2). Mutually exclusive with `header`; also bolds it unless you pass `bold` explicitly."},"quote":{"type":"boolean","description":"Style the thing as a blockquote. Mutually exclusive with `code`."},"code":{"type":"boolean","description":"Style the thing as a code block. Mutually exclusive with `quote`."},"note":{"type":"string","description":"A long-form note attached to the thing, in **markdown**. Shown behind a note icon in the app, never inline with `text`. Pass `\"\"` to remove it. Reads return the same markdown, so a note round-trips unchanged."}}},"example":{"completed":true}}}},"responses":{"200":{"description":"Update a thing","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"thing":{"$ref":"#/components/schemas/Thing"}}},"example":{"thing":{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":true,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"delete-thing","summary":"Delete a thing","description":"Permanently deletes a thing. Idempotent.\n\nRequires the `write:things` scope.","tags":["Things"],"x-scope":"write:things","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The thing id.","example":"64f0c2a1e4b0a1234567890b"}],"responses":{"200":{"description":"Delete a thing","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}},"example":{"success":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/things/{id}/reminder":{"put":{"operationId":"set-reminder","summary":"Set a reminder","description":"Set or replace the reminder on a thing. A thing has at most one reminder.\n\nRequires the `write:things` scope.","tags":["Reminders"],"x-scope":"write:things","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The thing id.","example":"64f0c2a1e4b0a1234567890b"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["at"],"properties":{"at":{"type":"string","description":"When to remind — ISO 8601. Include a timezone offset or Z for an exact time (e.g. 2026-06-03T17:00:00-04:00). A naive time (no offset) is interpreted in the user’s timezone."},"all_day":{"type":"boolean","description":"Treat it as an all-day reminder."},"duration_minutes":{"type":"integer","description":"How long the calendar block lasts, in minutes. 0 gives it no end time. Defaults to your \"Default reminder duration\" setting. Cannot be combined with end or all_day."},"end":{"type":"string","description":"When the block ends — ISO 8601, an alternative to duration_minutes. Must use the same form as at: both with a timezone offset, or both without."},"repeat":{"type":"string","enum":["none","daily","weekly","monthly","yearly"],"description":"Recurrence. Defaults to \"none\"."},"every":{"type":"integer","description":"Repeat every N of the chosen interval, so repeat \"monthly\" with every 3 is every 3 months. Defaults to 1. Cannot be combined with days."},"repeat_until":{"type":"string","description":"Stop repeating after this date — ISO 8601. Must be after at."},"days":{"type":"array","items":{"type":"string"},"description":"For weekly reminders, the weekdays it repeats on — names (\"monday\") or 0–6 (0=Sunday). Providing days makes it weekly."},"alert_minutes":{"type":"integer","description":"Notify this many minutes before. Defaults to your reminder settings. 0 means no advance notice."},"second_alert_minutes":{"type":"integer","description":"A second, independent notification this many minutes before — e.g. 1440 for a day before, alongside a 60-minute first alert. 0 means none."},"color":{"type":"string","description":"Hex color. Defaults to your default reminder color."}}},"example":{"at":"2026-06-03T17:00:00Z","duration_minutes":30,"repeat":"monthly","every":3,"alert_minutes":60,"second_alert_minutes":1440}}}},"responses":{"200":{"description":"Set a reminder","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"reminder":{"$ref":"#/components/schemas/Reminder"}}},"example":{"reminder":{"id":"64f0c2a1e4b0a1234567890c","thing_id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","text":"Buy oat milk","at":"2026-06-03T17:00:00.000Z","all_day":false,"repeat":"none","every":1,"repeat_until":null,"days":[],"end":"2026-06-03T18:00:00.000Z","duration_minutes":60,"alert_minutes":10,"second_alert_minutes":null,"color":"#FF9500"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}},"delete":{"operationId":"remove-reminder","summary":"Remove a reminder","description":"Remove the reminder from a thing. Idempotent.\n\nRequires the `write:things` scope.","tags":["Reminders"],"x-scope":"write:things","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The thing id.","example":"64f0c2a1e4b0a1234567890b"}],"responses":{"200":{"description":"Remove a reminder","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}},"example":{"success":true}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/reminders":{"get":{"operationId":"list-reminders","summary":"List reminders","description":"Your upcoming reminders (future first), up to 50.\n\nRequires the `read:things` scope.","tags":["Reminders"],"x-scope":"read:things","responses":{"200":{"description":"List reminders","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"reminders":{"type":"array","items":{"$ref":"#/components/schemas/Reminder"}}}},"example":{"reminders":[{"id":"64f0c2a1e4b0a1234567890c","thing_id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","text":"Buy oat milk","at":"2026-06-03T17:00:00.000Z","all_day":false,"repeat":"none","every":1,"repeat_until":null,"days":[],"end":"2026-06-03T18:00:00.000Z","duration_minutes":60,"alert_minutes":10,"second_alert_minutes":null,"color":"#FF9500"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/search":{"get":{"operationId":"search","summary":"Search","description":"Case-insensitive search across your list titles and thing text. Returns up to 50 lists + 50 things; `has_more` is true when results were capped (narrow the query). Not a complete enumeration — page `/things` to read a whole list.\n\nRequires the `search` scope.","tags":["Search"],"x-scope":"search","parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string"},"description":"The search query (the legacy alias `q` also works).","example":"milk"}],"responses":{"200":{"description":"Search","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string"},"lists":{"type":"array","items":{"$ref":"#/components/schemas/List"}},"things":{"type":"array","items":{"$ref":"#/components/schemas/Thing"}},"results":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"url":{"type":"string"}}}},"limit":{"type":"integer"},"has_more":{"type":"boolean"},"lists_has_more":{"type":"boolean"},"things_has_more":{"type":"boolean"}}},"example":{"query":"milk","lists":[],"things":[{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":false,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"}],"results":[{"id":"64f0c2a1e4b0a1234567890b","title":"Buy oat milk","url":"https://writethingsdown.com/list/5f9a1b2c3d4e5f6a7b8c9d0e"}],"limit":50,"has_more":false,"lists_has_more":false,"things_has_more":false}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/fetch":{"get":{"operationId":"fetch","summary":"Fetch a document","description":"Fetch one resource (a list or a thing) by id and return it as a single document — `{ id, title, text, url, metadata }`. For a list, `text` is its things rendered as plain text. Pairs with search (search returns ids, fetch retrieves full content). Primarily for AI deep-research clients.\n\nRequires the `read:things` scope.","tags":["Search"],"x-scope":"read:things","parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string"},"description":"A list or thing id.","example":"64f0c2a1e4b0a1234567890b"}],"responses":{"200":{"description":"Fetch a document","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"text":{"type":"string"},"url":{"type":"string"},"metadata":{"type":"object","properties":{"kind":{"type":"string"},"list_id":{"type":"string"},"type":{"type":"string"},"tags":{"type":"array","items":{}}}}}},"example":{"id":"64f0c2a1e4b0a1234567890b","title":"Buy oat milk","text":"Buy oat milk","url":"https://writethingsdown.com/list/5f9a1b2c3d4e5f6a7b8c9d0e","metadata":{"kind":"thing","list_id":"5f9a1b2c3d4e5f6a7b8c9d0e","type":"todo","tags":[]}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/tags":{"get":{"operationId":"list-tags","summary":"List tags","description":"The tags you use across your things, with usage counts.\n\nRequires the `read:things` scope.","tags":["Tags"],"x-scope":"read:things","responses":{"200":{"description":"List tags","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"count":{"type":"integer"},"color":{"type":"string"}}}}}},"example":{"tags":[{"name":"errands","count":12,"color":"#66cc99"}]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}}},"components":{"responses":{"BadRequest":{"description":"Bad Request. A required field or parameter is missing or malformed.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"A required field or parameter is missing or malformed."}}}},"Unauthorized":{"description":"Unauthorized. The API key is missing, invalid, or has been revoked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"The API key is missing, invalid, or has been revoked."}}}},"Forbidden":{"description":"Forbidden. The key is valid but lacks the scope required for this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"The key is valid but lacks the scope required for this endpoint."}}}},"NotFound":{"description":"Not Found. The referenced list or thing does not exist or is not owned by you.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"The referenced list or thing does not exist or is not owned by you."}}}},"TooManyRequests":{"description":"Too Many Requests. The rate limit of 1000 requests per hour has been exceeded.","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Requests allowed per hour for this key (1000)."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix time (seconds) when the window resets."},"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying. Honor this rather than retrying immediately."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"The rate limit of 1000 requests per hour has been exceeded."}}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"twos_<keyId>_<secret>","description":"A NewTwos API key. The person you are acting for creates one at https://writethingsdown.com/settings/api-keys, where it is shown once. Ask them for it rather than guessing, and never send it to another host."}},"schemas":{"List":{"type":"object","description":"A list.","properties":{"id":{"type":"string"},"title":{"type":"string"},"emoji":{"type":"string"},"today":{"type":"boolean"},"favorited":{"type":"boolean"},"archived":{"type":"boolean"},"sort":{"type":"string","examples":["chronological","reverse-chronological","alphabetical","reverse-alphabetical","modified"],"description":"How things are ordered in the app. Usually `chronological` (oldest first, the default), `reverse-chronological`, `alphabetical`, `reverse-alphabetical`, or `modified`. Treat an unrecognized value as `chronological`."},"created":{"type":"string","format":"date-time"},"updated":{"type":"string","format":"date-time"}},"example":{"id":"64f0c2a1e4b0a1234567890a","title":"Groceries","emoji":"🛒","today":false,"favorited":false,"archived":false,"sort":"chronological","created":"2026-06-01T12:00:00.000Z","updated":"2026-06-02T08:30:00.000Z"}},"Thing":{"type":"object","description":"A thing (a todo or a note).","properties":{"id":{"type":"string"},"list_id":{"type":"string"},"list_ref":{"type":["string","null"],"description":"If this thing links to another list (a sub-list reference), that list’s id; else null."},"text":{"type":"string"},"type":{"type":"string","enum":["todo","dash","number","bullet","none","photo"],"description":"The thing’s style. `todo` is a checkbox, `none` is a plain line. Writes also accept `note` as an alias for `none`, but a read always returns `none`, so do not expect a write of `note` to read back unchanged."},"url":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"tabs":{"type":"integer","description":"Indent level (0 = top level)."},"photos":{"type":"array","items":{"type":"string"},"description":"Hosted image URLs attached to this thing."},"completed":{"type":"boolean"},"canceled":{"type":"boolean"},"favorited":{"type":"boolean"},"clone_of":{"type":["string","null"],"description":"If this thing is a clone (a live linked copy), the id of the origin thing; else null."},"has_clones":{"type":"boolean","description":"True if this thing is an original that has one or more clones."},"bold":{"type":"boolean"},"italic":{"type":"boolean"},"underline":{"type":"boolean"},"header":{"type":"boolean","description":"Whole-thing heading (H1). Mutually exclusive with subheader."},"subheader":{"type":"boolean","description":"Whole-thing subheading (H2). Mutually exclusive with header."},"quote":{"type":"boolean","description":"Whole-thing blockquote. Mutually exclusive with code."},"code":{"type":"boolean","description":"Whole-thing code block. Mutually exclusive with quote."},"note":{"type":"string","description":"Long-form note attached to the thing, in markdown. Shown behind a note icon in the app, never inline with `text`. Empty string when there is no note."},"created":{"type":"string","format":"date-time"},"updated":{"type":"string","format":"date-time"}},"example":{"id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","list_ref":null,"text":"Buy oat milk","type":"todo","url":"","tags":["errands"],"tabs":0,"photos":[],"completed":false,"canceled":false,"favorited":false,"clone_of":null,"has_clones":false,"bold":false,"italic":false,"underline":false,"header":false,"subheader":false,"quote":false,"code":false,"note":"Prefer the **barista** blend — the oat one at the corner shop.","created":"2026-06-02T08:30:00.000Z","updated":"2026-06-02T08:30:00.000Z"}},"Reminder":{"type":"object","description":"A reminder on a thing.","properties":{"id":{"type":"string"},"thing_id":{"type":"string"},"list_id":{"type":"string"},"text":{"type":"string"},"at":{"type":"string","format":"date-time","description":"When it fires (ISO 8601)."},"all_day":{"type":"boolean"},"repeat":{"type":"string","enum":["none","daily","weekly","monthly","yearly","custom"]},"every":{"type":"integer","description":"Repeat interval, in units of `repeat`. 2 with `weekly` means every other week."},"repeat_until":{"type":["string","null"],"format":"date-time","description":"When the repeat stops. Null repeats forever. Not the same as `end`."},"days":{"type":"array","items":{"type":"string"},"description":"Weekdays it repeats on, when repeat is weekly."},"end":{"type":["string","null"],"format":"date-time","description":"When this single occurrence ends, for a reminder that occupies a span of time."},"duration_minutes":{"type":["integer","null"],"description":"Length of the occurrence in minutes. The same span as `end`, expressed as a duration. Writes set one or the other, never both."},"alert_minutes":{"type":["integer","null"],"description":"Minutes before `at` to notify. 0 or null means no advance notice."},"second_alert_minutes":{"type":["integer","null"],"description":"An optional second notification, also in minutes before `at`."},"color":{"type":"string","description":"Hex color. Empty when the reminder has no color of its own."}},"example":{"id":"64f0c2a1e4b0a1234567890c","thing_id":"64f0c2a1e4b0a1234567890b","list_id":"64f0c2a1e4b0a1234567890a","text":"Buy oat milk","at":"2026-06-03T17:00:00.000Z","all_day":false,"repeat":"none","every":1,"repeat_until":null,"days":[],"end":"2026-06-03T18:00:00.000Z","duration_minutes":60,"alert_minutes":10,"second_alert_minutes":null,"color":"#FF9500"}},"Error":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}