cassette.json (manifest)
Lives at the root of the game folder. Checked by the API against the schema below when you upload it (POST /v1/metadata) (manifestVersion: 1). Unknown fields are ignored.
| Field | Type | Required | Default / rule |
|---|---|---|---|
manifestVersion | 1 | yes | literal 1 |
slug | string | yes | ^[a-z0-9][a-z0-9-]{1,46}[a-z0-9]$ (3-48 chars). Unique, immutable after first publish |
title | string | yes | 1-60 chars |
tagline | string | no | ≤ 80 chars; keep ≤ 6 words (shown on covers) |
description | string | no | ≤ 2000 chars |
genres | string[] | no | ≤ 3 items, each ≤ 24 chars. Default [] |
tags | string[] | no | ≤ 10 items, each ≤ 24 chars. Default [] |
capabilities | string[] | yes | ≥ 1 of phone tablet desktop touch mouse keyboard gamepad multiplayer-local multiplayer-online offline audio; at least one of phone/tablet/desktop |
orientation | portrait|landscape|any | no | any |
pricing | object | yes | {"type":"free"} · {"type":"free-with-items"} · {"type":"paid","priceUsd":"4.99","licenseTransferable":false} |
demo | boolean or object | no | A free demo, then the licence to keep playing: true, or {"minutes": 30, "covers": "Chapter 1"} (≤ 600 min, ≤ 120 chars); false = pay to play. Unset: a demo when the storefront sells a licence. The game's own paywall decides where it ends; the store shows "Free demo · $4.99" |
items | object[] | no | ≤ 100, requires pricing ≠ free. See below. Default [] |
entry | string | no | index.html (path of the HTML file inside the folder) |
media | object | yes | cover (required, 16:9 image path or https URL), icon (square, for the installable app), screenshots (≤ 12), trailer (URL) |
contentRating | everyone|teen|mature | no | everyone. Realistic violence or gore, drug use or strong language needs "mature" (content policy); the store page then shows a Mature marker. |
license | string | no | ≤ 64 chars, SPDX id e.g. MIT |
sourceUrl | https URL | no | link to the source repo |
hosting | object | no | {"type":"hosted"} (default; legacy: Cassette hosts builds for its own games only) · {"type":"external","url":"https://..."} · {"type":"ipfs","cid":"..."}. A developer's game is hosted in their own account (https://cassette.gg/agents/host-game.md) and registered as {"type":"external","url":"<play URL>"} (ar://<id> on Arweave) |
communityListingId | string | no | set when converting a claimed community listing |
storefront | object | no | The developer's own storefront contract (set it in publish-game step 5) that sells this game: {"chainId":137,"address":"0x…","game":"0x…"?,"license":"1"?,"items":{"<sku>":"2"}}. game defaults to keccak256(slug); license/items are listing ids. The paywall reads it from the served cassette.json |
previousStorefronts | object[] | no | Earlier storefronts of the same developer (same shape as storefront, ≤ 8): their licences and items still count as owned |
legal | object | no | The adopted documents (https://cassette.gg/agents/set-up-seller.md): {"terms":{"url","sha256"},"refunds"?,"privacy"?,"imprint"?,"withdrawalForm"?}, each on the developer's own origin with the sha256 of the served bytes |
mirrors | https URL[] | no | ≤ 4 mirrors of the build in the developer's own accounts (e.g. an IPFS subdomain gateway). Add them when you register the game |
priceUsd: ^\d{1,7}(\.\d{1,2})?$ (> 0 for paid).
items[]
| Field | Type | Rule |
|---|---|---|
sku | string | ^[a-z0-9][a-z0-9._-]{0,30}$, unique per game, permanent |
name | string | 1-48 chars |
description | string | ≤ 280 chars |
priceUsd | string | USD, ≤ 2 decimals; "0" = free claim |
maxSupply | integer | 0 = unlimited. Immutable after first sync |
transferable | boolean | default true. Immutable |
consumable | boolean | default false. Immutable |
image | string | path inside the build or https URL |
Media paths for hosted games are relative to the folder and must exist; for external/ipfs hosting they must be absolute https URLs. Keep relative paths in the build's copy; in the copy you upload (POST /v1/metadata), make them absolute URLs on the play origin.
Minimal example
{
"manifestVersion": 1,
"slug": "blob-run",
"title": "Blob Run",
"capabilities": ["phone", "desktop", "touch", "keyboard"],
"pricing": { "type": "free" },
"media": { "cover": "cover.svg", "icon": "icon.svg" }
}
Code in this document (and paywall.js) is MIT-licensed: SPDX-License-Identifier: MIT, Copyright (c) 2026 Delegas
Labs, S.L. Copy and adapt it freely.