# Start here

From an invitation to a pending portal with proposed domains, and what approval changes.

## An account

Accounts are created by invitation. Ask for one with the form at [/request/](/request/): your email, what you
build, a few lines. Staff reads the requests and sends an invitation by email; the link is valid 7 days. You set
a name and a password, and the account is active. An authenticator (TOTP) is optional for a developer and
required for staff.

One account holds any number of games and sites. The console shows one scope at a time; the switcher in the
top bar lists every game and site of the account.

## Create the portal

In the console, "New portal". A portal is a site row: a slug, a name, locales, a policy.

- The slug is yours for good and appears in the API paths (`/v1/catalog/<slug>`). Reserved words are refused:
  `admin`, `api`, `play`, `developers`, `filudi`, `www`, `static`.
- The first locale is the default. Texts of your catalog are written per locale.
- The policy says whether players may create accounts (`accountsAllowed`), whether personalised ads are
  allowed, and the age band (`all`, `kids`, `teen`, `adult`). A `kids` site never gets personalised ads.

The site is created pending. Your account is its owner. You hold at most 3 pending sites at a time.

<CodePanel title="The same, with the API">
  <Fragment slot="curl">

    ```sh
    curl -X POST 'https://api.filudi.com/v1/admin/sites' \
      -b "filudi_console=$FILUDI_SESSION" \
      -H 'origin: https://developers.filudi.com' \
      -H 'content-type: application/json' \
      -d '{ "slug": "string", "name": "string", "locales": ["en"], "accountsAllowed": true, "ageBand": "all" }'
    ```

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

    ```js
    const response = await fetch('https://api.filudi.com/v1/admin/sites', {
      method: 'POST',
      credentials: 'include',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ slug, name, locales: ['en'], accountsAllowed: true, ageBand: 'all' }),
    });
    const site = await response.json(); // site.approvedAt is null until staff approves it
    ```

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

    ```py
    import requests

    session = requests.Session()
    session.headers['origin'] = 'https://developers.filudi.com'
    session.cookies.set('filudi_console', console_session)
    site = session.post(
        'https://api.filudi.com/v1/admin/sites',
        json={'slug': slug, 'name': name, 'locales': ['en'], 'accountsAllowed': True, 'ageBand': 'all'},
    ).json()
    ```

  </Fragment>
</CodePanel>

## What pending means

A pending site works from the console and on the development origins (`http://localhost:4321`,
`http://localhost:5173` on a local API; the playground). You can build the whole front before anyone sees it:

- its catalog, categories, tags, top menu and texts;
- its players, from the development origins;
- its own pending games, if you write games too: a pending site may list the pending games of its own
  developer, so a game and its portal are built together.

A pending site has no allowed origin of its own: its domains do not call the API and do not frame a game. It
has no server keys. It is not in `otherSites` of any site.

## Propose domains

In the site's settings, add the hostnames your front will run on: lowercase, without scheme or port
(`games.example.com`). They go into `pendingDomains`. Staff approves each one; an approved domain moves into
`domains` and becomes an allowed origin within a minute on every API pod.

The first domain of `domains` is canonical: the sign-in emails of your players link to it. A domain on or under
the play domain is refused with the issue code `game_host`.

`localhost` is never a domain. Local work goes through the development origins above.

## Approval

Staff sees pending sites, domains, games and versions in one queue, oldest first, and gets an email for each
new item. A refusal comes with a reason, by email, and the site stays pending so you can change it and ask
again. Approval sets `approvedAt`; from then on:

- the approved domains are allowed origins and `frame-ancestors`;
- the site can create server keys;
- the site can list approved games whose distribution allows it ([Listing games](/docs/portals/listing-games/)).

## Members

An owner invites members from the site's Members screen, by email. An existing account gets the membership at
once; an unknown email gets an invitation that carries it. A member has every right of the owner except
membership changes and deletion. A site keeps at least one owner.
