# Listing games

A game's distribution, what your site may list, how to add a game to your catalog, and why an add is refused.

Your catalog is yours: which games, in which order, with which titles, descriptions, categories and tags, per
locale. Which games you may pick is decided by the game's developer and by approval.

## Distribution

Every game carries a `distribution`, set by its developer in the console:

- `first_party`, the default: the game is listed by the owner's own sites only (filudi.com and the other sites
  marked first-party by staff).
- `any_portal`: any approved site may list it.

A developer may change it at any time. A game switched back to `first_party` stays in your catalog as an entry,
and the public catalog skips it from then on: your players stop seeing it, and the console shows the entry with
the reason. Nothing else of your catalog moves.

## What you may list

An approved game, with a live version, whose distribution is `any_portal`. The console's "Add a game" list
shows exactly those; the API behind it is `GET /v1/admin/games?available=<your site id>`. A pending site of a
game developer also sees that developer's own pending games, so a game and its portal are built together before
either is approved.

<CodePanel title="Games your site may add">
  <Fragment slot="curl">

    ```sh
    curl 'https://api.filudi.com/v1/admin/games?available=00000000-0000-0000-0000-000000000000' \
      -b "filudi_console=$FILUDI_SESSION"
    ```

  </Fragment>
  <Fragment slot="javascript">

    ```js
    const response = await fetch(`https://api.filudi.com/v1/admin/games?available=${siteId}`, {
      credentials: 'include',
    });
    const games = await response.json(); // Game[], each with slug, name, distribution, versions
    ```

  </Fragment>
  <Fragment slot="python">

    ```py
    games = session.get(
        'https://api.filudi.com/v1/admin/games',
        params={'available': site_id},
    ).json()
    ```

  </Fragment>
</CodePanel>

## Add a game

`PUT /v1/admin/sites/{siteId}/catalog/{gameId}` creates or updates the entry: `featured`, `visible`,
`categories` and `tags` (slugs of your site's own lists), and `texts` per locale (`title`, `tagline`,
`description`, `howToPlay`, `controls`, `seoTitle`, `seoDescription`). Your site's default locale needs a
title. The same route updates an entry later; `DELETE` removes it; `PUT .../catalog/order` reorders the whole
list.

<CodePanel title="Add Gloop! to your catalog">
  <Fragment slot="curl">

    ```sh
    curl -X PUT 'https://api.filudi.com/v1/admin/sites/00000000-0000-0000-0000-000000000000/catalog/00000000-0000-0000-0000-000000000000' \
      -b "filudi_console=$FILUDI_SESSION" \
      -H 'origin: https://developers.filudi.com' \
      -H 'content-type: application/json' \
      -d '{
      "visible": true,
      "featured": false,
      "categories": ["puzzle"],
      "tags": [],
      "texts": { "en": { "title": "Gloop!", "tagline": "string", "description": "string" } }
    }'
    ```

  </Fragment>
  <Fragment slot="javascript">

    ```js
    const response = await fetch(`https://api.filudi.com/v1/admin/sites/${siteId}/catalog/${gameId}`, {
      method: 'PUT',
      credentials: 'include',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({
        visible: true,
        featured: false,
        categories: ['puzzle'],
        tags: [],
        texts: { en: { title: 'Gloop!', tagline: 'string', description: 'string' } },
      }),
    });
    if (response.status === 403) {
      const { message } = await response.json(); // why the site may not list the game
    }
    ```

  </Fragment>
  <Fragment slot="python">

    ```py
    response = session.put(
        f'https://api.filudi.com/v1/admin/sites/{site_id}/catalog/{game_id}',
        json={
            'visible': True,
            'featured': False,
            'categories': ['puzzle'],
            'tags': [],
            'texts': {'en': {'title': 'Gloop!', 'tagline': 'string', 'description': 'string'}},
        },
    )
    if response.status_code == 403:
        print(response.json()['message'])
    ```

  </Fragment>
</CodePanel>

## Why an add is refused

The route answers 403 with the reason in `message`, and the console shows the same sentence:

| Reason                                                     | What to do                                                                      |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------- |
| The game is pending approval                               | Wait for staff, or, for your own game, ask for approval from the game's scope   |
| The game has no live version                               | The developer must get a version activated: a draft is not playable on any site |
| The game is `first_party` and your site is not first-party | Ask the developer to set the distribution to any portal                         |

A 404 means the game id is unknown, or one your account may not see.

## Texts, categories and tags

Texts are per site and per locale: your title for a game can differ from the same game's title on filudi.com.
Categories and tags are your site's own lists, created under the site, with their own texts and order; a game
entry carries slugs of those lists. `navCategories` of the site picks which categories make the top menu.
Changes show on your front after you publish ([Going live](/docs/portals/going-live/)).

## The game itself

The bundle, its versions, its media, its boards and its name belong to the developer. Your catalog entry
points at the game; the live version is the same on every site that lists it. When the developer activates a
new version, your players get it at the next load, with no action on your side.
