> For the complete documentation index, see [llms.txt](https://docs.idosgames.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.idosgames.com/api/api-v2/character.md).

# Character

### **Overview**

Base URL: `https://api.idosgames.com`

HTTP Method: `POST`

Route: `/api/v2/{TitleTemplateId}/{TitleId}/Client/Character/{Action}/{UserID}`

Documentation: [**LiveOps Configuration & Logic**](https://docs.idosgames.com/liveops/character)

**Actions (CharacterAction):**

* `GetCharacterDefinitions`
* `GetUserCharacters`
* `UpgradeStatLevel`
* `UpgradeCharacterLevel`
* `EquipItems`
* `UnequipItems`
* `UnequipAllCharacters`

### Authentication

#### Required Headers

* `Authorization: Bearer <SessionTicket>`
* `Content-Type: application/json`

#### Unauthorized (401)

Returned when:

* `Authorization` header is missing
* Bearer token is missing/invalid
* Session validation failed

### Response Envelope (Server Standard)

All responses are wrapped with `OperationResult<T>`:

#### Success

```json
{
  "Success": true,
  "Error": null,
  "Data": { }
}
```

#### Failure

```json
{
  "Success": false,
  "Error": "Some error message",
  "Data": null
}
```

#### SuccessResponse

Used by operations like Equip/Unequip:

```json
{
  "IsCompleted": true,
  "ServerTime": "2026-02-09T12:34:56.789Z"
}
```

***

### Quick Reference (Action Table)

|                                                   Action | Request Body Fields               | Response Data Type                                                                                                |
| -------------------------------------------------------: | --------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| [GetCharacterDefinitions](#id-1-getcharacterdefinitions) | *(none)*                          | [`CharacterDefinitions`](#response-operationresult-less-than-characterdefinitions-greater-than)                   |
|             [GetUserCharacters](#id-2-getusercharacters) | *(none)*                          | [`GetCharactersResponse`](#response-operationresult-less-than-getcharactersresponse-greater-than)                 |
|               [UpgradeStatLevel](#id-3-upgradestatlevel) | `CharacterID`, `StatID`           | [`UpgradeStatLevelResponse`](#response-operationresult-less-than-upgradestatlevelresponse-greater-than)           |
|     [UpgradeCharacterLevel](#id-4-upgradecharacterlevel) | `CharacterID`                     | [`UpgradeCharacterLevelResponse`](#response-operationresult-less-than-upgradecharacterlevelresponse-greater-than) |
|                           [EquipItems](#id-5-equipitems) | `CharacterID`, `ItemsToEquip[]`   | [`SuccessResponse`](#response-operationresult-less-than-successresponse-greater-than)                             |
|                       [UnequipItems](#id-6-unequipitems) | `CharacterID`, `UnequipSlotIDs[]` | [`SuccessResponse`](#response-operationresult-less-than-successresponse-greater-than-1)                           |
|       [UnequipAllCharacters](#id-7-unequipallcharacters) | *(none)*                          | [`SuccessResponse`](#response-operationresult-less-than-successresponse-greater-than-2)                           |

> Request body can be `{}` for actions without arguments.

***

## Endpoints

### 1) GetCharacterDefinitions

Returns `CharacterDefinitions`.

#### Request

**Route** `POST /api/v2/{TitleTemplateId}/{TitleId}/Client/Character/GetCharacterDefinitions/{UserID}`

**Body**

```json
{}
```

#### Response (OperationResult\<CharacterDefinitions>)

```json
{
  "Success": true,
  "Error": null,
  "Data": {
    "AllowedCharacterIDs": ["main", "hero_1"],
    "AllowedEquipmentSlotIDs": ["Weapon", "Helmet"],

    "StatDefinitions": [
      {
        "StatID": "Power",
        "MaxLevel": 100,
        "Weight": 10,
        "BaseCostResource": {
          "Type": "VirtualCurrency",
          "CurrencyID": "CO",
          "Amount": 1
        },
        "BaseCost": 50,
        "CostScalingFactor": 1.08,
        "BaseStatValue": 10,
        "StatScalingFactor": 1.05,
        "Requirements": [
          { "RequiredStatID": "HP", "RequiredLevel": 5 }
        ],
        "DisplayName": "Power",
        "IconPath": "icons/power.png",
        "Description": "Increases damage."
      }
    ],

    "CustomStatDefinitions": {
      "hero_1": [
        {
          "StatID": "Power",
          "MaxLevel": 150,
          "BaseCost": 75
        }
      ]
    },

    "LevelDefinitions": [
      {
        "Level": 1,
        "UpgradeCost": [],
        "GlobalStatMultiplier": 1.0,
        "StatMaxLevelMultiplier": 1.0
      },
      {
        "Level": 2,
        "UpgradeCost": [
          { "Type": "Item", "ItemID": "hero_1_shard", "Amount": 10 }
        ],
        "GlobalStatMultiplier": 1.1,
        "StatMaxLevelMultiplier": 1.25
      }
    ],

    "CustomLevelDefinitions": {
      "hero_1": [
        {
          "Level": 2,
          "UpgradeCost": [
            { "Type": "VirtualCurrency", "CurrencyID": "CO", "Amount": 200 }
          ],
          "GlobalStatMultiplier": 1.15,
          "StatMaxLevelMultiplier": 1.5
        }
      ]
    }
  }
}
```

#### Common Errors

```json
{ "Success": false, "Error": "Character definitions not found.", "Data": null }
```

***

### 2) GetUserCharacters

Returns player characters dictionary.

#### Request

**Route** `POST /api/v2/{TitleTemplateId}/{TitleId}/Client/Character/GetUserCharacters/{UserID}`

**Body**

```json
{}
```

#### Response (OperationResult\<GetCharactersResponse>)

```json
{
  "Success": true,
  "Error": null,
  "Data": {
    "Characters": {
      "Main": {
        "CharacterID": "main",
        "Class": "Warrior",
        "Name": "Main Hero",
        "Level": 1,
        "Experience": 0,
        "Power": 120,
        "StatLevels": { "Power": 10, "HP": 5 },
        "Equipment": {
          "Weapon": {
            "SlotID": "Weapon",
            "CatalogVersion": "Items",
            "ItemID": "sword_01",
            "ItemInstanceID": "I_001",
            "EquippedAt": "2026-02-09T12:30:00Z"
          }
        },
        "UpdatedAt": "2026-02-09T12:34:56.789Z"
      }
    }
  }
}
```

#### Empty State

```json
{
  "Success": true,
  "Error": null,
  "Data": { "Characters": {} }
}
```

***

### 3) UpgradeStatLevel

Upgrades a stat by +1 and consumes required resource.

#### Request

**Route** `POST /api/v2/{TitleTemplateId}/{TitleId}/Client/Character/UpgradeStatLevel/{UserID}`

**Body**

```json
{
  "CharacterID": "main",
  "StatID": "Power"
}
```

#### Response (OperationResult\<UpgradeStatLevelResponse>)

```json
{
  "Success": true,
  "Error": null,
  "Data": {
    "StatID": "Power",
    "StatLevel": 11,
    "ConsumedResource": {
      "Type": "VirtualCurrency",
      "CurrencyID": "CO",
      "Amount": 120
    },
    "ConsumedResourceBalance": 880
  }
}
```

#### Common Errors

**Missing StatID**

```json
{ "Success": false, "Error": "StatId is required", "Data": null }
```

**Stat Not Found**

```json
{ "Success": false, "Error": "Stat 'Power' not found in configuration for character 'main'.", "Data": null }
```

**Character Locked**

```json
{ "Success": false, "Error": "Character 'hero_1' is locked. Unlock it first.", "Data": null }
```

**Requirements Not Met**

```json
{ "Success": false, "Error": "The required stat did not reach the desired level.", "Data": null }
```

**Max Reached**

```json
{
  "Success": false,
  "Error": "Already at maximum level (100/100). Upgrade Character Rank to increase limit.",
  "Data": null
}
```

**Fail / Not Enough Resource**

```json
{
  "Success": false,
  "Error": "Upgrade failed (not enough resource / race condition / requirements).",
  "Data": null
}
```

***

### 4) UpgradeCharacterLevel

Upgrades character rank/level by +1 (including unlock 0 → 1).

#### Request

**Route** `POST /api/v2/{TitleTemplateId}/{TitleId}/Client/Character/UpgradeCharacterLevel/{UserID}`

**Body**

```json
{
  "CharacterID": "hero_1"
}
```

#### Response (OperationResult\<UpgradeCharacterLevelResponse>)

```json
{
  "Success": true,
  "Error": null,
  "Data": {
    "CharacterID": "hero_1",
    "NewLevel": 1,
    "ConsumedResources": [
      { "Type": "Item", "ItemID": "hero_1_shard", "Amount": 10 }
    ]
  }
}
```

#### Common Errors

**Not Allowed**

```json
{ "Success": false, "Error": "Character 'hero_1' is not allowed.", "Data": null }
```

**Next Level Config Missing**

```json
{ "Success": false, "Error": "Max level reached or config for level 3 is missing.", "Data": null }
```

**Not Enough Currency**

```json
{ "Success": false, "Error": "Not enough currency: CO", "Data": null }
```

**Not Enough Items**

```json
{ "Success": false, "Error": "Not enough items: hero_1_shard", "Data": null }
```

**Resource Revocation Failed**

```json
{ "Success": false, "Error": "Resource revocation failed.", "Data": null }
```

**Database Error (Refunded)**

```json
{ "Success": false, "Error": "Database error. Resources refunded.", "Data": null }
```

***

### 5) EquipItems

Equips items to specified slots.

#### Request

**Route** `POST /api/v2/{TitleTemplateId}/{TitleId}/Client/Character/EquipItems/{UserID}`

**Body**

```json
{
  "CharacterID": "main",
  "ItemsToEquip": [
    { "SlotID": "Weapon", "ItemInstanceID": "I_001" },
    { "SlotID": "Helmet", "ItemInstanceID": "I_777" }
  ]
}
```

#### Response (OperationResult\<SuccessResponse>)

```json
{
  "Success": true,
  "Error": null,
  "Data": {
    "IsCompleted": true,
    "ServerTime": "2026-02-09T12:34:56.789Z"
  }
}
```

***

### 6) UnequipItems

Unequips items from specified slots.

#### Request

**Route** `POST /api/v2/{TitleTemplateId}/{TitleId}/Client/Character/UnequipItems/{UserID}`

**Body**

```json
{
  "CharacterID": "Main",
  "UnequipSlotIDs": ["Weapon", "Helmet"]
}
```

#### Response (OperationResult\<SuccessResponse>)

```json
{
  "Success": true,
  "Error": null,
  "Data": {
    "IsCompleted": true,
    "ServerTime": "2026-02-09T12:34:56.789Z"
  }
}
```

### 7) UnequipAllCharacters

Unequips items from **all characters** (clears equipment slots everywhere).

**Request**

**Route** `POST /api/v2/{TitleTemplateId}/{TitleId}/Client/Character/UnequipAllCharacters/{UserID}`

**Body**

```json
{}
```

#### Response (OperationResult\<SuccessResponse>)

```json
{
  "Success": true,
  "Error": null,
  "Data": {
    "IsCompleted": true,
    "ServerTime": "2026-02-09T12:34:56.789Z"
  }
}
```

***

## Models (Contracts)

### CharacterRequest

```json
{
  "CharacterID": "string",
  "StatID": "string",
  "ItemsToEquip": [
    { "SlotID": "string", "ItemInstanceID": "string" }
  ],
  "UnequipSlotIDs": ["string"]
}
```

### EquipSlotPair

```json
{
  "SlotID": "string",
  "ItemInstanceID": "string"
}
```

### GetCharactersResponse

```json
{
  "Characters": {
    "<CharacterID>": {
      "CharacterID": "string",
      "Class": "string",
      "Name": "string",
      "Level": 0,
      "Experience": 0,
      "Power": 0,
      "StatLevels": { "<StatID>": 0 },
      "Equipment": {
        "<SlotID>": {
          "SlotID": "string",
          "CatalogVersion": "string",
          "ItemID": "string",
          "ItemInstanceID": "string",
          "EquippedAt": "2026-02-09T12:34:56.789Z"
        }
      },
      "UpdatedAt": "2026-02-09T12:34:56.789Z"
    }
  }
}
```

### CharacterModel

Represents a single owned character (stored inside `GetCharactersResponse.Characters`).

```json
{
  "CharacterID": "string",
  "Class": "string",
  "Name": "string",
  "Level": 0,
  "Experience": 0,
  "Power": 0,
  "StatLevels": { "<StatID>": 0 },
  "Equipment": {
    "<SlotID>": {
      "SlotID": "string",
      "CatalogVersion": "string",
      "ItemID": "string",
      "ItemInstanceID": "string",
      "EquippedAt": "2026-02-09T12:34:56.789Z"
    }
  },
  "UpdatedAt": "2026-02-09T12:34:56.789Z"
}
```

### EquippedItem

Equipment entry in `CharacterModel.Equipment`.

```json
{
  "SlotID": "string",
  "CatalogVersion": "string",
  "ItemID": "string",
  "ItemInstanceID": "string",
  "EquippedAt": "2026-02-09T12:34:56.789Z"
}
```

### ItemOrCurrency

Universal representation of currency or item costs/rewards.

```json
{
  "Type": "Item",
  "Catalog": "Items", //CatalogVersion, CatalogName
  "Amount": 10,
  "ImagePath": "icons/items/hero_1_shard.png",
  "Name": "Hero Shard",
  "CurrencyID": null,
  "ItemID": "hero_1_shard"
}
```

#### ItemType

```json
"Item" | "VirtualCurrency"
```

**Notes**

* When `Type = "VirtualCurrency"`: use `CurrencyID`, `Amount`
* When `Type = "Item"`: use `Catalog`, `ItemID`, `Amount`
* `Name` and `ImagePath` are optional UI helpers (may be null)

### UpgradeStatLevelResponse

```json
{
  "StatID": "string",
  "StatLevel": 0,
  "ConsumedResource": { /* ItemOrCurrency */ },
  "ConsumedResourceBalance": 0
}
```

### UpgradeCharacterLevelResponse

```json
{
  "CharacterID": "string",
  "NewLevel": 0,
  "ConsumedResources": [
    { /* ItemOrCurrency */ }
  ]
}
```

### SuccessResponse

```json
{
  "IsCompleted": true,
  "ServerTime": "2026-02-09T12:34:56.789Z"
}
```

***

## Client Flow (Recommended)

### A) Bootstrap (Open Character Screen)

1. `GetCharacterDefinitions`
2. `GetUserCharacters`

### B) Unlock / Rank Up

1. `UpgradeCharacterLevel` (`CharacterID`)
2. `GetUserCharacters`

### C) Upgrade Stat

1. `UpgradeStatLevel` (`CharacterID`, `StatID`)
2. `GetUserCharacters`

### D) Equip / Unequip

1. `EquipItems` / `UnequipItems`
2. `GetUserCharacters`

***

## Config Usage Guide (Client)

### Stat Resolution (Custom → Global)

When rendering or calculating a stat:

1. If `CustomStatDefinitions` contains `CharacterID` and has matching `StatID`, use that `StatDefinition`.
2. Otherwise use the matching entry from `StatDefinitions`.

### Level Resolution (Custom → Global)

When resolving upgrade cost or multipliers for a character level:

1. Try `CustomLevelDefinitions[CharacterID]` for the target `Level`.
2. Otherwise use `LevelDefinitions`.

### Effective Max Stat Level

To display the true stat cap for a character:

* `BaseMaxLevel = StatDefinition.MaxLevel`
* `Multiplier = CharacterLevelDefinition.StatMaxLevelMultiplier` for current character `Level`
* `EffectiveMaxLevel = floor(BaseMaxLevel * Multiplier)`

***

## Examples (cURL)

### GetCharacterDefinitions

```bash
curl -X POST "https://<host>/api/v2/<TitleTemplateId>/<TitleId>/Client/Character/GetCharacterDefinitions/<UserID>" \
  -H "Authorization: Bearer <SessionTicket>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

### GetUserCharacters

```bash
curl -X POST "https://<host>/api/v2/<TitleTemplateId>/<TitleId>/Client/Character/GetUserCharacters/<UserID>" \
  -H "Authorization: Bearer <SessionTicket>" \
  -H "Content-Type: application/json" \
  -d "{}"
```

### UpgradeStatLevel

```bash
curl -X POST "https://<host>/api/v2/<TitleTemplateId>/<TitleId>/Client/Character/UpgradeStatLevel/<UserID>" \
  -H "Authorization: Bearer <SessionTicket>" \
  -H "Content-Type: application/json" \
  -d "{\"CharacterID\":\"main\",\"StatID\":\"Power\"}"
```

### UpgradeCharacterLevel

```bash
curl -X POST "https://<host>/api/v2/<TitleTemplateId>/<TitleId>/Client/Character/UpgradeCharacterLevel/<UserID>" \
  -H "Authorization: Bearer <SessionTicket>" \
  -H "Content-Type: application/json" \
  -d "{\"CharacterID\":\"hero_1\"}"
```

### EquipItems

```bash
curl -X POST "https://<host>/api/v2/<TitleTemplateId>/<TitleId>/Client/Character/EquipItems/<UserID>" \
  -H "Authorization: Bearer <SessionTicket>" \
  -H "Content-Type: application/json" \
  -d "{\"CharacterID\":\"main\",\"ItemsToEquip\":[{\"SlotID\":\"Weapon\",\"ItemInstanceID\":\"I_001\"}]}"
```

### UnequipItems

```bash
curl -X POST "https://<host>/api/v2/<TitleTemplateId>/<TitleId>/Client/Character/UnequipItems/<UserID>" \
  -H "Authorization: Bearer <SessionTicket>" \
  -H "Content-Type: application/json" \
  -d "{\"CharacterID\":\"main\",\"UnequipSlotIDs\":[\"Weapon\"]}"
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.idosgames.com/api/api-v2/character.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
