---
title: For AI assistants
description: A structured reference for a coding agent integrating the FlickList API.
section: Guides
order: 4
---

# For AI assistants

This page is a brief for an AI coding agent wiring up the FlickList API. It's structured for fast scanning, not a narrative walkthrough.

## Machine-readable surfaces

- `/dev/llms.txt` indexes every page on this wiki, one line each. Use it to decide what to fetch instead of crawling the whole site.
- Every page under `/dev` has a raw Markdown twin at the same URL with `.md` appended. Fetch `/dev/catalog.md` instead of parsing rendered HTML.
- The full OpenAPI 3.1 spec lives at `/openapi/v3.yaml`. Generate a client from it directly instead of reconstructing request shapes from prose.

## Authentication, in five lines

- Two credential types exist: a bearer token from the device-code flow, and an API key.
- An API key works as `Authorization: Bearer <key>` or as an `api_key` query parameter.
- API keys carry `read` and/or `write` scopes. Every `/sync/*` GET needs `read`; sync mutations and every `/scrobble/*` call need `write`.
- Catalog reads need a credential of either kind, with no additional scope requirement.
- A handful of endpoints work with no credential at all: community lists, list search, list tags, a user's public lists, a public list's own items, and the global calendar (`GET /calendar/shows/{start}/{days}`). The personal calendar needs `read`.

| Surface | Credential | Scope |
| --- | --- | --- |
| Catalog reads | required | none |
| `/sync/*` reads | required | `read` |
| `/sync/*` writes, `/scrobble/*` | required | `write` |
| Public list reads | optional | none |

## Contract subtleties you must not violate

- `ids.fldb` is present on every object and never changes. Treat it as the stable join key, not `tmdb` or `imdb`.

  ```json
  { "ids": { "fldb": "flm_c61f0a84", "tmdb": 550, "imdb": "tt0137523" } }
  ```

- The `ids` object gains new keys over time. Ignore any key you don't recognize instead of rejecting the response or failing validation on it.
- `media_type: "tv"` and `media_type: "show"` are the same value on write requests. Accept both; don't normalize one away or reject either.
- When writing multiple identifiers for one item, resolution precedence is `fldb` > `tmdb` > `imdb` > `tvdb`. Send whichever ones you have; the API doesn't require a single identifier.
- A `not_found` array in a batch response lists items that didn't resolve. It is not an error, and the request still returns `200`.

  ```json
  { "not_found": [{ "ids": { "imdb": "tt9999999" } }] }
  ```

- The API is additive-only. New fields appear on existing objects without a version bump, and existing fields are never renamed or removed. Parse defensively and don't assume today's field list is final.
- On a `429`, read the `Retry-After` header and wait that many seconds before retrying. Don't retry immediately, and don't invent your own backoff schedule.
- There's no separate heartbeat call for scrobbling. Re-POST `scrobble/start` with updated progress to report ongoing playback. It's an upsert, not a new session each time, and it's also how you resolve a stale session from an earlier one.
- `scrobble/stop` auto-marks an item watched at 90% progress or more, provided the session also cleared a minimum watch-time floor. Don't wait for 100% before treating a session as finished.
- A scrobble target without a TMDB-linked catalog item returns `400`. Resolve against the catalog before you send `scrobble/start`.
- Unmarking watched history hides the event. It never deletes underlying account data, so don't build irreversible-delete assumptions on top of it.
- When adding history, always send an explicit `watched_at` and reuse the same value on retries. History writes are idempotent per item and timestamp (retried duplicates land in the response's `existing` count); omitting `watched_at` makes a retried request record a second play.
- Error responses are always `{ "error": "..." }`. Check the status code first; don't pattern-match on the message string, since its wording isn't part of the contract.
- Rate limiting is 1,000 requests per hour per credential. Poll `GET /sync/last_activities` before re-fetching a collection instead of re-fetching it on a fixed timer.

## When the spec and this page disagree

Treat `/openapi/v3.yaml` as the source of truth for exact request and response shapes. This page exists to flag behavior that trips up integrations even after reading the spec, not to restate every field in it.
