# The game metadata: store copy and required facts

> Online page: https://cassette.gg/agents/metadata.md. Fetch it again before you act on it; don't work from a saved copy.

Cassette asks for more about each game than a person would type into a form: the store page, search, the age rating and
review all run on it. You can fill all of it in a few minutes by reading and playing the game. **The schema is the
authority**: `GET https://cassette.gg/v1/schema/game.json` lists every field, which ones are required, the allowed values, the
limits and the quality rubric. Check your document with `POST https://cassette.gg/v1/schema/game/validate` (every problem at
once), sending it wrapped as `{"metadata": <your document>}`: the bare document answers `missing field metadata`. (Creating the game takes the same
wrapper; `PUT …/metadata` takes the bare document, and the wrapped one too.) This page says how to answer well. Where they disagree, the schema wins.

**Work from the game, not from hopes.** Open the code, run the game in a browser, play it. If you can't find something
out, ask the human. Reviewers compare these answers with the game.

`connectivity.worksOffline` means the game keeps playing after it has loaded and the connection drops.
Test that in Cassette's play frame, including any assets loaded during play. It does not mean the game can be
opened or reloaded offline: Cassette does not cache game builds for offline launch, and a hosted game's frame
cannot register a service worker. An `offline-playable` tag must use the same meaning; don't promise offline installation.

What you declare must be **true, complete, and updated whenever the game changes**: the Developer Distribution
Agreement (§4) makes inaccurate information a breach. Cassette sells the game in its own name, and this is what players
are told before they buy. A false answer gets the game rejected or delisted, and it lowers the game's ranking.

A complete document has this shape (values shortened):

```json
{ "schema": "cassette.game/2", "slug": "neon-drift", "title": "Neon Drift", "tagline": "Drift through a city of light",
  "description": "…300 to 4000 characters…", "genres": ["racing", "arcade"], "tags": ["synthwave", "time-trial"],
  "stage": "released",
  "devices": { "desktop": "full", "phone": "full", "tablet": "playable" },
  "controls": { "keyboard": "required", "mouse": "unsupported", "touch": "supported", "gamepad": "supported" },
  "orientation": "landscape", "players": { "min": 1, "max": 1, "modes": ["single"] },
  "connectivity": { "worksOffline": true, "requiresAccount": false },
  "languages": [{ "code": "en", "interface": true, "subtitles": true, "audio": false }],
  "audio": { "music": true, "soundEffects": true, "voice": false, "playableMuted": true },
  "playtime": { "sessionMinutes": 10, "totalMinutes": 180, "completionMinutes": 480, "endless": false },
  "structure": { "kind": "levels", "count": 24 }, "progress": "cloud",
  "contentRating": { "violence": "none", "bloodGore": "none", "fear": "none", "language": "none", "sexual": "none",
    "substances": "none", "gambling": "none", "discrimination": "none", "purchases": "none",
    "interaction": { "interacts": false }, "onlineServices": { "uses": false } },
  "accessibility": { "remappableControls": true, "subtitles": true, "colorblindFriendly": false, "difficultyOptions": true,
    "reducedMotion": false, "noTimePressure": false, "oneHandedPlay": false, "screenReader": false },
  "quality": { "selfScore": 7, "justification": "…at least 150 characters…" },
  "aiDisclosure": { "code": "assisted", "art": "none", "audio": "none", "music": "none", "voice": "none", "text": "assisted",
    "liveGeneration": false },
  "pricing": { "type": "paid", "priceUsd": "4.99", "demo": { "minutes": 20, "covers": "The first district" } },
  "media": { "box": "media/box.png", "icon": "media/icon.png",
    "screenshots": ["media/s1.png", "media/s2.png", "media/s3.png"] } }
```

## Store copy: `title`, `tagline`, `description`

- **`title`:** the game's real name, as on its title screen. **`slug`:** its address (lowercase, digits, `-`), which
  can't change later. The platform's own words are held back (`www`, `api`, `play`, `docs`, …) and so is anything
  starting with `cassette` — the game's own word is free, and a refused slug names the rule it broke. `reserved` does
  not mean taken: `409 slug_taken` is what says somebody already holds an address.
- **`tagline`:** one line, at most 80 characters, saying what the player does. Not a slogan, no ALL CAPS, no price
  words.
- **`description`:** 300 to 4000 characters. Cover what the game is, what you do, what makes it different, how long it
  is, and what's in the demo if there is one. Be concrete: name the mechanics, the setting and the goal.
  - No filler ("an unforgettable adventure"), no claims you can't show, and don't name other stores.
  - Don't mention how the game was made or which tools made it: that goes in `aiDisclosure`, never in the copy.

## Media: `media`

Take every image from the real game, never a mock-up:
- run it in a browser at the right size (e.g. 1920×1080);
- play to interesting moments;
- capture them.

You need a `box` image (2:3, at least 600×900; an upload whose box has another shape is refused), an `icon` (square) and at least three gameplay `screenshots` (3 to 12,
each ≥1280×720). The box art is the game's face everywhere a player sees it, so it carries the game's title as designed
lettering and is never a bare screenshot: https://cassette.gg/agents/box-art.md. A `loop` (up to 10 seconds, with a `poster`) plays
behind the top of the store page, and a `trailer` helps too. A 16:9 `cover` is optional: only share cards use it, and a wide picture belongs there, not in `box`. A wide `hero`
key art (21:9) is accepted and reviewed with the page, but nothing shows it to players yet: leave it out unless you already have it. Each is a
path inside the build (`media/box.png`); put the files in the zip. PNG, JPEG or WebP. No browser to take the screenshots
with: https://cassette.gg/agents/capture-media.md.

You can leave `media` out while you make them. The game is created and plays by its link; it is listed only once `media`
is complete (a `listing` answer says what is missing), and a game that has been reviewed can't drop them again.

## Content questions: `contentRating`

Questions about what the game contains, not a rating you pick: Cassette works out the age rating from the answers
(the highest age any answer calls for), and `derived.ageRating` shows it. What the ratings mean:
https://cassette.gg/ratings. Answer for the whole game, including what comes after the demo:

| Field | Answers (the age each sets) |
|---|---|
| `violence` | `none` · `cartoon` mild cartoon or fantasy (7) · `fantasy` fantasy, or mild towards realistic characters (12) · `realistic` towards people, or strong fantasy (16) · `extreme` gratuitous, torture, the defenceless (18) |
| `bloodGore` | `none` · `blood` some (12) · `gore` or dismemberment (16) · `extreme` (18) |
| `fear` | `none` · `mild` mildly scary (7) · `horror` jump scares, disturbing scenes (12) · `intense` intense or sustained (16) |
| `language` | `none` · `mild` (12) · `strong` (16) |
| `sexual` | `none` · `suggestive` innuendo (12) · `themes` sexual themes or non-sexual nudity (16) · `explicit` **not allowed** |
| `substances` | `none` · `references` (12) · `depicted` use (16) · `glamorised` illegal drug use (18) |
| `gambling` | `none` · `depicted` (12) · `simulated` playable, no real money (18) · `real-money` or prizes of value **not allowed** |
| `discrimination` | `none` · `critical` shown critically (16) · `encouraged` **not allowed** |
| `purchases` | `none` · `yes` · `random-items` paid random items (their odds must be shown) |
| `interaction` | `{ "interacts": false }`, or `true` with `chat`, `multiplayer`, `playerContent` and `moderation` (how it's moderated) |
| `onlineServices` | `{ "uses": false }`, or `true` with `services`: what the game sends data to besides Cassette |

A "not allowed" answer can't be published. If the game has a level you can't reach, ask the human what's in it. When
unsure between two answers, choose the stronger one.

## Kind of game: `genres`, `tags`, `stage`, `players`

- **`genres`:** one to three, from the schema's list (`action`, `puzzle`, `roguelike`, `rpg`, …): the ones a player
  would use to find it.
- **`tags`:** mechanics, mood, setting and perspective. Be specific ("deck-building", "pixel-art", "cozy"); "fun" and
  "game" aren't tags. Three to twenty of them, each 2 to 24 characters of lowercase letters, digits and `-`, none twice.
- **`stage`:** `released`, `early-access`, `demo-only`, `prototype` or `jam`. Be honest: a prototype says so.
- **`players`:** `min`, `max` and `modes` (`single`, `local-coop`, `online-versus`, …).

## How it's played: `devices`, `controls`, `orientation`, `connectivity`, `languages`, `audio`

- **`devices`:** for `desktop`, `phone` and `tablet`, say `full`, `playable` or `unsupported`. Test it at a phone size
  (e.g. 390×844) with touch before you say `phone`.
- **`controls`:** for `keyboard`, `mouse`, `touch` and `gamepad`, say `required`, `supported` or `unsupported`.
- **`languages`:** one entry per language the game ships (`code`), marking `interface`, `subtitles` and `audio`
  separately.
- **`audio`:** does it have `music`, `soundEffects`, `voice`, and is it `playableMuted`?
- **`connectivity`:** does it work offline, and does it need an account?

## Length: `playtime`, `structure`, `progress`

- **`playtime`:**
  - `sessionMinutes`: a typical sitting;
  - `totalMinutes`: the main path;
  - `completionMinutes`: everything;
  - `endless`: there's no end. Then `totalMinutes` must be `null`, not an estimate (`must be null when endless`); a game with an end
    says `"endless": false` and a `totalMinutes`.

  Measure it if you can, and say how you estimated it in the quality justification.
- **`structure`:** `kind` (`levels`, `chapters`, `episodes`, `runs`, `open-world`, `endless`) and `count`, for levels,
  chapters or episodes.
- **`progress`:** `cloud` if the game saves through Cassette storage, `none` if it keeps nothing.

## Accessibility: `accessibility`

`true` only for what the game really has: `remappableControls`, `subtitles`, `colorblindFriendly`,
`difficultyOptions`, `reducedMotion`, `noTimePressure`, `oneHandedPlay`, `screenReader`.

## Quality: `quality`

`selfScore` runs from 1 to 10 and follows the schema's rubric:
- 1-2: broken or a stub;
- 3-4: a playable prototype;
- 5-6: a complete loop with little content;
- 7-8: finished and polished;
- 9-10: commercial-grade.

Write a `justification` of at least 150 characters that names specifics:
- what works;
- what's rough;
- bugs you saw;
- how complete the content is;
- how you measured the playtime.

`knownIssues` is optional. Score it as a demanding player would. The test run and the reviewers check it, and an
inflated score is treated like any other false answer. A small, finished game can score well; a big unfinished one
shouldn't.

## AI use: `aiDisclosure`

For each of `code`, `art`, `audio`, `music`, `voice` and `text`, answer `none`, `assisted` or `generated`. Your own
work on the game counts too. `liveGeneration` is `true` if the game generates content with AI while it runs. This is a
required fact for classification and the law; it never appears in the store copy.

## Links: `links`

Optional `website`, `source` and `community` HTTPS URLs appear with the game's credits.
For a Discord server, use `"links": {"discord": "https://discord.gg/YourCode"}`.
Only `https://discord.gg/<code>` and `https://discord.com/invite/<code>` are accepted.
The complete input URL, including any surrounding spaces, must be at most 500 characters.
Codes contain ASCII letters, digits, underscores and hyphens and keep their case.
Scheme and host are case-insensitive; surrounding spaces and one trailing slash are removed.
Both forms are stored as `https://discord.gg/<code>`. No credentials, port, extra path, query or fragment.
Use an invite that does not expire. An unset invite shows nothing on the game page.

To change only the invite, `PATCH /v1/games/{game}/metadata` with
`{"links":{"discord":"https://discord.gg/YourCode"}}`; to remove it, patch
`{"links":{"discord":null}}`. Omit the field in a complete metadata document when unset.
On a listed game, adding, changing or removing a link waits for the existing store-copy review;
players see the approved version until then. The developer can also edit it on the dashboard's game page.

## Price: `pricing`

`{"type":"free"}`, or `{"type":"paid","priceUsd":"4.99"}` with an optional `demo` (`minutes` and/or `covers`: what the
free part includes). The price includes VAT, and the developer's share of a sale is 90% after tax and payment fees; see
https://cassette.gg/agents/publish-game.md §6.

---

Code in this document is MIT-licensed: `SPDX-License-Identifier: MIT`, Copyright (c) 2026 Delegas Labs, S.L. Copy and
adapt it freely.
