> For the complete documentation index, see [llms.txt](https://sdk.metamarket.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sdk.metamarket.tech/evaluate-and-integrate/examples.md).

# Worked Examples

Concrete request-and-response pairs for common mechanics, using the real envelope, the real endpoints, and action ids drawn from the platform's own sample games.

Conventions used throughout:

* **Requests are raw HTTP.** The .NET SDK wraps each one as an `IEventClient`, `IPoolItemClient`, or `IQuestClient` call.
* **Headers are elided.** The blocks focus on the body, but every non-auth call also carries `X-App-Id` and `Authorization: Bearer <player JWT>`. The listing example below shows them in full.
* **Check `skipped` before treating an action as done.** A non-zero `skipped` means an action was not accepted, which is a failure. See [Sending Actions](/evaluate-and-integrate/events-and-directives.md#treat-a-non-zero-skipped-as-a-failure).
* **A success returns the four-channel envelope**: a `data` result plus `effects`, `mutations`, and `directives`.
* **A rejection returns an RFC 7807 problem document**, as the quota example shows.

One example is different. The starter package's `process_pending_packages` is fired by a session-start directive whose `skipped` the caller never sees, so there the client confirms the grant from authoritative state rather than from a count.

One shape to know up front. An `effects` entry is `{ entityId, key, kind, value }`, where `kind` is `absolute` (overwrite the client's value) or `delta` (add to it). A `mutations` entry carries the full current state of an entity that changed, in the same shape that entity has when you read it.

## Daily login quest

A quest set to renew each day. The player accepts it once; a fresh instance re-bakes at the same address after it completes.

Accept it with the universal `use` verb on the quest's pool item:

```
POST /api/slots/{slotId}/items/daily-login/use
X-App-Id: your-app-id
Authorization: Bearer <player JWT>
```

Response (the quest's own state; trimmed to the relevant fields):

```json
{
  "data": {
    "contentType": "quest",
    "itemSlug": "daily-login",
    "status": "active",
    "tasks": [{ "taskSlug": "log-in", "status": "active", "progress": { "current": 0, "target": 1 } }]
  },
  "effects": [],
  "mutations": [],
  "directives": []
}
```

The task completes when its condition is met, for a login quest, an `open_app` action fired at session start, and claiming it pays the reward. The next day, renewal prepares a fresh instance at the same `(slotId, itemSlug)` handle, so a client that bookmarked the address keeps working. Accepting an already-active quest returns `400` with `code: business_logic`, not a second acceptance.

## Achievement on an absolute goal

An achievement is a quest that watches a stat cross a threshold. This uses the sample pet game's `care` action, which writes a `growth` resource; the quest completes when `growth` reaches its target.

Report the activity:

```http
POST /api/events/track
[{ "playerId": "…", "actionType": "care", "value": 10, "clientKey": "care-1a2b", "timestamp": "2026-07-16T10:00:00Z", "entityId": "pet-a" }]
```

Response:

```json
{
  "data": { "processed": 1, "skipped": 0 },
  "effects": [{ "entityId": "pet-a", "key": "growth", "kind": "absolute", "value": 40 }],
  "mutations": [],
  "directives": []
}
```

State before: `growth = 30`. State after: `growth = 40`, echoed as an absolute in `effects`. On the quest's next evaluation the absolute check passes and its task becomes claimable; claiming it through `POST /api/slots/{slotId}/items/{itemSlug}/tasks/{taskSlug}/claim` returns the quest's new state and the reward's touched values.

## Multi-step quest

A staged chain: three tasks, each unlocking once the previous is claimed, each checked against a higher `growth` threshold. The player accepts once; the engine advances the stage after each claim.

A claim names the task being claimed:

```
POST /api/slots/{slotId}/items/grow-your-pet/tasks/stage-1/claim
```

Starting from a quest whose `stage-1` task is claimable, that claim returns the quest with the claimed stage and the next one now active:

```json
{
  "data": {
    "contentType": "quest",
    "itemSlug": "grow-your-pet",
    "status": "active",
    "tasks": [
      { "taskSlug": "stage-1", "status": "claimed", "progress": { "current": 20, "target": 20 } },
      { "taskSlug": "stage-2", "status": "active",  "progress": { "current": 20, "target": 45 } }
    ]
  },
  "effects": [{ "entityId": "pet-a", "key": "growth", "kind": "absolute", "value": 20 }],
  "mutations": [],
  "directives": []
}
```

## Daily energy quota

The sample pet game caps `feed` at four uses a day. `feed` is a pool item with a quota that refills at the UTC day boundary, so it is called with the `use` verb, not the raw action endpoint.

```
POST /api/slots/{slotId}/items/feed/use
X-App-Id: your-app-id
Authorization: Bearer <player JWT>
```

A successful feed (three charges left afterward) settles the pet's vitals and returns them:

```json
{
  "data": { "contentType": "package", "itemSlug": "feed", "bundle": { "quantity": 3, "maxQuantity": 4, "regenType": "daily" } },
  "effects": [
    { "entityId": "pet-a", "key": "hunger", "kind": "absolute", "value": 82 },
    { "entityId": "pet-a", "key": "mood",   "kind": "absolute", "value": 91 }
  ],
  "mutations": [],
  "directives": []
}
```

`hunger` is written directly; `mood` is derived from it and returned in the same channel. The fifth `feed` on the same UTC day has one deterministic outcome, a rejection, because the quota is spent and has not crossed a day boundary to refill:

```json
{
  "title": "Bundle exhausted for item 'feed'.",
  "status": 400,
  "code": "business_logic",
  "traceId": "00-...",
  "instance": "POST /api/bundles/{id}/use"
}
```

The quota refills to four (not higher) at the next UTC midnight. A retry of the same call before then returns the same `400`, so do not retry it: the rejection consumes nothing, but the outcome will not change until the quota refills. Wait for the refill, or change the state some other way, before trying again.

## Starter reward package

**Configuration.** The app's `init_package_ref` grants a starter package at enrollment in `Manual` mode with `Mark{open}`. `start_session_directives` declares one `track_actions` directive naming `process_pending_packages`.

**Flow.** No client open call is needed. `POST /api/session/start` returns that directive, and running it fires `process_pending_packages` on the no-receipt `/track` lane. Because the package is marked to open, that action opens it and its contents come back in the touch's `effects`. This lane does not emit a `reveal` directive: reveal is gated to the `/execute` lane, so a box opened by the session-start touch is surfaced through `effects` and the resulting state, not a reveal card. The player sees the reward already applied on first login, with no open button. Because the open runs through a directive rather than a call you make yourself, `ProcessDirectivesAsync` does not surface the follow-up action's `skipped` count to the caller. This flow therefore demonstrates the grant-and-open mechanism; it does not by itself establish that the action was accepted. To confirm the grant landed, the client reads authoritative state rather than the directive's result:

```
GET /api/player/state
```

and checks the starter items are present (they also arrive in the touch's `effects`). That `process_pending_packages` is declared on the right lane is what a test integration verifies (see [App Configuration](/evaluate-and-integrate/app-configuration.md) and [SDK Guide](/evaluate-and-integrate/sdk-guide.md)).

## Shared portfolio milestone

**Configuration.** The platform cannot see the pet's `growth` resource; local state never leaves the game's own tenant. The chain is declared instead. The game's configuration attaches a milestone reward to the package that the final evolution fulfils. When that package is delivered, the engine emits the milestone key under the game's namespace, and a platform-owned quest watching that key grants GG on the shared balance. The key is agreed at onboarding and the amount is set on the platform side, not by the game.

**Starting state.** `growth = 95`. The `care` action below adds 5, bringing it to 100.

```http
POST /api/events/track
[{ "playerId": "…", "actionType": "care", "value": 5, "clientKey": "care-final", "timestamp": "2026-07-16T10:10:00Z", "entityId": "pet-a" }]
```

The game's own response reflects only its local change:

```json
{
  "data": { "processed": 1, "skipped": 0 },
  "effects": [{ "entityId": "pet-a", "key": "growth", "kind": "absolute", "value": 100 }],
  "mutations": [],
  "directives": []
}
```

`growth` reaching 100 is the milestone this integration reports, and it is the part an integrator can see and verify: the local `effects` above. What follows is platform-side. A platform-owned bridge quest reacts to the milestone and may grant GG on the shared balance, but that grant never appears in the game's response, because a game cannot read or move GG, and it is confirmed on platform-operated surfaces rather than by the integrator. The example is here for the boundary, not for an observable GG figure: the game produces the trigger, and the platform owns whatever it pays out. The player's loyalty tier is a separate platform mechanic and is not changed by this action.

## Player listing and purchase

A player lists an item they own; another player in the same game buys it.

**Configuration.** The app declares a `list_item` action (a synchronous, `/execute` action) that creates a listing of one product type; the action's `value` is the seller's asking price.

**Create the listing (seller).** The seller sends the `list_item` action. The engine moves the product out of the seller's balance and holds it against the new listing, so it cannot be listed twice; if the seller does not actually hold the product, the escrow fails, nothing is listed, and the action comes back counted in `skipped` on an HTTP 200 rather than as a `400`.

```http
POST /api/events/execute
[{ "playerId": "seller-id", "actionType": "list_item", "value": 160, "clientKey": "list-42", "timestamp": "2026-07-16T09:00:00Z" }]
```

The `/execute` result confirms the action was accepted, not just transported:

```json
{
  "data": { "processed": 1, "skipped": 0, "receipts": [{ "action_type": "list_item", "items": [] }] },
  "effects": [],
  "mutations": [],
  "directives": []
}
```

`skipped` is `0`; a non-zero `skipped` here most often means the escrow or a rule failed, and can also mean `list_item` is undeclared or reached the wrong lane, and no listing would have been created. The engine creates the listing as a `listing` pool item with its own stable `itemSlug` (shown here as `listing-42`), priced at 160 coins, with `sellerPlayerId` set to the seller. The `clientKey` on the create call is the seller's idempotency token, not the listing's address. A buyer discovers the listing by scanning the game's shared listing slot and addresses it by its `(slotId, itemSlug)`; the discovery slot and its contents are part of the marketplace configuration set up with the team, not a self-serve typed surface today.

**Buy the listing (buyer).** Starting state: the buyer holds 500 coins. The buyer purchases with the `use` verb on the listing:

```
POST /api/slots/{slotId}/items/listing-42/use
X-App-Id: your-app-id
Authorization: Bearer <buyer's player JWT>
```

Response (a listing carries no separate "sold" flag; it is sold when its one charge is spent):

```json
{
  "data": {
    "contentType": "listing",
    "itemSlug": "listing-42",
    "sellerPlayerId": "7c2e…",
    "bundle": { "quantity": 0, "maxQuantity": 1, "regenType": null }
  },
  "effects": [{ "entityId": "buyer-player-id", "key": "coins", "kind": "absolute", "value": 340 }],
  "mutations": [],
  "directives": []
}
```

The buyer's `coins` go from 500 to 340 (the 160 price), echoed as the one `effects` entry. The item moving to the buyer and the 160 credited to the seller are settled server-side but are not echoed in the buyer's own response; the buyer reads the new item from their inventory, and the seller sees the credit in their own state. A second buyer calling `use` on the spent listing gets `400 business_logic`, because the item and the listing's single charge move together. Buyer and seller share the same `X-App-Id`; there is no cross-game purchase.
