> ## Documentation Index
> Fetch the complete documentation index at: https://api.simkl.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Add to Watchlist

> Move an item into one of the user's **Watchlist** statuses. The body's per-item `to` field selects the destination:

<Note>
**Most apps prefer [`POST /sync/history`](/api-reference/simkl/add-to-history) for sync flows.** Use this endpoint (`/sync/add-to-list`) only when your app has explicit "Add to Plan to Watch" / "Move to Hold" UI buttons — i.e. setting watchlist status without recording a watch event. For backfill from another tracker, scrobbling, or marking-watched UI, send to `/sync/history` instead (which carries `status`, `rating`, `memo`, AND the watch event in one shape).

**Don't chain `/sync/history` then `/sync/add-to-list` — the history call already moves the item.** `POST /sync/history` automatically places (and re-classifies) the item on the right Watchlist based on the watch event. Read `added.statuses[].response.status` in the history response to see the resolved status — e.g. a `"completed"` write on a still-airing show is silently downgraded to `"watching"` and reflected there. A follow-up `/sync/add-to-list` call is redundant and may overwrite the server's smarter decision.

See the [Sync guide](/guides/sync) for the two-phase pull/delta pattern.
</Note>

<Warning>
**This endpoint does not save memos.** If you include a `memo` field per item, the request **succeeds** and the memo is **echoed back** in the response — but the value is not persisted; reading the item with `?memos=yes` afterwards returns `memo: {}`. To set or update a memo, send the item to [`POST /sync/history`](/api-reference/simkl/add-to-history#memo-only-updates-and-add-to-watchlist-memo-in-one-call) with `ids` + `status` + `memo`. `/sync/history` also auto-adds the item to the watchlist if it wasn't there yet, so a single call covers both "add the item" and "set the memo".
</Warning>

| `to` value | Destination |
|---|---|
| `watching` | Currently watching. (For movies, automatically becomes `completed` since movies are atomic.) |
| `plantowatch` | Plan to watch. |
| `hold` | On hold. |
| `dropped` | Dropped. |
| `completed` | Completed. |

`to` is **per-item** (each entry in the array carries its own destination). The legacy top-level `to` shape is **not accepted** — the server returns `400 empty_field` (`Missed "to" parameter`) when `to` only appears at the top level.

<Tip>
**You don't need a Simkl ID.** The server resolves any combination of identifiers via its internal Search — pass whatever IDs your app already has and skip the `/redirect` lookup step. Identifier slots accepted on each item:

| Slot | Notes |
|---|---|
| `ids.simkl` | Simkl internal ID. Always wins when present. |
| `ids.imdb` | IMDb ID (e.g. `tt1375666`). Works for movies and shows. |
| `ids.tmdb` | TMDB ID. Works for movies and shows. |
| `ids.tvdb` | TVDB ID. Most-used canonical for TV in media-server stacks (Plex, Sonarr, Jellyfin, Kodi). |
| `ids.mal` / `ids.anidb` / `ids.anilist` / `ids.kitsu` | Anime catalogs. Send any/all you have. |
| `ids.slug` | URL slug — useful when you only have a Simkl-shaped link. |
| `title` + `year` (no `ids` at all) | Text fallback. Fuzzy match; ambiguous titles may miss — inspect `not_found` in the response. |

**Send everything you have.** The server picks the first identifier that resolves and accepts the extras. This is the canonical shape for migrations from another tracker (Trakt → Simkl, IMDb-list import, Letterboxd export, etc.) — just forward whatever the source carried.
</Tip>

```json
{
  "movies": [
    {
      "to": "completed",
      "title": "Inception",
      "year": 2010,
      "ids": {
        "simkl": 472214,
        "imdb": "tt1375666",
        "tmdb": "27205"
      }
    }
  ],
  "shows": [
    {
      "to": "watching",
      "title": "Game of Thrones",
      "year": 2011,
      "ids": {
        "simkl": 17465,
        "slug": "game-of-thrones",
        "imdb": "tt0944947",
        "tmdb": "1399",
        "tvdb": "121361"
      }
    }
  ]
}
```

Optional per-item fields: `watched_at`, `added_at`.

<Warning>
**To remove an item, use [`POST /sync/history/remove`](/api-reference/simkl/remove-from-history).** This endpoint operates on the Watchlist (the five statuses above); the canonical un-track / delete-from-list path is `/sync/history/remove`, which writes to the same backing store and returns the same kind of result envelope. A legacy `to: "remove"` value is accepted by this endpoint for backwards compatibility, but it is **undocumented** and should not be used in new integrations — Simkl reserves the right to change its behavior without notice.
</Warning>

#### Response: `added` and `not_found`

The response always returns 201 (even on partial failures) with two arrays per media-type:

```json
{
 "added": {
 "movies": [{ "to": "completed", "ids": {...}, "type": "movie" }],
 "shows": [{ "to": "watching", "ids": {...}, "type": "show" }]
 },
 "not_found": {
 "movies": [{ "title": "Definitely Not A Real Movie", "year": 9999 }],
 "shows": []
 }
}
```

Items the server's resolver matched land in `added`. Items it couldn't match (typos, fuzzy title misses, IDs not in Simkl's catalog yet) land in `not_found` — verbatim copies of the input so you can show "we couldn't add: …" in your UI. **Always inspect both arrays after a bulk call.**

Errors: `400 empty_field` if `to` is missing on an item; `400 wrong_parameter` if `to` is not one of the values above.

#### Silent `to` rewrites

The server may downgrade your requested `to` value when an item isn't in a state where that status applies:

- **Movies** with `to: "watching"` → silently rewritten to `completed` (movies are atomic; you can't "be watching" a movie).
- **Shows** that aren't ready for `completed` (still airing, or pre-release) get rewritten to `watching` or `plantowatch` respectively, depending on whether any episode has aired.

The rewrites happen server-side; the consumer just sees the actual stored value in `added.<type>[i].to`. There is **no error code** surfaced for the rewrite — the only way to detect it is to compare the value you sent against the value that came back.

> Note: this endpoint operates on the Simkl **Watchlist** (the five canonical statuses above). Custom user-created lists will get their own API in a future release.

<Card title="Sync guide — full walkthrough" icon="arrows-rotate" href="/guides/sync" horizontal>
 Two-phase model (initial pull → activities-checked delta loop), `date_from` semantics, deletion reconciliation, edge cases, and reference implementations in Node and Python.
</Card>

**Anime titles:** can go in either the `anime[]` array OR the `shows[]` array — both are accepted. The server normalizes anime into the response's `shows` array with `"type": "show"` per-item, since anime are TV-like in the cross-catalog data model. AniDB-specific IDs (`mal`, `anidb`, `anilist`, `kitsu`) belong inside each item's `ids` object regardless of which array it lives in. See [Anime under shows[]](/conventions/standard-media-objects#anime).

<Tip>
**Which IDs can I send/expect?** All accepted input identifiers and the keys you'll see echoed back in responses are listed at [**Standard media objects → Supported ID keys**](/conventions/standard-media-objects#supported-id-keys). Send every ID you have on writes — Simkl picks the first that resolves and ignores the rest. Reminder: `slug` is **response-only** (never send it on a request).
</Tip>



## OpenAPI

````yaml /openapi.json post /sync/add-to-list
openapi: 3.1.0
info:
  title: Simkl API
  version: 1.0.0
  description: >-
    The **Simkl API** lets you build apps that track Movies, TV Shows, and Anime
    — scrobble playback, sync watch history, and pull rich metadata.


    All requests use HTTPS and return JSON. The base URL is
    `https://api.simkl.com`.


    **Every request needs three URL parameters and a `User-Agent` header:**


    ```

    /endpoint?client_id=YOUR_CLIENT_ID&app-name=my-app&app-version=1.0

    ```


    See [Headers and required parameters](/conventions/headers) for the full
    reference. Endpoints that read or modify a user's data also need an
    `Authorization: Bearer <token>` header — see [OAuth
    2.0](/api-reference/oauth) or the [PIN flow](/api-reference/pin).


    > ⚠️ **About the auto-rendered cURL examples on each endpoint page:** they
    omit `?client_id=…&app-name=…&app-version=…` from the URL for brevity. **You
    must add them yourself when copy-pasting**, or use the interactive **"Try
    it"** playground (it fills them in for you). This is a Mintlify rendering
    convention — every Mintlify-built API doc behaves the same way.


    **Get started**


    - [Quickstart](/quickstart) — first request in 60 seconds

    - [API rules](/api-rules) — what's allowed, what isn't

    - [Standard media objects](/conventions/standard-media-objects) — the shapes
    every endpoint speaks

    - [Errors](/conventions/errors) — every status code Simkl returns
  contact:
    name: Simkl Developer Support
    url: https://support.simkl.com
  termsOfService: https://simkl.com/about/policies/terms/
  license:
    name: Simkl API Terms of Use
    url: https://simkl.com/about/policies/terms/
  x-logo:
    url: https://i.simkl.com/img_tv/apiary_logo_api.png
    backgroundColor: '#0E5DAB'
    altText: Simkl
servers:
  - url: https://api.simkl.com
    description: Production API
  - url: https://data.simkl.in
    description: CDN for static, public data files (Calendar + Trending). No auth required.
security:
  - clientId: []
  - simklApiKey: []
tags:
  - name: OAuth 2.0
    description: >-
      OAuth 2.0 authorization-code flow for web and mobile apps. The user is
      redirected to Simkl, signs in, approves your app, and is redirected back
      with a `code` you exchange for an `access_token`. Long-lived tokens — no
      refresh-token flow needed.
    externalDocs:
      description: OAuth 2.0 walkthrough
      url: https://api.simkl.org/api-reference/oauth
  - name: PIN
    description: >-
      Device flow for TVs, consoles, smart watches, CLI tools — anywhere typing
      a URL is hard. Show a 5-character code; user enters it on simkl.com/pin;
      you poll for the access token. **No `client_secret` required.**
    externalDocs:
      description: PIN flow walkthrough
      url: https://api.simkl.org/api-reference/pin
  - name: Redirect
    description: >-
      Helper redirects for one-click "mark as watched", trailers, sharing, and
      more.
  - name: Search
    description: Find shows, movies, and anime by ID, text query, file name, or randomly.
    externalDocs:
      description: Search guide
      url: https://api.simkl.org/guides/search
  - name: Movies
    description: >-
      Browse the Simkl movies catalog: details, premieres, top-of-best, and
      genre filters.
  - name: TV
    description: >-
      Browse the Simkl TV catalog: details, episodes, what's airing, premieres,
      top-of-best, and genre filters.
  - name: Anime
    description: >-
      Browse the Simkl anime catalog: details, episodes, what's airing,
      premieres, top-of-best, and genre filters.
  - name: Ratings
    description: >-
      Read Simkl's average community rating, rank, drop rate, and external
      ratings (IMDB, MAL) for any item, or for every item in a user's lists.
  - name: Scrobble
    description: >-
      Report real-time playback to Simkl with `start`, `pause`, and `stop`. At ≥
      80 % progress Simkl marks the item as watched automatically; below 80 %
      the session is saved as a paused playback the user can resume from any
      device.


      See [Standard media objects](/conventions/standard-media-objects) for the
      supported ID keys.


      <!-- IDS_TABLE_START -->


      ### Supported ID keys


      Inside any `ids` object, pass as many of these as you have. Simkl resolves
      to the canonical record.


      | ID key | Type | Example |

      |---|---|---|

      | `simkl` | integer | `49108`. Simkl's canonical ID. Most reliable. |

      | `imdb` | string | `tt1520211` |

      | `tmdb` | integer | `76757` (for TV, specify `type`) |

      | `tvdb` | int / string | `153021` or `the-walking-dead` |

      | `mal` | integer | `4246` (MyAnimeList) |

      | `anidb` | integer | `10846`. Specifying just this is enough for anime
      lookups. |

      | `anilist` | integer | `21` |

      | `kitsu` | integer | `12` |

      | `anisearch` | integer | `2227` |

      | `animeplanet` | string | `one-piece` |

      | `livechart` | integer | `321` |

      | `letterboxd` | string | `the-truman-show` |

      | `netflix` | integer | `70210890` (movie ID) |

      | `traktslug` | string | `john-wick-chapter-4-2023` |


      > 📖 Full reference: [Standard media
      objects](/conventions/standard-media-objects)


      <!-- IDS_TABLE_END -->
    externalDocs:
      description: Scrobble guide
      url: https://api.simkl.org/guides/scrobble
  - name: Sync
    description: >-
      Use Simkl as a cloud backup for the user's watch history and lists
      (Watching, Plan to Watch, On Hold, Dropped, Completed). **Always check
      [`/sync/activities`](/api-reference/simkl/get-activities) first** and sync
      only the lists that have moved.


      All write endpoints accept arrays — batch aggressively. See [Standard
      media objects](/conventions/standard-media-objects) for the supported ID
      keys.


      <!-- IDS_TABLE_START -->


      ### Supported ID keys


      Inside any `ids` object, pass as many of these as you have. Simkl resolves
      to the canonical record.


      | ID key | Type | Example |

      |---|---|---|

      | `simkl` | integer | `49108`. Simkl's canonical ID. Most reliable. |

      | `imdb` | string | `tt1520211` |

      | `tmdb` | integer | `76757` (for TV, specify `type`) |

      | `tvdb` | int / string | `153021` or `the-walking-dead` |

      | `mal` | integer | `4246` (MyAnimeList) |

      | `anidb` | integer | `10846`. Specifying just this is enough for anime
      lookups. |

      | `anilist` | integer | `21` |

      | `kitsu` | integer | `12` |

      | `anisearch` | integer | `2227` |

      | `animeplanet` | string | `one-piece` |

      | `livechart` | integer | `321` |

      | `letterboxd` | string | `the-truman-show` |

      | `netflix` | integer | `70210890` (movie ID) |

      | `traktslug` | string | `john-wick-chapter-4-2023` |


      > 📖 Full reference: [Standard media
      objects](/conventions/standard-media-objects)


      <!-- IDS_TABLE_END -->
    externalDocs:
      description: Sync guide
      url: https://api.simkl.org/guides/sync
  - name: Users
    description: User profile, settings, and watch statistics.
externalDocs:
  description: Simkl API documentation
  url: https://api.simkl.org
paths:
  /sync/add-to-list:
    post:
      tags:
        - Sync
      summary: Add to Watchlist
      description: >-
        Move an item into one of the user's **Watchlist** statuses. The body's
        per-item `to` field selects the destination:


        <Note>

        **Most apps prefer [`POST
        /sync/history`](/api-reference/simkl/add-to-history) for sync flows.**
        Use this endpoint (`/sync/add-to-list`) only when your app has explicit
        "Add to Plan to Watch" / "Move to Hold" UI buttons — i.e. setting
        watchlist status without recording a watch event. For backfill from
        another tracker, scrobbling, or marking-watched UI, send to
        `/sync/history` instead (which carries `status`, `rating`, `memo`, AND
        the watch event in one shape).


        **Don't chain `/sync/history` then `/sync/add-to-list` — the history
        call already moves the item.** `POST /sync/history` automatically places
        (and re-classifies) the item on the right Watchlist based on the watch
        event. Read `added.statuses[].response.status` in the history response
        to see the resolved status — e.g. a `"completed"` write on a
        still-airing show is silently downgraded to `"watching"` and reflected
        there. A follow-up `/sync/add-to-list` call is redundant and may
        overwrite the server's smarter decision.


        See the [Sync guide](/guides/sync) for the two-phase pull/delta pattern.

        </Note>


        <Warning>

        **This endpoint does not save memos.** If you include a `memo` field per
        item, the request **succeeds** and the memo is **echoed back** in the
        response — but the value is not persisted; reading the item with
        `?memos=yes` afterwards returns `memo: {}`. To set or update a memo,
        send the item to [`POST
        /sync/history`](/api-reference/simkl/add-to-history#memo-only-updates-and-add-to-watchlist-memo-in-one-call)
        with `ids` + `status` + `memo`. `/sync/history` also auto-adds the item
        to the watchlist if it wasn't there yet, so a single call covers both
        "add the item" and "set the memo".

        </Warning>


        | `to` value | Destination |

        |---|---|

        | `watching` | Currently watching. (For movies, automatically becomes
        `completed` since movies are atomic.) |

        | `plantowatch` | Plan to watch. |

        | `hold` | On hold. |

        | `dropped` | Dropped. |

        | `completed` | Completed. |


        `to` is **per-item** (each entry in the array carries its own
        destination). The legacy top-level `to` shape is **not accepted** — the
        server returns `400 empty_field` (`Missed "to" parameter`) when `to`
        only appears at the top level.


        <Tip>

        **You don't need a Simkl ID.** The server resolves any combination of
        identifiers via its internal Search — pass whatever IDs your app already
        has and skip the `/redirect` lookup step. Identifier slots accepted on
        each item:


        | Slot | Notes |

        |---|---|

        | `ids.simkl` | Simkl internal ID. Always wins when present. |

        | `ids.imdb` | IMDb ID (e.g. `tt1375666`). Works for movies and shows. |

        | `ids.tmdb` | TMDB ID. Works for movies and shows. |

        | `ids.tvdb` | TVDB ID. Most-used canonical for TV in media-server
        stacks (Plex, Sonarr, Jellyfin, Kodi). |

        | `ids.mal` / `ids.anidb` / `ids.anilist` / `ids.kitsu` | Anime
        catalogs. Send any/all you have. |

        | `ids.slug` | URL slug — useful when you only have a Simkl-shaped link.
        |

        | `title` + `year` (no `ids` at all) | Text fallback. Fuzzy match;
        ambiguous titles may miss — inspect `not_found` in the response. |


        **Send everything you have.** The server picks the first identifier that
        resolves and accepts the extras. This is the canonical shape for
        migrations from another tracker (Trakt → Simkl, IMDb-list import,
        Letterboxd export, etc.) — just forward whatever the source carried.

        </Tip>


        ```json

        {
          "movies": [
            {
              "to": "completed",
              "title": "Inception",
              "year": 2010,
              "ids": {
                "simkl": 472214,
                "imdb": "tt1375666",
                "tmdb": "27205"
              }
            }
          ],
          "shows": [
            {
              "to": "watching",
              "title": "Game of Thrones",
              "year": 2011,
              "ids": {
                "simkl": 17465,
                "slug": "game-of-thrones",
                "imdb": "tt0944947",
                "tmdb": "1399",
                "tvdb": "121361"
              }
            }
          ]
        }

        ```


        Optional per-item fields: `watched_at`, `added_at`.


        <Warning>

        **To remove an item, use [`POST
        /sync/history/remove`](/api-reference/simkl/remove-from-history).** This
        endpoint operates on the Watchlist (the five statuses above); the
        canonical un-track / delete-from-list path is `/sync/history/remove`,
        which writes to the same backing store and returns the same kind of
        result envelope. A legacy `to: "remove"` value is accepted by this
        endpoint for backwards compatibility, but it is **undocumented** and
        should not be used in new integrations — Simkl reserves the right to
        change its behavior without notice.

        </Warning>


        #### Response: `added` and `not_found`


        The response always returns 201 (even on partial failures) with two
        arrays per media-type:


        ```json

        {
         "added": {
         "movies": [{ "to": "completed", "ids": {...}, "type": "movie" }],
         "shows": [{ "to": "watching", "ids": {...}, "type": "show" }]
         },
         "not_found": {
         "movies": [{ "title": "Definitely Not A Real Movie", "year": 9999 }],
         "shows": []
         }
        }

        ```


        Items the server's resolver matched land in `added`. Items it couldn't
        match (typos, fuzzy title misses, IDs not in Simkl's catalog yet) land
        in `not_found` — verbatim copies of the input so you can show "we
        couldn't add: …" in your UI. **Always inspect both arrays after a bulk
        call.**


        Errors: `400 empty_field` if `to` is missing on an item; `400
        wrong_parameter` if `to` is not one of the values above.


        #### Silent `to` rewrites


        The server may downgrade your requested `to` value when an item isn't in
        a state where that status applies:


        - **Movies** with `to: "watching"` → silently rewritten to `completed`
        (movies are atomic; you can't "be watching" a movie).

        - **Shows** that aren't ready for `completed` (still airing, or
        pre-release) get rewritten to `watching` or `plantowatch` respectively,
        depending on whether any episode has aired.


        The rewrites happen server-side; the consumer just sees the actual
        stored value in `added.<type>[i].to`. There is **no error code**
        surfaced for the rewrite — the only way to detect it is to compare the
        value you sent against the value that came back.


        > Note: this endpoint operates on the Simkl **Watchlist** (the five
        canonical statuses above). Custom user-created lists will get their own
        API in a future release.


        <Card title="Sync guide — full walkthrough" icon="arrows-rotate"
        href="/guides/sync" horizontal>
         Two-phase model (initial pull → activities-checked delta loop), `date_from` semantics, deletion reconciliation, edge cases, and reference implementations in Node and Python.
        </Card>


        **Anime titles:** can go in either the `anime[]` array OR the `shows[]`
        array — both are accepted. The server normalizes anime into the
        response's `shows` array with `"type": "show"` per-item, since anime are
        TV-like in the cross-catalog data model. AniDB-specific IDs (`mal`,
        `anidb`, `anilist`, `kitsu`) belong inside each item's `ids` object
        regardless of which array it lives in. See [Anime under
        shows[]](/conventions/standard-media-objects#anime).


        <Tip>

        **Which IDs can I send/expect?** All accepted input identifiers and the
        keys you'll see echoed back in responses are listed at [**Standard media
        objects → Supported ID
        keys**](/conventions/standard-media-objects#supported-id-keys). Send
        every ID you have on writes — Simkl picks the first that resolves and
        ignores the rest. Reminder: `slug` is **response-only** (never send it
        on a request).

        </Tip>
      operationId: post-sync-add-to-list
      parameters:
        - $ref: '#/components/parameters/ClientIdQuery'
        - $ref: '#/components/parameters/AppNameQuery'
        - $ref: '#/components/parameters/AppVersionQuery'
        - $ref: '#/components/parameters/UserAgentHeader'
      requestBody:
        content:
          application/json:
            example:
              movies:
                - title: 'Terminator 3: Rise of the Machines'
                  to: completed
                  year: '2003'
                  added_at: '2014-09-01T09:10:11.000Z'
                  watched_at: '2014-10-10T22:10:00.000Z'
                  ids:
                    imdb: tt0181852
                    tmdb: 296
                    simkl: 53536
                - to: plantowatch
                  ids:
                    simkl: 210728
              shows:
                - to: watching
                  title: Attack on Titan
                  year: 2013
                  ids:
                    simkl: 39687
                    mal: 16498
                    tvdb: 267440
                    imdb: tt2560140
                    anidb: 9541
                - title: The Walking Dead
                  year: 2010
                  to: completed
                  ids:
                    simkl: 2090
                    tvdb: 153021
                    imdb: tt1520211
            schema:
              $ref: '#/components/schemas/AddToListRequest'
            examples:
              add_anime_completed:
                summary: Add an anime to `completed`
                description: >-
                  Anime use the `anime` array key in the REQUEST body. (In
                  responses, anime entries are wrapped in `show:` for
                  cross-catalog compatibility — see /conventions/null-values.)
                value:
                  anime:
                    - to: completed
                      ids:
                        simkl: 831411
                        mal: 38000
                        anidb: 14116
                        anilist: 101922
              add_anime_dropped:
                summary: Add an anime to `dropped`
                description: >-
                  Anime use the `anime` array key in the REQUEST body. (In
                  responses, anime entries are wrapped in `show:` for
                  cross-catalog compatibility — see /conventions/null-values.)
                value:
                  anime:
                    - to: dropped
                      ids:
                        simkl: 831411
                        mal: 38000
                        anidb: 14116
                        anilist: 101922
              add_anime_hold:
                summary: Add an anime to `hold`
                description: >-
                  Anime use the `anime` array key in the REQUEST body. (In
                  responses, anime entries are wrapped in `show:` for
                  cross-catalog compatibility — see /conventions/null-values.)
                value:
                  anime:
                    - to: hold
                      ids:
                        simkl: 831411
                        mal: 38000
                        anidb: 14116
                        anilist: 101922
              add_anime_plantowatch:
                summary: Add an anime to `plantowatch`
                description: >-
                  Anime use the `anime` array key in the REQUEST body. (In
                  responses, anime entries are wrapped in `show:` for
                  cross-catalog compatibility — see /conventions/null-values.)
                value:
                  anime:
                    - to: plantowatch
                      ids:
                        simkl: 831411
                        mal: 38000
                        anidb: 14116
                        anilist: 101922
              add_anime_watching:
                summary: Add an anime to `watching`
                description: >-
                  Anime use the `anime` array key in the REQUEST body. (In
                  responses, anime entries are wrapped in `show:` for
                  cross-catalog compatibility — see /conventions/null-values.)
                value:
                  anime:
                    - to: watching
                      ids:
                        simkl: 831411
                        mal: 38000
                        anidb: 14116
                        anilist: 101922
              add_movie_completed_per_item:
                summary: Add a single movie to `completed` (per-item `to`)
                description: >-
                  Canonical body shape: media-type array of items, each with its
                  own `to` field.
                value:
                  movies:
                    - to: completed
                      ids:
                        simkl: 752138
              add_movie_dropped_per_item:
                summary: Add a single movie to `dropped` (per-item `to`)
                description: >-
                  Canonical body shape: media-type array of items, each with its
                  own `to` field.
                value:
                  movies:
                    - to: dropped
                      ids:
                        simkl: 752138
              add_movie_plantowatch_per_item:
                summary: Add a single movie to `plantowatch` (per-item `to`)
                description: >-
                  Canonical body shape: media-type array of items, each with its
                  own `to` field.
                value:
                  movies:
                    - to: plantowatch
                      ids:
                        simkl: 752138
              add_show_completed:
                summary: Add a TV show to `completed`
                description: >-
                  TV shows accept all 5 watchlist statuses (movies are
                  restricted to plantowatch/completed/dropped).
                value:
                  shows:
                    - to: completed
                      ids:
                        simkl: 2090
              add_show_dropped:
                summary: Add a TV show to `dropped`
                description: >-
                  TV shows accept all 5 watchlist statuses (movies are
                  restricted to plantowatch/completed/dropped).
                value:
                  shows:
                    - to: dropped
                      ids:
                        simkl: 2090
              add_show_hold:
                summary: Add a TV show to `hold`
                description: >-
                  TV shows accept all 5 watchlist statuses (movies are
                  restricted to plantowatch/completed/dropped).
                value:
                  shows:
                    - to: hold
                      ids:
                        simkl: 2090
              add_show_plantowatch:
                summary: Add a TV show to `plantowatch`
                description: >-
                  TV shows accept all 5 watchlist statuses (movies are
                  restricted to plantowatch/completed/dropped).
                value:
                  shows:
                    - to: plantowatch
                      ids:
                        simkl: 2090
              add_show_watching:
                summary: Add a TV show to `watching`
                description: >-
                  TV shows accept all 5 watchlist statuses (movies are
                  restricted to plantowatch/completed/dropped).
                value:
                  shows:
                    - to: watching
                      ids:
                        simkl: 2090
              bulk_add_cross_media_types:
                summary: Bulk add across all three media types in one request
                description: >-
                  The body can carry `movies`, `shows`, and `anime` arrays
                  simultaneously. Each is processed independently; the response
                  aggregates the per-bucket counts.
                value:
                  movies:
                    - to: plantowatch
                      ids:
                        simkl: 752138
                  shows:
                    - to: watching
                      ids:
                        simkl: 1203662
                  anime:
                    - to: completed
                      ids:
                        simkl: 37089
                        mal: 1
                        anidb: 23
                        anilist: 1
              bulk_add_movies_mixed_statuses:
                summary: Bulk add movies with per-item statuses
                description: >-
                  Most efficient way to seed a user's library after import: send
                  all items in one request, each with its own `to`. The server
                  processes each item independently and the response counts all
                  successes. NB: per-item `to` is the canonical shape —
                  top-level `to` is a legacy fallback default.
                value:
                  movies:
                    - to: completed
                      ids:
                        simkl: 472214
                    - to: plantowatch
                      ids:
                        simkl: 752138
              change_movie_status:
                summary: Move an existing item to a new status
                description: >-
                  Same body shape as initial-add. The server updates the
                  existing row instead of duplicating it. Use this to move
                  plantowatch → completed when a user finishes a movie they had
                  on their list.
                value:
                  movies:
                    - to: completed
                      ids:
                        simkl: 472214
              negative_top_level_to_rejected:
                summary: 'REJECTED: `to` at top level alone (without per-item `to`)'
                description: >-
                  **This shape is wrong** — the inherited apiary spec showed it
                  as canonical, but the live server rejects it with `400
                  empty_field` (`Missed "to" parameter`). Per-item `to` is
                  required.
                value:
                  to: plantowatch
                  movies:
                    - ids:
                        simkl: 472214
              quirk_movies_with_watching_status:
                summary: >-
                  Quirk: server silently rewrites `watching` → `completed` for
                  movies
                description: >-
                  Movies are documented as not supporting `watching`/`hold` (see
                  /conventions/list-statuses). The server silently rewrites the
                  requested `watching` to `completed` After the request, the
                  movie's status is `'completed'`.
                value:
                  movies:
                    - to: watching
                      ids:
                        simkl: 472214
              add_anime_full_id_set:
                summary: Add an anime with the full anime-catalog ID set
                description: >-
                  Anime have a richer canonical ID set than movies/shows: `mal`
                  (MyAnimeList) + `anidb` (AniDB) + `anilist` + `kitsu` — in
                  addition to the cross-catalog IDs (`simkl`, `imdb`, `tmdb`,
                  `tvdb`). Send everything you have.
                value:
                  anime:
                    - to: watching
                      title: Kimetsu no Yaiba
                      year: 2019
                      ids:
                        simkl: 831411
                        mal: '38000'
                        anidb: '14107'
                        anilist: '101922'
                        kitsu: '41370'
              add_anime_via_mal_only:
                summary: Add an anime using ONLY its MyAnimeList ID
                description: >-
                  MAL is the most-used anime ID. AniDB / AniList / Kitsu work
                  identically — pass them inside `ids` and the server resolves.
                  No simkl_id required.
                value:
                  anime:
                    - to: plantowatch
                      ids:
                        mal: '38000'
              add_movie_via_imdb_only:
                summary: Add a movie using ONLY its IMDb ID (no simkl_id needed)
                description: >-
                  **The simkl_id is not required.** The server resolves any ID
                  combination internally. Pass whatever IDs your app already has
                  — IMDb, TMDB, TVDB, MAL, AniDB, AniList, Kitsu — alone or in
                  combination. No need to call `/redirect` first.
                value:
                  movies:
                    - to: plantowatch
                      ids:
                        imdb: tt1375666
              add_movie_via_title_year_only:
                summary: Add a movie using ONLY title + year (no IDs at all)
                description: >-
                  When you don't have ANY external ID, the server can match by
                  `title` + `year`. The match is fuzzy and may miss for
                  ambiguous titles — items that don't resolve land in
                  `not_found` rather than `added`, so always inspect the
                  response. Useful for spreadsheet/OCR/voice-assistant backfill
                  flows.
                value:
                  movies:
                    - to: plantowatch
                      title: Inception
                      year: 2010
              add_movie_via_tmdb_only:
                summary: Add a movie using ONLY its TMDB ID
                description: >-
                  Same as the imdb-only example: the server resolves the TMDB ID
                  server-side; no need to know the simkl_id first. Common for
                  apps integrating with Plex, Jellyfin, Stremio, etc., where
                  TMDB is the primary metadata source.
                value:
                  movies:
                    - to: plantowatch
                      ids:
                        tmdb: '27205'
              add_show_full_id_set_plus_title_year:
                summary: >-
                  Add with the full ID set + title + year (canonical maximal
                  shape)
                description: >-
                  Defensive: send EVERY identifier you have. The server picks
                  the first that resolves and accepts the extras. Use this shape
                  when importing from another tracker that already carried
                  multiple IDs — no need to drop any. Useful for Trakt → Simkl
                  migrations, IMDb-list imports, etc. `slug` is response-only —
                  never send it; see [Standard media objects → Supported ID
                  keys](/conventions/standard-media-objects#supported-id-keys).
                value:
                  shows:
                    - to: completed
                      title: Game of Thrones
                      year: 2011
                      ids:
                        simkl: 17465
                        imdb: tt0944947
                        tmdb: '1399'
                        tvdb: '121361'
              add_show_via_tvdb_only:
                summary: Add a TV show using ONLY its TVDB ID
                description: >-
                  TVDB is the most-used canonical ID for TV in media-server
                  stacks (Plex, Sonarr, Jellyfin, Kodi). No simkl_id needed.
                value:
                  shows:
                    - to: watching
                      ids:
                        tvdb: '153021'
              add_unresolvable_lands_in_not_found:
                summary: 'PARTIAL SUCCESS: items that don''t resolve land in `not_found`'
                description: >-
                  When the server can't resolve an item to any catalog record,
                  the request still returns 201 — but the item lands in
                  `response.not_found.<media_type>` instead of
                  `response.added.<media_type>`. Always inspect both arrays
                  after a bulk call.
                value:
                  movies:
                    - to: plantowatch
                      title: ZZZ-Definitely-Not-A-Real-Movie-Title-XYZ
                      year: 9999
        required: true
      responses:
        '200':
          description: OK
          headers: {}
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AddToListResponse'
              example:
                added:
                  movies:
                    - title: 'Terminator 3: Rise of the Machines'
                      to: completed
                      year: '2003'
                      added_at: '2014-09-01T09:10:11.000Z'
                      watched_at: '2014-10-10T22:10:00.000Z'
                      ids:
                        imdb: tt0181852
                        tmdb: 296
                        simkl: 53536
                    - to: plantowatch
                      ids:
                        simkl: 210728
                  shows:
                    - to: watching
                      title: Attack on Titan
                      year: 2013
                      ids:
                        simkl: 39687
                        mal: 16498
                        tvdb: 267440
                        imdb: tt2560140
                        anidb: 9541
                    - title: The Walking Dead
                      year: 2010
                      to: watching
                      ids:
                        simkl: 2090
                        tvdb: 153021
                        imdb: tt1520211
                not_found:
                  movies:
                    - ids:
                        imdb: tt0000222
                  shows: []
              examples:
                add_anime_completed:
                  summary: Add an anime to `completed`
                  description: >-
                    Anime use the `anime` array key in the REQUEST body. (In
                    responses, anime entries are wrapped in `show:` for
                    cross-catalog compatibility — see /conventions/null-values.)
                  value:
                    added:
                      movies: []
                      shows:
                        - to: completed
                          ids:
                            simkl: 831411
                            mal: 38000
                            anidb: 14116
                            anilist: 101922
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_anime_dropped:
                  summary: Add an anime to `dropped`
                  description: >-
                    Anime use the `anime` array key in the REQUEST body. (In
                    responses, anime entries are wrapped in `show:` for
                    cross-catalog compatibility — see /conventions/null-values.)
                  value:
                    added:
                      movies: []
                      shows:
                        - to: dropped
                          ids:
                            simkl: 831411
                            mal: 38000
                            anidb: 14116
                            anilist: 101922
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_anime_hold:
                  summary: Add an anime to `hold`
                  description: >-
                    Anime use the `anime` array key in the REQUEST body. (In
                    responses, anime entries are wrapped in `show:` for
                    cross-catalog compatibility — see /conventions/null-values.)
                  value:
                    added:
                      movies: []
                      shows:
                        - to: hold
                          ids:
                            simkl: 831411
                            mal: 38000
                            anidb: 14116
                            anilist: 101922
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_anime_plantowatch:
                  summary: Add an anime to `plantowatch`
                  description: >-
                    Anime use the `anime` array key in the REQUEST body. (In
                    responses, anime entries are wrapped in `show:` for
                    cross-catalog compatibility — see /conventions/null-values.)
                  value:
                    added:
                      movies: []
                      shows:
                        - to: plantowatch
                          ids:
                            simkl: 831411
                            mal: 38000
                            anidb: 14116
                            anilist: 101922
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_anime_watching:
                  summary: Add an anime to `watching`
                  description: >-
                    Anime use the `anime` array key in the REQUEST body. (In
                    responses, anime entries are wrapped in `show:` for
                    cross-catalog compatibility — see /conventions/null-values.)
                  value:
                    added:
                      movies: []
                      shows:
                        - to: watching
                          ids:
                            simkl: 831411
                            mal: 38000
                            anidb: 14116
                            anilist: 101922
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_movie_completed_per_item:
                  summary: Add a single movie to `completed` (per-item `to`)
                  description: >-
                    Canonical body shape: media-type array of items, each with
                    its own `to` field.
                  value:
                    added:
                      movies:
                        - to: completed
                          ids:
                            simkl: 752138
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                add_movie_dropped_per_item:
                  summary: Add a single movie to `dropped` (per-item `to`)
                  description: >-
                    Canonical body shape: media-type array of items, each with
                    its own `to` field.
                  value:
                    added:
                      movies:
                        - to: dropped
                          ids:
                            simkl: 752138
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                add_movie_plantowatch_per_item:
                  summary: Add a single movie to `plantowatch` (per-item `to`)
                  description: >-
                    Canonical body shape: media-type array of items, each with
                    its own `to` field.
                  value:
                    added:
                      movies:
                        - to: plantowatch
                          ids:
                            simkl: 752138
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                add_show_completed:
                  summary: Add a TV show to `completed`
                  description: >-
                    TV shows accept all 5 watchlist statuses (movies are
                    restricted to plantowatch/completed/dropped).
                  value:
                    added:
                      movies: []
                      shows:
                        - to: completed
                          ids:
                            simkl: 2090
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_show_dropped:
                  summary: Add a TV show to `dropped`
                  description: >-
                    TV shows accept all 5 watchlist statuses (movies are
                    restricted to plantowatch/completed/dropped).
                  value:
                    added:
                      movies: []
                      shows:
                        - to: dropped
                          ids:
                            simkl: 2090
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_show_hold:
                  summary: Add a TV show to `hold`
                  description: >-
                    TV shows accept all 5 watchlist statuses (movies are
                    restricted to plantowatch/completed/dropped).
                  value:
                    added:
                      movies: []
                      shows:
                        - to: hold
                          ids:
                            simkl: 2090
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_show_plantowatch:
                  summary: Add a TV show to `plantowatch`
                  description: >-
                    TV shows accept all 5 watchlist statuses (movies are
                    restricted to plantowatch/completed/dropped).
                  value:
                    added:
                      movies: []
                      shows:
                        - to: plantowatch
                          ids:
                            simkl: 2090
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_show_watching:
                  summary: Add a TV show to `watching`
                  description: >-
                    TV shows accept all 5 watchlist statuses (movies are
                    restricted to plantowatch/completed/dropped).
                  value:
                    added:
                      movies: []
                      shows:
                        - to: watching
                          ids:
                            simkl: 2090
                          type: show
                    not_found:
                      movies: []
                      shows: []
                bulk_add_cross_media_types:
                  summary: Bulk add across all three media types in one request
                  description: >-
                    The body can carry `movies`, `shows`, and `anime` arrays
                    simultaneously. Each is processed independently; the
                    response aggregates the per-bucket counts.
                  value:
                    added:
                      movies:
                        - to: plantowatch
                          ids:
                            simkl: 752138
                          type: movie
                      shows:
                        - to: watching
                          ids:
                            simkl: 1203662
                          type: show
                        - to: completed
                          ids:
                            simkl: 37089
                            mal: 1
                            anidb: 23
                            anilist: 1
                          type: show
                    not_found:
                      movies: []
                      shows: []
                bulk_add_movies_mixed_statuses:
                  summary: Bulk add movies with per-item statuses
                  description: >-
                    Most efficient way to seed a user's library after import:
                    send all items in one request, each with its own `to`. The
                    server processes each item independently and the response
                    counts all successes. NB: per-item `to` is the canonical
                    shape — top-level `to` is a legacy fallback default.
                  value:
                    added:
                      movies:
                        - to: completed
                          ids:
                            simkl: 472214
                          type: movie
                        - to: plantowatch
                          ids:
                            simkl: 752138
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                change_movie_status:
                  summary: Move an existing item to a new status
                  description: >-
                    Same body shape as initial-add. The server updates the
                    existing row instead of duplicating it. Use this to move
                    plantowatch → completed when a user finishes a movie they
                    had on their list.
                  value:
                    added:
                      movies:
                        - to: completed
                          ids:
                            simkl: 472214
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                quirk_movies_with_watching_status:
                  summary: >-
                    Quirk: server silently rewrites `watching` → `completed` for
                    movies
                  description: >-
                    Movies are documented as not supporting `watching`/`hold`
                    (see /conventions/list-statuses). The server silently
                    rewrites the requested `watching` to `completed` After the
                    request, the movie's status is `'completed'`.
                  value:
                    added:
                      movies:
                        - to: watching
                          ids:
                            simkl: 472214
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                add_anime_full_id_set:
                  summary: Add an anime with the full anime-catalog ID set
                  description: >-
                    Anime have a richer canonical ID set than movies/shows:
                    `mal` (MyAnimeList) + `anidb` (AniDB) + `anilist` + `kitsu`
                    — in addition to the cross-catalog IDs (`simkl`, `imdb`,
                    `tmdb`, `tvdb`). Send everything you have.
                  value:
                    added:
                      movies: []
                      shows:
                        - to: watching
                          title: Kimetsu no Yaiba
                          year: 2019
                          ids:
                            simkl: 831411
                            mal: '38000'
                            anidb: '14107'
                            anilist: '101922'
                            kitsu: '41370'
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_anime_via_mal_only:
                  summary: Add an anime using ONLY its MyAnimeList ID
                  description: >-
                    MAL is the most-used anime ID. AniDB / AniList / Kitsu work
                    identically — pass them inside `ids` and the server
                    resolves. No simkl_id required.
                  value:
                    added:
                      movies: []
                      shows:
                        - to: plantowatch
                          ids:
                            mal: '38000'
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_movie_via_imdb_only:
                  summary: Add a movie using ONLY its IMDb ID (no simkl_id needed)
                  description: >-
                    **The simkl_id is not required.** The server resolves any ID
                    combination internally. Pass whatever IDs your app already
                    has — IMDb, TMDB, TVDB, MAL, AniDB, AniList, Kitsu — alone
                    or in combination. No need to call `/redirect` first.
                  value:
                    added:
                      movies:
                        - to: plantowatch
                          ids:
                            imdb: tt1375666
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                add_movie_via_title_year_only:
                  summary: Add a movie using ONLY title + year (no IDs at all)
                  description: >-
                    When you don't have ANY external ID, the server can match by
                    `title` + `year`. The match is fuzzy and may miss for
                    ambiguous titles — items that don't resolve land in
                    `not_found` rather than `added`, so always inspect the
                    response. Useful for spreadsheet/OCR/voice-assistant
                    backfill flows.
                  value:
                    added:
                      movies:
                        - to: plantowatch
                          title: Inception
                          year: 2010
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                add_movie_via_tmdb_only:
                  summary: Add a movie using ONLY its TMDB ID
                  description: >-
                    Same as the imdb-only example: the server resolves the TMDB
                    ID server-side; no need to know the simkl_id first. Common
                    for apps integrating with Plex, Jellyfin, Stremio, etc.,
                    where TMDB is the primary metadata source.
                  value:
                    added:
                      movies:
                        - to: plantowatch
                          ids:
                            tmdb: '27205'
                          type: movie
                      shows: []
                    not_found:
                      movies: []
                      shows: []
                add_show_full_id_set_plus_title_year:
                  summary: >-
                    Add with the full ID set + title + year (canonical maximal
                    shape)
                  description: >-
                    Defensive: send EVERY identifier you have. The server picks
                    the first that resolves and accepts the extras. Use this
                    shape when importing from another tracker that already
                    carried multiple IDs — no need to drop any. Useful for Trakt
                    → Simkl migrations, IMDb-list imports, etc.
                  value:
                    added:
                      movies: []
                      shows:
                        - to: completed
                          title: Game of Thrones
                          year: 2011
                          ids:
                            simkl: 17465
                            slug: game-of-thrones
                            imdb: tt0944947
                            tmdb: '1399'
                            tvdb: '121361'
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_show_via_tvdb_only:
                  summary: Add a TV show using ONLY its TVDB ID
                  description: >-
                    TVDB is the most-used canonical ID for TV in media-server
                    stacks (Plex, Sonarr, Jellyfin, Kodi). No simkl_id needed.
                  value:
                    added:
                      movies: []
                      shows:
                        - to: watching
                          ids:
                            tvdb: '153021'
                          type: show
                    not_found:
                      movies: []
                      shows: []
                add_unresolvable_lands_in_not_found:
                  summary: >-
                    PARTIAL SUCCESS: items that don't resolve land in
                    `not_found`
                  description: >-
                    When the server can't resolve an item to any catalog record,
                    the request still returns 201 — but the item lands in
                    `response.not_found.<media_type>` instead of
                    `response.added.<media_type>`. Always inspect both arrays
                    after a bulk call.
                  value:
                    added:
                      movies: []
                      shows: []
                    not_found:
                      movies:
                        - to: plantowatch
                          title: ZZZ-Definitely-Not-A-Real-Movie-Title-XYZ
                          year: 9999
                          type: movie
                      shows: []
        '400':
          $ref: '#/components/responses/BadRequest'
          content:
            application/json:
              examples:
                negative_top_level_to_rejected:
                  summary: 'REJECTED: `to` at top level alone (without per-item `to`)'
                  description: >-
                    **This shape is wrong** — the inherited apiary spec showed
                    it as canonical, but the live server rejects it with `400
                    empty_field` (`Missed "to" parameter`). Per-item `to` is
                    required.
                  value:
                    error: empty_field
                    code: 400
                    message: Missed "to" parameter
        '401':
          $ref: '#/components/responses/Unauthorized'
        '412':
          $ref: '#/components/responses/ClientIdFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/ServerError'
      security:
        - clientId: []
          bearerAuth: []
        - simklApiKey: []
          bearerAuth: []
components:
  parameters:
    ClientIdQuery:
      name: client_id
      in: query
      required: true
      description: >-
        Your **`client_id`** from your [Simkl developer
        settings](https://simkl.com/settings/developer/). Required on every
        request.
      schema:
        type: string
      example: YOUR_CLIENT_ID
    AppNameQuery:
      name: app-name
      in: query
      required: true
      description: >-
        Short, lowercase identifier for your app (e.g. `plex-scrobbler`,
        `kodi-bridge`). Helps Simkl identify which apps are using the API.
      schema:
        type: string
      example: my-app
    AppVersionQuery:
      name: app-version
      in: query
      required: true
      description: >-
        Your app's current version (e.g. `1.0`, `2.4.1`). Helps Simkl debug
        issues you report.
      schema:
        type: string
      example: '1.0'
    UserAgentHeader:
      name: User-Agent
      in: header
      required: true
      description: >-
        Descriptive identifier for your app, ideally `name/version`. Examples:
        `PlexMediaServer/1.43.1.10540`, `kodi-simkl/0.9.2`, `MyApp/2.4.1
        (https://myapp.com)`.
      schema:
        type: string
      example: my-app/1.0
  schemas:
    AddToListRequest:
      type: object
      description: >-
        Body for `POST /sync/add-to-list`. Moves items into one of the user's
        Watchlist statuses. Items go under `movies[]`, `shows[]`, or `anime[]` —
        Simkl resolves anime titles correctly under either `shows[]` or
        `anime[]`, so match the field to your data type when known.
      required:
        - to
      properties:
        to:
          $ref: '#/components/schemas/WatchlistStatus'
        movies:
          type: array
          items:
            $ref: '#/components/schemas/Movie'
        shows:
          type: array
          items:
            $ref: '#/components/schemas/Show'
        anime:
          type: array
          items:
            $ref: '#/components/schemas/Show'
          description: Array of anime entries (same shape as `shows[]`).
    AddToListResponse:
      type: object
      description: Response from `POST /sync/add-to-list`.
      properties:
        added:
          type: object
          properties:
            movies:
              type: array
              items:
                type: object
                properties:
                  title:
                    type: string
                    example: The Walking Dead
                  year:
                    type: integer
                    example: 2010
                  to:
                    $ref: '#/components/schemas/WatchlistStatus'
                  ids:
                    type: object
                    additionalProperties: true
                    description: Echoed IDs from the request.
                description: >-
                  Item echoed back from the request. The `to` field may differ
                  from what you sent — Simkl silently downgrades `completed` to
                  `watching` (for currently-airing titles) or `plantowatch` (for
                  unreleased titles).
            shows:
              type: array
              items:
                type: object
                properties:
                  title:
                    type: string
                    example: The Walking Dead
                  year:
                    type: integer
                    example: 2010
                  to:
                    $ref: '#/components/schemas/WatchlistStatus'
                  ids:
                    type: object
                    additionalProperties: true
                    description: Echoed IDs from the request.
                description: >-
                  Item echoed back from the request. The `to` field may differ
                  from what you sent — Simkl silently downgrades `completed` to
                  `watching` (for currently-airing titles) or `plantowatch` (for
                  unreleased titles).
            anime:
              type: array
              items:
                type: object
                properties:
                  title:
                    type: string
                    example: The Walking Dead
                  year:
                    type: integer
                    example: 2010
                  to:
                    $ref: '#/components/schemas/WatchlistStatus'
                  ids:
                    type: object
                    additionalProperties: true
                    description: Echoed IDs from the request.
                description: >-
                  Item echoed back from the request. The `to` field may differ
                  from what you sent — Simkl silently downgrades `completed` to
                  `watching` (for currently-airing titles) or `plantowatch` (for
                  unreleased titles).
        not_found:
          $ref: '#/components/schemas/NotFoundReport'
    WatchlistStatus:
      type: string
      enum:
        - watching
        - plantowatch
        - hold
        - completed
        - dropped
      description: >-
        User watchlist status. Movies skip `watching` and `hold` — see
        /conventions/list-statuses.
    Movie:
      type: object
      description: >-
        Standard movie object. See the [Standard Media
        Objects](/conventions/standard-media-objects) guide.
      properties:
        title:
          type: string
          example: 'Terminator 3: Rise of the Machines'
        year:
          type: integer
          example: 2003
        ids:
          $ref: '#/components/schemas/Ids'
      required:
        - ids
    Show:
      type: object
      description: >-
        Standard show object. May include nested seasons/episodes for partial
        sync.
      properties:
        title:
          type: string
          example: The Walking Dead
        year:
          type: integer
          example: 2010
        ids:
          $ref: '#/components/schemas/Ids'
        seasons:
          type: array
          items:
            type: object
            properties:
              number:
                type: integer
              episodes:
                type: array
                items:
                  $ref: '#/components/schemas/Episode'
      required:
        - ids
    NotFoundReport:
      type: object
      description: >-
        Items Simkl could not match. Inspect this to surface diagnostics back to
        your users. Anime entries that fail to resolve land in `shows`
        regardless of which top-level array they were sent under — there is no
        separate `anime` bucket in not_found.
      properties:
        movies:
          type: array
          items:
            $ref: '#/components/schemas/Movie'
        shows:
          type: array
          items:
            $ref: '#/components/schemas/Show'
        episodes:
          type: array
          items:
            $ref: '#/components/schemas/Show'
    ErrorResponse:
      type: object
      description: >-
        Standard error envelope for 4xx and 5xx responses. Branch on `error`
        (machine-readable identifier) — `message`, when present, is
        human-readable guidance and is **not stable across releases**.
      properties:
        error:
          type: string
          description: >-
            Machine-readable error identifier. Stable across responses — use
            this for programmatic branching in client code. Examples:
            `user_token_failed`, `client_id_failed`, `empty_field`,
            `wrong_parameter`, `id_err`, `rate_limit`.
          example: user_token_failed
        code:
          type: integer
          description: >-
            HTTP status code echoed in the body for convenience — same value as
            the response status line. Always an integer.
          example: 401
        message:
          type: string
          description: >-
            Human-readable error guidance. Optional — present on most errors,
            but not guaranteed. May reference the specific field or value that
            triggered the error. NOT stable; do not parse.
          example: Your client_id is wrong. Try another one
      required:
        - error
        - code
    Ids:
      type: object
      description: >-
        External and internal identifiers for an item. Pass as many as you have
        — Simkl resolves to the canonical record.
      properties:
        simkl:
          type: integer
          description: Simkl internal ID. Most reliable.
          example: 53536
        slug:
          type: string
          description: URL-safe slug returned in responses.
          example: attack-on-titan
        imdb:
          type: string
          description: IMDb ID.
          example: tt0181852
        tmdb:
          type: string
          description: TMDb ID.
          example: '296'
        tvdb:
          oneOf:
            - type: integer
            - type: string
          description: TVDB ID or slug.
          example: 153021
        mal:
          type: string
          description: MyAnimeList ID.
          example: '4246'
        anidb:
          type: string
          description: AniDB ID. Specifying just this is enough for anime lookups.
          example: '10846'
        anilist:
          type: string
          description: AniList ID.
          example: '21'
        kitsu:
          type: string
          description: Kitsu ID.
          example: '12'
        anisearch:
          type: string
          description: aniSearch ID.
          example: '2227'
        animeplanet:
          type: string
          description: Anime-Planet slug.
          example: one-piece
        livechart:
          type: string
          description: LiveChart ID.
          example: '321'
        letterboxd:
          type: string
          description: Letterboxd slug.
          example: the-truman-show
        netflix:
          type: string
          description: Netflix movie ID.
          example: '70210890'
        hulu:
          type: string
          description: Hulu episode ID.
        crunchyroll:
          type: string
          description: Crunchyroll episode ID.
        traktslug:
          type: string
          description: Trakt slug.
          example: john-wick-chapter-4-2023
      additionalProperties: true
      example:
        simkl: 53536
        imdb: tt0181852
        tmdb: 296
    Episode:
      type: object
      description: Episode reference. Use `season` + `number`, or `ids`.
      properties:
        season:
          type: integer
          minimum: 0
          example: 1
        number:
          type: integer
          minimum: 1
          example: 2
        watched_at:
          type: string
          format: date-time
          example: '2014-09-01T09:10:11Z'
          description: ISO-8601 GMT timestamp.
        ids:
          $ref: '#/components/schemas/Ids'
  responses:
    BadRequest:
      description: Bad request — a required field is missing or has the wrong shape.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: empty_field
            code: 400
            message: Missed "to" parameter
    Unauthorized:
      description: >-
        Missing or invalid user access token. Provide a valid `Authorization:
        Bearer <access_token>` header.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: user_token_failed
            code: 401
    ClientIdFailed:
      description: >-
        Your `client_id` is missing, wrong, disabled, or has hit a request
        limit. Verify in your [developer
        settings](https://simkl.com/settings/developer/).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: client_id_failed
            code: 412
            message: Your client_id is wrong. Try another one
    RateLimited:
      description: >-
        Too many requests in too short a window. Back off and retry with
        exponential backoff — see [Rate limits](/resources/rate-limits) for the
        playbook.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: rate_limit
            code: 429
    ServerError:
      description: >-
        Something is broken on Simkl's side. Retry after a short delay. If it
        persists, report on the [Simkl Discord](https://discord.gg/MJsWNE4).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error: internal
            code: 500
  securitySchemes:
    clientId:
      type: apiKey
      in: query
      name: client_id
      description: >-
        Preferred form: your `client_id` as a URL query parameter on every
        request. Self-describing in logs and curl commands. See [Headers and
        required parameters](/conventions/headers).
      x-default: YOUR_CLIENT_ID
    simklApiKey:
      type: apiKey
      in: header
      name: simkl-api-key
      description: >-
        Optional alias for the `client_id` query parameter. Simkl accepts your
        `client_id` either as the `simkl-api-key` request header **or** as the
        `?client_id=…` query parameter — pick one. The query-parameter form is
        preferred because it makes the request fully self-describing in URL
        form.
      x-default: YOUR_CLIENT_ID
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        OAuth 2.0 or PIN-flow `access_token`. Required for endpoints that read
        or modify the user's library, scrobble session, ratings, settings, or
        playbacks. See [Authentication](/authentication).
      x-default: YOUR_ACCESS_TOKEN

````