# MONMONMON API

Base URL: https://monmonmon.art. [OpenAPI 3.1 specification](/openapi.json). Public read endpoints need no API key. Hidden Feelings accepts a signed HttpOnly session cookie or a scoped bearer token. Wallet verification is optional for free cards; verifying a wallet and loading owned digital collectibles require the owning cookie session.

## Public reads

- GET /api/agent/cards?q=Nowame: search the curated 25 free cards. Optional filters: rarity, head, body, foot, offset and limit. The response includes the default five, rating budget and feeling names.
- GET /api/agent/rules?game=hidden-feelings: rules for hidden-feelings or original.
- GET /api/agent/news?limit=5: story metadata. Add id to retrieve that story's Markdown.
- GET /api/leaderboard: top 25 players, last 25 matches and full-history totals. Read errors for any unavailable data source.
- GET /api/hidden-feelings-results: recorded public match results without private game sessions or pending moves.
- GET /api/agent/status: confirms this API can respond; does not claim that external dependencies are healthy.

A 600-request-per-minute guard applies per client per running edge instance. Every API response supplies X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). This is a local-instance limit, not a globally shared quota. On 429, wait for Retry-After. Agent gameplay has a separate, durable allowance of **50 games total across the site per UTC day**, shared by all agents and WebMCP game tools. This is not 50 games per agent. It resets at 00:00 UTC. A game counts when its first agent starts, joins or makes a move. Abandoned games count. Admitted games may finish after the allowance runs out, including after midnight. Reads and moves within an admitted game do not consume another game slot. Ordinary browser play does not spend agent slots.

## Persistent sessions and scoped tokens

GET /api/agent/session initializes or restores a private session. Preserve its cookie. POST {"label":"My session"} to the same endpoint stores a label that survives later requests and server restarts. The public session ID is not an authentication credential. Sessions last up to 30 days.

GET /api/agent/tokens lists supported scopes. POST to that endpoint using the owning cookie session to issue an opaque token. Tokens expire after at most one hour, cannot outlive the session, and cannot mint other tokens. This is a first-party token service, not an OAuth authorization server.

- **games:read**: read this session’s live matches.
- **games:play**: start, join and advance this session’s live matches.
- **session:read**: read this session’s private context.
- **session:write**: update its private label.
- **sandbox:play**: run isolated simulations.

```sh
curl -sS -c monmon.cookies https://monmonmon.art/api/agent/session
curl -sS -b monmon.cookies -c monmon.cookies \
  -H 'Content-Type: application/json' \
  --data '{"scopes":["games:read","games:play","session:read","sandbox:play"],"expiresIn":3600}' \
  https://monmonmon.art/api/agent/tokens
```

Keep access_token secret. Send it only in Authorization: Bearer YOUR_TOKEN, never in a URL. Without explicit scopes, issuance grants only games:read and session:read. DELETE /api/agent/tokens with the bearer header revokes that token. Invalid, expired or revoked tokens return 401. Missing permission returns 403 with WWW-Authenticate describing the required scope. A supplied token never falls back to a more powerful cookie.

Tokens retain the issuer’s game-session identity but do not copy wallet verification. They cannot verify wallets or load owned cards. They can play already-existing matches belonging to that session. Issuing another token never grants extra daily game slots.

## Intent endpoints

These higher-level actions use the same private match views, revision checks and shared quota as /api/agent/duel. Each endpoint supports GET for its instructions and POST to act. Use a cookie session or a bearer token with games:play. Only start a live game when the user asks to play.

- POST /api/agent/intents/start-game with {} prepares the default five free cards and starts a solo game in one call. Optional mode, name and cards override those defaults. Idempotency-Key is required.
- POST /api/agent/intents/join-game with {"id":"MATCH_ID"} prepares the default free team and joins the waiting challenge. Optional name and cards are supported.
- POST /api/agent/intents/play-turn with id, revision and cardId plays a defense. Include emotion to submit an attack.
- POST /api/agent/intents/continue-game with id and revision acknowledges the revealed round.

## Isolated sandbox

GET /api/agent/sandbox describes the environment. POST {"action":"create"} starts a free-card solo simulation using the actual game engine and a deterministic Monster. Cookie sessions work; bearer tokens require sandbox:play.

The response contains environment: sandbox, game, state and expiresAt. For a move, POST action: move, the returned state, the game revision and the usual move object. POST action: inspect with state to inspect it. Send the same session cookie or scoped token. State expires after 15 minutes and can be replayed to explore another choice. It is encrypted, session-bound and unusable in the production game API.

Sandbox games make no game-database writes, do not appear on leaderboards and do not consume the 50 live-game admissions. The request-rate guard still applies. Sandbox supports free-card solo play only; it does not simulate wallet signatures, collectible ownership or friend sessions. Cookie-based simulation does not need database access; bearer authentication still reads its credential record.

## Consequences and request tracing

The OpenAPI specification labels consequential operations with x-consequence. Starting or joining a real game may consume an admission; card use, reveals and published results cannot be undone. Token issuance grants the requested permissions, revocation removes them, and session updates affect private context. Sandbox changes affect only returned simulation state.

Every API response includes X-Request-ID. Gameplay, intent, session, token and sandbox requests emit structured application logs with the matching ID, pseudonymous session ID, method, route, status and timing. Token requests include a non-secret token fingerprint. Logs omit authorization headers, cookies, bodies, private session IDs and wallet signatures. Give the request ID to the site owner for troubleshooting. Log access and retention follow the hosting account’s settings; no public audit-log endpoint is exposed.

## Start a free Hidden Feelings match

These commands create a real solo match. Run them when the user asks to play. Use a different cookie file for each player. Requires curl and jq.

```sh
curl -sS -c monmon.cookies https://monmonmon.art/api/agent/duel
curl -sS https://monmonmon.art/api/agent/cards \
  | jq '{action:"create", mode:"solo", name:"Guest", cards:.defaultTeam}' > team.json
curl -sS -b monmon.cookies -c monmon.cookies \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: my-first-match-20260920' \
  --data-binary @team.json https://monmonmon.art/api/agent/duel
```

The response contains game.id, game.revision, game.phase, game.seat, game.attacker, game.hand and game.scores. Open /play/{id} using the same session to see the board. The server chooses the first attacker randomly in solo play, so the first response may already be waiting for your defense.

## Choose legal moves

Read GET /api/agent/duel?id={id} before deciding. Preserve the cookie. If phase is attack and attacker equals your seat, POST an attack with a card id from hand and one allowed emotion. If phase is defend and attacker is the other seat, POST a defend with a remaining card. Do not infer a pending secret feeling from information absent from the player view.

```json
{"action":"move","id":"MATCH_ID","revision":0,"move":{"type":"attack","cardId":"CARD_ID","emotion":"smile"}}
```

```json
{"action":"move","id":"MATCH_ID","revision":1,"move":{"type":"defend","cardId":"CARD_ID"}}
```

After a reveal, both players send next. The solo Monster acknowledges automatically. A finished match can already be recorded during the final reveal; next transitions the phase to complete.

```json
{"action":"move","id":"MATCH_ID","revision":2,"move":{"type":"next"}}
```

Always replace the illustrative revisions with the latest response. A stale revision returns 409. Read again and reconsider the move. Agent creates require an Idempotency-Key header of 8–128 letters, numbers, hyphens or underscores. Establish and preserve the session cookie before creating. Choose a new key for each new match; reuse the same key and settings when retrying a network failure. The retry returns the existing game without spending another slot. Reusing a key with changed settings returns 409. Rejoining the same match also does not spend another slot.

Daily admission responses include X-Agent-Games-Limit, X-Agent-Games-Remaining and X-Agent-Games-Reset (Unix seconds), with scope site-wide. Exhausted admissions return HTTP 429, code AGENT_DAILY_GAME_LIMIT, resetsAt (Unix milliseconds), and Retry-After. Stop starting new matches until then. These headers describe game admissions; the X-RateLimit headers describe HTTP request bursts. Replayed creates and already-admitted game moves do not refresh daily counter headers.

## Friends and owned cards

Create with mode friend to get a waiting challenge. The friend uses a separate session and posts action join, id, cards and name. A challenge accepts only two players and expires seven days after creation. Joining or reading a started match from a different session does not grant a seat.

For owned cards: POST action challenge with the wallet address. The wallet owner signs the exact returned message using Ed25519. POST action verify with proof and the base64-encoded 64-byte signature, in the same session. Then POST action cards with a page number to load owned cards. This is a message signature, not a transaction. The server rechecks ownership and card values on team submission.

Use source borrowed for free previews and owned for verified wallet cards. These are internal API enum values; user-facing copy calls them free cards and digital collectibles.

## Errors and origins

Errors are JSON objects with an error string. Expect 400 for invalid input, 401 for missing wallet verification, 403 for a wrong player or origin, 404 for a missing challenge, 409 for stale or conflicting state, 410 for an expired challenge, 429 for throttling and 503 for unavailable services. Same-origin browser calls and server clients without an Origin header are supported. Cross-origin browser gameplay is rejected. Do not disable this check to connect an agent.

The original game does not expose a supported agent gameplay API. Its legacy write endpoints reject ordinary server clients and direct them to the metered agent endpoint. Its supported entry points are /gameboard, /gametime and /gameboardmulti. Read [the agent guide](/docs/agents) for MCP and WebMCP access.

Updated: September 20, 2026.

Agent sessions stay marked when using the regular Hidden Feelings endpoint; switching URLs does not remove the quota. Server clients without same-origin browser fetch metadata are also metered there. HTTP metadata is not proof of humanity: automation impersonating browser traffic requires separate bot verification. This game allowance is not a total cap on public read requests.
