---
name: set-rolling-a-character
description: How to roll a character with the Set World API. Use when generating a hero, NPC or enemy with a class, nine stats, traits, 15 equipped items, 23 derived attributes and a power rating, when reading the full character schema correctly, when spending orbs to improve a character's equipment or stats, when saving a permanent birth certificate page, or when designing a game's own character record on top of it.
license: Free to use when obtained from set.world, commercially too (the API, what it returns, the skills and downloaded models). The source code of Set is © Jimmy Lee under CC BY-NC 4.0 and its commercial use needs a paid waiver.
metadata:
  version: "0.8.9"
  title: Rolling a character
---

# Rolling a character

One call returns a complete, render-ready character. Heroes, NPCs and monsters all come from the same call; nothing in the response depends on how many characters you make.

```sh
curl https://set.world/api/roll/character                              # random
curl 'https://set.world/api/roll/character?seed=2026'                  # reproducible
curl 'https://set.world/api/roll/character?seed=2026&equipmentBonus=1' # opt in to the gear stat bonus
```

| Parameter | Meaning |
| --- | --- |
| `seed` | Optional uint32. The same seed and `contentVersion` return the same character |
| `equipmentBonus` | Optional `1`, `0`, `true` or `false`. Off by default ([Slots](https://set.world/skills/equipment-slots)) |

## The character schema

Shared types have one owner each: `Item`, `Equipment` and `SlotBreakdown` in [Rolling an item](https://set.world/skills/rolling-an-item); `Slot` in [Slots](https://set.world/skills/equipment-slots); `StatBlock` in [Stats](https://set.world/skills/character-stats); `TraitImpact` in [Calculations](https://set.world/skills/calculations).

```ts
type Character = {
  seed: number | null;
  contentVersion: string;
  class: { name: string; flavor: string; mainhand: string };
  stats: StatBlock & { level: 1; skills: number; advantages: number; disadvantages: number };   // the roll, with the trait counts
  modifiedStats: StatBlock;        // after traits
  equipmentBonus: boolean;
  equipmentBonuses: StatBlock;     // all zero unless equipmentBonus was requested
  finalStats: StatBlock;           // the block to play with
  equipment: Equipment;            // 15 Items
  powerRating: number;
  powerRatingMax: number;
  equipmentOrbValue: number;
  equipmentOrbBreakdown: SlotBreakdown[];   // 13 rows, canonical slot order
  traits: {
    skills: Array<{ id: number; name: string; flavor: string; index: number }>;          // 1 to 4
    advantages: Array<{ id: number; name: string; index: number; impact: TraitImpact }> | null;      // null when the count is 0
    disadvantages: Array<{ id: number; name: string; index: number; impact: TraitImpact }> | null;
  };
  attributes: Record<string, number>;   // the 23 derived attributes, from finalStats
};
```

A full response, computed from the current tables and formulas. The character is built from named table entries rather than a seed, so `seed` is null. Parts that another skill shows in full are shortened here and marked.

```jsonc
{
  "seed": null,
  "contentVersion": "4.swpi78HUCtcrZ1Eoi9dipw",
  "class": { "name": "Adventurer", "flavor": "The Adventurer arrived upon these shores seeking a tale; what was found is a story that permits no departure", "mainhand": "sword" },
  "stats": {
    "level": 1,
    "strength": 16,
    "dexterity": 14,
    "intelligence": 12,
    "wisdom": 11,
    "agility": 15,
    "vitality": 18,
    "perception": 13,
    "resolve": 12,
    "luck": 9,
    "skills": 2,
    "advantages": 1,
    "disadvantages": 1
  },
  "modifiedStats": { "strength": 16, "dexterity": 15, "intelligence": 12, "wisdom": 11, "agility": 15, "vitality": 18, "perception": 12, "resolve": 13, "luck": 9 },
  "equipmentBonus": false,
  "equipmentBonuses": { "strength": 0, "dexterity": 0, "intelligence": 0, "wisdom": 0, "agility": 0, "vitality": 0, "perception": 0, "resolve": 0, "luck": 0 },
  "finalStats": { "strength": 16, "dexterity": 15, "intelligence": 12, "wisdom": 11, "agility": 15, "vitality": 18, "perception": 12, "resolve": 13, "luck": 9 },
  "equipment": {
    "head": { "name": "leather helm", "slot": "head", "tierGrade": "C", "orbValue": 125 /* … the rest of the Item */ },
    "tool": { "name": "fine burning steel sword of the dawn", "slot": "tool", "tierGrade": "C", "orbValue": 125 /* … the rest of the Item */ },
    "offhand": { "name": "iron dagger", "slot": "offhand", "tierGrade": "D", "orbValue": 25 /* … the rest of the Item */ },
    "back": { "name": "wool cloak", "slot": "back", "tierGrade": "F", "orbValue": 1 /* … the rest of the Item */ },
    "chest": { "name": "silk tunic", "slot": "chest", "tierGrade": "C", "orbValue": 125 /* … the rest of the Item */ },
    "hand": [
      { "name": "leather glove", "slot": "hand", "tierGrade": "C", "orbValue": 125 /* … the rest of the Item */ },
      { "name": "cotton wraps", "slot": "hand", "tierGrade": "F", "orbValue": 1 /* … the rest of the Item */ }
    ],
    "finger": [
      { "name": "gold ring", "slot": "finger", "tierGrade": "B", "orbValue": 625 /* … the rest of the Item */ },
      { "name": "stone signet", "slot": "finger", "tierGrade": "E", "orbValue": 5 /* … the rest of the Item */ }
    ],
    "feet": { "name": "bone boots", "slot": "feet", "tierGrade": "E", "orbValue": 5 /* … the rest of the Item */ },
    "legs": { "name": "cotton pants", "slot": "legs", "tierGrade": "F", "orbValue": 1 /* … the rest of the Item */ },
    "neck": { "name": "copper amulet", "slot": "neck", "tierGrade": "E", "orbValue": 5 /* … the rest of the Item */ },
    "shoulders": { "name": "cotton mantle", "slot": "shoulders", "tierGrade": "F", "orbValue": 1 /* … the rest of the Item */ },
    "waist": { "name": "linen belt", "slot": "waist", "tierGrade": "D", "orbValue": 25 /* … the rest of the Item */ },
    "wrist": { "name": "bronze bracer", "slot": "wrist", "tierGrade": "D", "orbValue": 25 /* … the rest of the Item */ }
  },
  "powerRating": 1108,
  "powerRatingMax": 1808,
  "equipmentOrbValue": 1219,
  "equipmentOrbBreakdown": [/* 13 SlotBreakdown rows: tool, offhand, head, neck, back, shoulders, chest, waist, legs, feet, wrist, hand, finger */],
  "traits": {
    "skills": [
      { "id": 0, "name": "acrobatics", "flavor": "To move as one unbound by weight, for the body becomes an instrument of grace beyond the common measure", "index": 0 },
      {
        "id": 3,
        "name": "alchemy",
        "flavor": "To discern the hidden virtue within base matter and draw it forth; let the practitioner remember that transformation begins with understanding",
        "index": 3
      }
    ],
    "advantages": [
      {
        "id": 1,
        "name": "unyielding in the face of adversity",
        "index": 1,
        "impact": {
          "name": "unyielding in the face of adversity",
          "kind": "advantage",
          "tier": 2,
          "primary": "dexterity",
          "primaryDelta": 0.1,
          "secondary": "resolve",
          "secondaryDelta": 0.05
        }
      }
    ],
    "disadvantages": [
      {
        "id": 1,
        "name": "a mind that teeters on the brink of madness",
        "index": 1,
        "impact": { "name": "a mind that teeters on the brink of madness", "kind": "disadvantage", "tier": 1, "primary": "perception", "primaryDelta": -0.08 }
      }
    ]
  },
  "attributes": { "maxHealth": 184, "stamina": 96, "physicalDamage": 25.5 /* … all 23, shown in full in Calculations */ }
}
```

In this example the traits change these stats: dexterity 14 → 15, perception 13 → 12, resolve 12 → 13.

## Read it once, correctly

- **Use `finalStats` and `attributes` as returned.** Traits are already applied. Never apply a trait or the equipment bonus a second time.
- **`level` is a display default.** Set grants no stat points and has no level curve. Levels and progression belong to your game.
- **Equipment has no resolved effects.** Each item carries a grade, an orb value and advisory `affects`.
- **The class follows the mainhand:** `class.mainhand === equipment.tool.props[3].name`.
- **Health and stamina** start at `attributes.maxHealth` and `attributes.stamina`. Current values are your game's state.
- **A seed is not an identity.** Give each character your own ID and keep the full response beside your game's state.

Each part has one owner: [Stats](https://set.world/skills/character-stats), [Skills](https://set.world/skills/character-skills), [Classes](https://set.world/skills/character-classes), [Advantages](https://set.world/skills/character-advantages), [Disadvantages](https://set.world/skills/character-disadvantages), [Slots](https://set.world/skills/equipment-slots), and [Calculations](https://set.world/skills/calculations) for `attributes`, `powerRating` and how traits change stats.

## Spend orbs for a better character

`/api/roll/character` takes no orbs. Every character is an ordinary roll, and so are its 15 items; `equipmentOrbValue` (1,219 in the example above) is what that starting kit would cost at guarantee prices. A character gets better when your game spends orbs on it afterwards. Set prices each way; your game tracks the orbs and the result.

| Spend orbs on | Call | What changes | Where the result lives |
| --- | --- | --- | --- |
| Better equipment | `/api/craft?orbs=N` or `/api/craft?tier=T` | A new item of a better grade. A craft returns whichever slot it rolls, so equip it when it beats what that slot holds and keep or salvage the rest | Your equipped items and inventory |
| Reforging what it wears | None: Set's own recipe, run in your client | Each worn item keeps its slot and base type and redraws its material and words by the lottery's rule; it never loses grade | [Derived stats](https://set.world/skills/anima-derived-stats) |
| Higher stats | `/api/boost/cost?from=&to=` | The price of raising one stat: vitality from 18 to 20 costs 3,059 orbs. Then recompute with `/api/attributes` and `/api/power` | Your current stat block |

- More orbs per craft reach higher grades; the odds are in [Crafting an item](https://set.world/skills/crafting-an-item). How often a craft lands in each slot is in [Slots](https://set.world/skills/equipment-slots).
- To let graded gear raise stats by Set's own rule, roll with `equipmentBonus=1`.
- Never write an upgrade into the birth. Keep it in your `GameCharacter` below.

**Orbs are your game's currency, not Set's.** Set has no wallet and no rule for how a character earns orbs. Your game decides what an orb is worth in its own economy (a kill, a quest, ten logs of wood, a gold coin), keeps the balance, and sends only the spend. The bridge from your resources to orbs is in [Crafting an item](https://set.world/skills/crafting-an-item).

```js
const better = (a, b) => (a.tier !== b.tier ? a.tier > b.tier : a.score > b.score);

async function outfit(hero, wallet, orbsPerCraft, crafts, seed) {       // hero: your GameCharacter, with its equipped Items
  for (let i = 0; i < crafts && wallet.orbs >= orbsPerCraft; i++) {
    const craft = await (await fetch(`https://set.world/api/craft?orbs=${orbsPerCraft}&seed=${deriveSeed(seed, i)}`)).json();
    wallet.orbs -= craft.orbs;
    const worn = [hero.equipped[craft.item.slot]].flat();               // hand and finger hold two
    const weakest = worn.reduce((low, item) => (better(low, item) ? item : low));
    if (better(craft.item, weakest)) hero.replace(weakest, craft.item); else wallet.items.push(craft.item);
  }
}
```

`deriveSeed` is in [Rolling a group](https://set.world/skills/rolling-a-group); `hero.replace` is your own inventory rule.

## Save a birth certificate

The API stores nothing. The page route `/roll/character` saves one character exactly as rolled (equipment bonus off), encrypted, and redirects to its permanent sheet:

```text
https://set.world/roll/character?seed=2026
→ https://set.world/roll/character/<uuid>
```

| Parameter | Meaning |
| --- | --- |
| `seed` | Optional uint32 |
| `contentVersion` | Optional. Must equal the server's current version, or the route answers 409 |
| `name` | Optional UUIDv4 display name, created when omitted. It is separate from the row ID in the URL |

Creation answers 400 for an invalid seed or name and 503 when storage is unavailable. The stored page answers 400 for an invalid ID, 404 for a missing record, 503 when storage is unavailable and 500 for unreadable data. A birth is never updated: no levels, inventories or progression are written back.

## A schema for your game's character

Everything a character becomes lives in your database, beside the birth:

```ts
type GameCharacter = {
  id: string;                          // your own ID
  birth: Character;                    // the full response, never edited
  birthUrl: string | null;             // https://set.world/roll/character/<uuid>, if you saved one
  level: number;                       // yours: Set has no level curve
  experience: number;
  stats: StatBlock;                    // your current block; starts as birth.finalStats
  health: number;                      // starts at birth.attributes.maxHealth
  stamina: number;                     // starts at birth.attributes.stamina
  orbs: number;                        // your wallet: Set has none
  inventory: string[];                 // IDs of your InventoryItem records
  equipped: Partial<Record<Slot, string | [string, string]>>;
  effects: Array<{ name: string; remainingMs: number; stacks: number }>;
};
```

When `stats` changes, recompute the attributes and the rating with `/api/attributes` and `/api/power`, which accept stats from 8 to 24.

## Related

Many characters from one seed: [Rolling a group](https://set.world/skills/rolling-a-group). A body: [Character models](https://set.world/skills/anima-character-models). A fight without your own rules: [Combat](https://set.world/skills/basic-lightweight-combat), experimental and optional.
