# Economies - UGC

Welcome to the **UGC layer** of Sunflower Land. This is not only cosmetics: players can launch **resources**, **complex gameplay loops**, and more through the **resource launchpad** and **economy editor**.

* This feature is **experimental**. Enable it in the game: **Settings → Developer Options → Experiments → Economy Editor**.
* With the economy editor enabled, you can also browse other players’ economies and games from **Marketplace → Economies**.

This document focuses on **HTTP APIs** used by hosted HTML minigames. Handler code in this repo lives under `src/api/minigames/` (session + actions) and is wired in `infra/api.ts` as the **Minigames** API Gateway (see `MinigamesApi` routes for `/data`, `/action`, `/animate/...`, `/bumpkins/metadata/...`).

***

### Setting up an economy

An **economy** is your tokenised design: resources, rules, and copy that define what players can hold and do. You can keep it minimal or build something closer to a **world builder**.

#### Items

Here you define the things that make up your game: resources, buildings, achievements, skills, scores, etc. Think in terms of **tokenising everything**—each distinct thing players earn, spend, or unlock should usually be an **item** (balance token key) with metadata (name, description, optional art, marketplace fields).

#### Rules

Rules describe **allowed transitions** on player balances (and timed generator jobs).

| Rule type (editor) | Purpose                                                                                                                                                                                                                                                              |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Shop**           | **Burn** some tokens, **mint** others (and optional **require** / caps). Use for crafting, shops, exchanges (e.g. burn 3 wood, mint an axe). Shop-derived rows can appear in the default economy dashboard when `showInShop` is not `false`.                         |
| **Generator**      | Like a shop, but includes **time** and optional **random** collect outcomes (`chance` on collect rows). Use for cooking, production, staking-style “come back later” loops. Often modelled as one action with `produce` + `collect` and `itemId` for the active job. |
| **Custom**         | Rules for **advanced / iframe** minigames. They are **not** shown in the basic economy dashboard; your game calls them by **action id** via the API.                                                                                                                 |

Every time you **save**, your economy config is published for players. For behaviour that cannot be expressed in rules alone (dungeons, battles, etc.), host a **custom minigame** and call **custom** actions from your client.

Server-side rule shapes and processing align with `src/domain/playerEconomy/types.ts` and `processAction.ts`.

***

### Building a custom portal & uploading

Custom minigames are **static HTML** experiences opened in the browser (often in an iframe). The official templates use **React** and **Phaser**, but you may use any stack.

* Ship a build with **`index.html`** at the entry URL.
* In the economy editor, use the **upload** flow to publish the files needed to run your game.

#### Reading and writing player data

* **Read** economy config + farm snapshot + per-player economy state: **Minigames API** `GET /data?type=session` (see below). Use the **portal JWT** in `Authorization`.
* **Write** state transitions that you defined as rules: **Minigames API** `POST /action` with `type: "minigame.action"` (see below).

#### Reference implementation (open source)

* **Template repo (React + portal bootstrap):** [sunflower-land-template](https://github.com/sunflower-land/sunflower-land-template)
  * JWT from query string: [`src/lib/portal/url.ts`](https://github.com/sunflower-land/sunflower-land-template/blob/main/src/lib/portal/url.ts) (`getJwt`, optional `minigamesApiUrl` / `apiUrl` query params).
  * Session fetch: [`src/lib/portal/api.ts`](https://github.com/sunflower-land/sunflower-land-template/blob/main/src/lib/portal/api.ts) (`getPlayerEconomySession`).
  * Action POST: same file (`postPlayerEconomyAction`).
  * Economy dashboard example: `src/examples/ui-resources/` (shop + generator UI driven by session payload).

***

### Authentication (portal JWT)

Before you can load session data or submit actions, you need a **portal JWT**. It identifies the **player** and **farm** and authorises Minigames API calls. The **portal id** inside the JWT must match your economy’s **slug** as saved in the editor.

#### In production (player opens your game)

The game client typically opens your hosted URL with the token in the query string, for example:

`https://your-economy.minigames.sunflower-land.com/?jwt=…`

Read `jwt` in your app and send it on every Minigames request:

```http
Authorization: Bearer <portal-jwt>
```

#### Local development

In the **economy editor**, use **Generate token** (or equivalent) and append the token to your dev server URL, e.g.:

`http://localhost:3000/?jwt=…`

#### Server validation

Handlers verify the JWT with the portal secret; see `src/api/minigames/data.ts` and `src/api/minigames/action.ts`.

***

### Economy data (session payload)

The session response is the structured bundle your minigame uses to render UI and validate client-side optimistically:

* **`farm`** — high-level farm info (e.g. SFL balance string, bumpkin) for context.
* **`playerEconomy`** — balances, active generator jobs (`generating`), activity counters, daily mint buckets, optional `dailyActionUses` / `purchaseCounts`.
* **`actions`** — map of **action id → rule definition** (shop / generator / custom shapes).
* **`items`** — optional map of **token key →** display metadata (name, description, `image`, marketplace `id` / `tradeable`, etc.).
* **`descriptions`**, **`visualTheme`**, **`mainCurrencyToken`** — optional chrome for dashboards.
* **`playUrl`** — canonical hosted game base for this portal (derived server-side; see `playerEconomyOperations.ts`).

Authoritative TypeScript types: `PlayerEconomySessionPayload`.

#### Example `GET /data?type=session` success body

Shape returned by the API (your real `actions` / `items` / balances will differ):

```json
{
  "data": {
    "farm": {
      "balance": "123.45",
      "bumpkin": { }
    },
    "playerEconomy": {
      "balances": { "WOOD": 10, "AXE": 0 },
      "generating": {},
      "activity": 42,
      "dailyActivity": { "date": "2026-04-08", "count": 3 },
      "dailyMinted": { "utcDay": "2026-04-08", "minted": {} },
      "dailyActionUses": { "utcDay": "2026-04-08", "byAction": {} },
      "purchaseCounts": {}
    },
    "actions": {
      "craft_axe": {
        "burn": { "WOOD": { "amount": 3 } },
        "mint": { "AXE": { "amount": 1 } }
      }
    },
    "items": {
      "WOOD": { "name": "Wood", "description": "…", "image": "resources/wood.png" },
      "AXE": { "name": "Axe", "description": "…" }
    },
    "descriptions": {
      "title": "My economy",
      "subtitle": "Craft and gather"
    },
    "visualTheme": "default",
    "playUrl": "https://my-game.minigames.sunflower-land.com"
  }
}
```

#### Session errors (selected)

| HTTP                                            | Meaning                                                                          |
| ----------------------------------------------- | -------------------------------------------------------------------------------- |
| **401**                                         | Missing / invalid JWT.                                                           |
| **422** + `errorCode: "UNKNOWN_PLAYER_ECONOMY"` | No saved economy for this **portal id**, or slug mismatch vs editor.             |
| **422** + `errorCode: "FARM_NOT_FOUND"`         | JWT `farmId` does not match a farm in this API environment.                      |
| **403**                                         | Token minigames / player economies not available for this farm (feature gating). |

***

### Loading session data

**Request**

```http
GET /data?type=session
Authorization: Bearer <portal-jwt>
```

* **Implementation:** `src/api/minigames/data.handler` → `minigameSessionData` → `loadPlayerEconomySession`.
* **Portal id** is taken **only from the JWT** (not from query params).

**Base URL**

The Minigames API is deployed as its own API Gateway; the exact hostname depends on environment (there is not a single guaranteed public host like `minigame-api.sunflower-land.com` in code—always use the base URL from your deployment or client config, e.g. `VITE_MINIGAMES_API_URL` in the [template](https://github.com/sunflower-land/sunflower-land-template)). Hosted **game sites** often follow `{slug}.minigames.sunflower-land.com` (see `MINIGAMES_SITE_HOST_SUFFIX` in `infra/api.ts`); the **API** origin for `fetch` may differ—point your client at the Minigames API base you were given.

**Example client**

See [sunflower-land-template `getPlayerEconomySession`](https://github.com/sunflower-land/sunflower-land-template/blob/main/src/lib/portal/api.ts).

***

### Actions (mutations)

Use actions when the player **collects** something, **starts** a run, **submits** a score, **unlocks** progression, etc. The server **only executes actions that exist** in your published economy config, which keeps outcomes bounded to what you defined.

#### Request

```http
POST /action
Content-Type: application/json
Authorization: Bearer <portal-jwt>
```

**Body (JSON)**

```json
{
  "type": "minigame.action",
  "action": "your_action_id",
  "itemId": "optional-generator-job-id",
  "amounts": {
    "TOKEN_KEY": 5
  }
}
```

| Field     | Required | Description                                                                                                                           |
| --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `type`    | Yes      | Must be `"minigame.action"`.                                                                                                          |
| `action`  | Yes      | **Action id** from the editor (set a readable custom id in the label section when possible).                                          |
| `itemId`  | No       | For **generator collect** (and similar), the **job id** returned when the job was started (or present in `playerEconomy.generating`). |
| `amounts` | No       | Integer map used for **ranged mint** / **ranged burn** rules: token key → chosen amount within your configured min/max.               |

**Success response**

```json
{
  "data": {
    "playerEconomy": {
      "balances": {},
      "generating": {},
      "activity": 0,
      "dailyActivity": { "date": "", "count": 0 },
      "dailyMinted": { "utcDay": "", "minted": {} },
      "dailyActionUses": { "utcDay": "", "byAction": {} },
      "purchaseCounts": {}
    },
    "generatorJobId": "optional-new-job-uuid",
    "collectGrants": [{ "token": "CROP", "amount": 2 }]
  }
}
```

`collectGrants` appears when the server resolves weighted collect outcomes for a collect action.

**Errors**

| HTTP    | Typical cause                                                                   |
| ------- | ------------------------------------------------------------------------------- |
| **400** | Invalid body, unknown action, rule validation failed (`error` message in body). |
| **401** | Invalid JWT.                                                                    |
| **422** | Unknown economy / farm (same ideas as session).                                 |
| **403** | Feature disabled for farm.                                                      |

**Implementation:** `src/api/minigames/action.handler`, `minigameAction.ts`, `executePlayerEconomyAction`.

**Example client**

[sunflower-land-template `postPlayerEconomyAction`](https://github.com/sunflower-land/sunflower-land-template/blob/main/src/lib/portal/api.ts).

***

### Other useful APIs & data

#### Bumpkin / NPC animations (Minigames API)

**Route (same API Gateway as `/data` and `/action`):**

```http
GET /animate/{tokenUri}/{animation}
```

Renders bumpkin (and related) sprite animations for NPCs and large character art. Handler: `src/api/animation/`. Dependencies include layer definitions under `src/domain/bumpkins/` (e.g. `layerImages`, `npcBuilder`, `bumpkinBuilder`). Use this when you need walking, fishing, mining, chopping, jumping, etc., driven by bumpkin `tokenUri` and animation name.

There is also **`GET /bumpkins/metadata/{tokenUri}`** on the same API for metadata helpers.

#### Marketplace: discovering economies & trade context (main Sunflower Land API)

Browsing **Marketplace → Economies** in the game uses the **main** game API (player session JWT, not the portal JWT), for example:

```http
GET {API_URL}/data?type=marketplaceEconomies
Authorization: Bearer <game-user-jwt>
```

Returns economy rows and related leaderboard-style data; see `loadEconomiesMarketplacePageData` and the client helper [`loadEconomiesMarketplaceData.ts`](https://github.com/sunflower-land/sunflower-land/blob/main/src/features/marketplace/actions/loadEconomiesMarketplaceData.ts) in the `sunflower-land` repo.

For a **specific** tradable (listings, offers, sale history) the game uses collection endpoints such as:

```http
GET {API_URL}/collection/economies/{id}?type=economies&economy={economySlug}
Authorization: Bearer <game-user-jwt>
```

See [`loadTradeable.ts`](https://github.com/sunflower-land/sunflower-land/blob/main/src/features/marketplace/actions/loadTradeable.ts). Use this pattern when building dashboards or tools that need **market history** for an economy token—not for mutating minigame state (that remains **`POST /action`** on the Minigames API).

***

### Quick reference

| Goal                                     | API                                        | Auth                              |
| ---------------------------------------- | ------------------------------------------ | --------------------------------- |
| Load config + economy state for minigame | `GET /data?type=session`                   | Portal JWT                        |
| Apply a defined rule                     | `POST /action` (`type: "minigame.action"`) | Portal JWT                        |
| NPC / bumpkin animation asset            | `GET /animate/{tokenUri}/{animation}`      | Per deployment (often public GET) |
| List economies in marketplace            | `GET /data?type=marketplaceEconomies`      | Game user JWT                     |
| Tradeable detail / history               | `GET /collection/...`                      | Game user JWT                     |

For infrastructure wiring (routes, CORS, memory), see **`MinigamesApi`** in `infra/api.ts`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.sunflower-land.com/contributing/economies-ugc.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
