# API Reference

All API responses follow standardized format:

```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "request_id": "optional-request-id",
  "result": {},
  "error": null,
  "meta": {}
}
```

**Base URL:** `/api/v1`

---

## Authentication

Authentication handled by Appwrite with HTTP-only cookies. Sessions are stored in `coc_session` cookie.

**Cookie Configuration:**
- **Name:** `coc_session`
- **HTTP-only:** true (JavaScript cannot access)
- **Secure:** true (HTTPS only in production)
- **SameSite:** Lax (CSRF protection)
- **Duration (normal):** 24 hours
- **Duration (remember me):** 30 days

**Session Caching:**
Sessions are cached in Redis (db 2) to reduce Appwrite API calls by ~90%. Cache TTL is 5 minutes. Sessions are explicitly invalidated on logout and password change.

### Register

| | |
|---|---|
| **Endpoint** | `POST /api/v1/auth/register` |
| **Description** | Create new user account with email verification |
| **Auth Required** | No |

**Request Body:**
```json
{
  "email": "player@example.com",
  "password": "SecurePass123!",
  "name": "Adventurer"
}
```

**Response (201 Created):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 201,
  "timestamp": "2025-11-14T12:00:00Z",
  "result": {
    "user": {
      "id": "user_id_123",
      "email": "player@example.com",
      "name": "Adventurer",
      "email_verified": false,
      "tier": "free",
      "created_at": "2025-11-14T12:00:00Z"
    },
    "message": "Registration successful. Please check your email to verify your account."
  }
}
```

### Login

| | |
|---|---|
| **Endpoint** | `POST /api/v1/auth/login` |
| **Description** | User login with session cookie |
| **Auth Required** | No |

**Request Body:**
```json
{
  "email": "player@example.com",
  "password": "SecurePass123!",
  "remember_me": true
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-14T12:00:00Z",
  "result": {
    "user": {
      "id": "user_id_123",
      "email": "player@example.com",
      "name": "Adventurer",
      "email_verified": true,
      "tier": "free"
    },
    "message": "Login successful"
  }
}
```

**Set-Cookie Header:**
```
Set-Cookie: coc_session=<session_token>; HttpOnly; Secure; SameSite=Lax; Max-Age=2592000; Path=/
```

### Logout

| | |
|---|---|
| **Endpoint** | `POST /api/v1/auth/logout` |
| **Description** | Logout current session and clear cookie |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-14T12:00:00Z",
  "result": {
    "message": "Logout successful"
  }
}
```

### Get Current User

| | |
|---|---|
| **Endpoint** | `GET /api/v1/auth/me` |
| **Description** | Get current authenticated user's data |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-14T12:00:00Z",
  "result": {
    "id": "user_id_123",
    "email": "player@example.com",
    "name": "Adventurer",
    "email_verified": true,
    "tier": "premium"
  }
}
```

### Verify Email

| | |
|---|---|
| **Endpoint** | `GET /api/v1/auth/verify-email` |
| **Description** | Verify user email address |
| **Auth Required** | No |

**Query Parameters:**
- `userId` - User ID from verification email
- `secret` - Verification secret from email

**Success:** Redirects to `/auth/login?verified=true`
**Error:** Redirects to `/auth/login?verified=false`

### Forgot Password

| | |
|---|---|
| **Endpoint** | `POST /api/v1/auth/forgot-password` |
| **Description** | Request password reset email |
| **Auth Required** | No |

**Request Body:**
```json
{
  "email": "player@example.com"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-14T12:00:00Z",
  "result": {
    "message": "If an account exists with this email, you will receive a password reset link shortly."
  }
}
```

### Reset Password

| | |
|---|---|
| **Endpoint** | `POST /api/v1/auth/reset-password` |
| **Description** | Submit new password |
| **Auth Required** | No |

**Request Body:**
```json
{
  "user_id": "user_id_123",
  "secret": "reset_secret",
  "password": "NewSecurePass123!"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-14T12:00:00Z",
  "result": {
    "message": "Password reset successful. You can now log in with your new password."
  }
}
```

---

## Characters

### List Characters

| | |
|---|---|
| **Endpoint** | `GET /api/v1/characters` |
| **Description** | Get all characters for current user with tier information |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "characters": [
      {
        "character_id": "char_123",
        "name": "Thorin Ironheart",
        "class": "vanguard",
        "class_name": "Vanguard",
        "level": 5,
        "experience": 250,
        "gold": 1000,
        "current_location": "forgotten_crypt",
        "origin": "soul_revenant"
      }
    ],
    "count": 1,
    "tier": "free",
    "limit": 1
  }
}
```

### Get Character

| | |
|---|---|
| **Endpoint** | `GET /api/v1/characters/<id>` |
| **Description** | Get full character details including inventory, equipment, and skills |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "character_id": "char_123",
    "user_id": "user_456",
    "name": "Thorin Ironheart",
    "player_class": {
      "class_id": "vanguard",
      "name": "Vanguard",
      "description": "A seasoned warrior...",
      "base_stats": {
        "strength": 14,
        "dexterity": 10,
        "constitution": 14,
        "intelligence": 8,
        "wisdom": 10,
        "charisma": 9
      },
      "skill_trees": [
        {
          "tree_id": "shield_bearer",
          "name": "Shield Bearer",
          "description": "Defensive tanking specialization",
          "nodes": []
        }
      ],
      "starting_equipment": ["rusty_sword"],
      "starting_abilities": ["basic_attack"]
    },
    "origin": {
      "id": "soul_revenant",
      "name": "Soul Revenant",
      "description": "Returned from death...",
      "starting_location": {
        "id": "forgotten_crypt",
        "name": "The Forgotten Crypt",
        "region": "The Deadlands",
        "description": "An ancient burial site..."
      },
      "narrative_hooks": [],
      "starting_bonus": {}
    },
    "level": 5,
    "experience": 250,
    "base_stats": {
      "strength": 14,
      "dexterity": 10,
      "constitution": 14,
      "intelligence": 8,
      "wisdom": 10,
      "charisma": 9
    },
    "unlocked_skills": ["shield_bash", "iron_defense"],
    "inventory": [],
    "equipped": {},
    "gold": 1000,
    "active_quests": [],
    "discovered_locations": ["forgotten_crypt"],
    "current_location": "forgotten_crypt"
  }
}
```

**Error Response (404 Not Found):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 404,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": null,
  "error": {
    "code": "NOT_FOUND",
    "message": "Character not found: char_999",
    "details": {}
  }
}
```

### Create Character

| | |
|---|---|
| **Endpoint** | `POST /api/v1/characters` |
| **Description** | Create new character (validates tier limits) |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "name": "Thorin Ironheart",
  "class_id": "vanguard",
  "origin_id": "soul_revenant"
}
```

**Validation Rules:**
- **name**: 2-50 characters, letters/numbers/spaces/hyphens/apostrophes only
- **class_id**: Must be one of: vanguard, assassin, arcanist, luminary, wildstrider, oathkeeper, necromancer, lorekeeper
- **origin_id**: Must be one of: soul_revenant, memory_thief, shadow_apprentice, escaped_captive

**Response (201 Created):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 201,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "character_id": "char_123",
    "name": "Thorin Ironheart",
    "class": "vanguard",
    "class_name": "Vanguard",
    "origin": "soul_revenant",
    "origin_name": "Soul Revenant",
    "level": 1,
    "gold": 0,
    "current_location": "forgotten_crypt",
    "message": "Character created successfully"
  }
}
```

**Error Response (400 Bad Request - Limit Exceeded):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 400,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": null,
  "error": {
    "code": "CHARACTER_LIMIT_EXCEEDED",
    "message": "Character limit reached for free tier (1/1). Upgrade your subscription to create more characters.",
    "details": {}
  }
}
```

### Delete Character

| | |
|---|---|
| **Endpoint** | `DELETE /api/v1/characters/<id>` |
| **Description** | Permanently delete character and all associated game sessions |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "message": "Character deleted successfully",
    "character_id": "char_123"
  }
}
```

### Unlock Skill

| | |
|---|---|
| **Endpoint** | `POST /api/v1/characters/<id>/skills/unlock` |
| **Description** | Unlock skill node (validates prerequisites and skill points) |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "skill_id": "shield_bash"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "message": "Skill unlocked successfully",
    "character_id": "char_123",
    "skill_id": "shield_bash",
    "unlocked_skills": ["shield_bash"],
    "available_points": 0
  }
}
```

### Respec Skills

| | |
|---|---|
| **Endpoint** | `POST /api/v1/characters/<id>/skills/respec` |
| **Description** | Reset all unlocked skills (costs level × 100 gold) |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "message": "Skills reset successfully",
    "character_id": "char_123",
    "cost": 500,
    "remaining_gold": 500,
    "available_points": 5
  }
}
```

---

## Classes & Origins (Reference Data)

### List Classes

| | |
|---|---|
| **Endpoint** | `GET /api/v1/classes` |
| **Description** | Get all available player classes |
| **Auth Required** | No |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "classes": [
      {
        "class_id": "vanguard",
        "name": "Vanguard",
        "description": "A seasoned warrior who stands at the front lines...",
        "base_stats": {
          "strength": 14,
          "dexterity": 10,
          "constitution": 14,
          "intelligence": 8,
          "wisdom": 10,
          "charisma": 9
        },
        "skill_trees": ["Shield Bearer", "Weapon Master"],
        "starting_equipment": ["rusty_sword"],
        "starting_abilities": ["basic_attack"]
      }
    ],
    "count": 8
  }
}
```

### Get Class

| | |
|---|---|
| **Endpoint** | `GET /api/v1/classes/<class_id>` |
| **Description** | Get full class details with skill trees |
| **Auth Required** | No |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "class_id": "vanguard",
    "name": "Vanguard",
    "description": "A seasoned warrior who stands at the front lines...",
    "base_stats": {
      "strength": 14,
      "dexterity": 10,
      "constitution": 14,
      "intelligence": 8,
      "wisdom": 10,
      "charisma": 9
    },
    "skill_trees": [
      {
        "tree_id": "shield_bearer",
        "name": "Shield Bearer",
        "description": "Defensive tanking specialization",
        "nodes": [
          {
            "skill_id": "shield_bash",
            "name": "Shield Bash",
            "description": "Strike with shield for 120% damage and 1-turn stun",
            "tier": 1,
            "prerequisites": [],
            "effects": {
              "damage_multiplier": 1.2
            },
            "unlocks_abilities": ["shield_bash"]
          }
        ]
      }
    ],
    "starting_equipment": ["rusty_sword"],
    "starting_abilities": ["basic_attack"]
  }
}
```

### List Origins

| | |
|---|---|
| **Endpoint** | `GET /api/v1/origins` |
| **Description** | Get all available character origins |
| **Auth Required** | No |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-15T12:00:00Z",
  "result": {
    "origins": [
      {
        "id": "soul_revenant",
        "name": "Soul Revenant",
        "description": "Centuries ago, you perished in battle...",
        "starting_location": {
          "id": "forgotten_crypt",
          "name": "The Forgotten Crypt",
          "region": "The Deadlands",
          "description": "An ancient burial site shrouded in mist..."
        },
        "narrative_hooks": [
          "Why were you brought back to life?",
          "What happened in the centuries you were dead?"
        ],
        "starting_bonus": {
          "trait": "Deathless Resolve",
          "description": "Having already died once, death holds no fear for you",
          "effect": "Once per day, when reduced to 0 HP, survive with 1 HP instead"
        }
      }
    ],
    "count": 4
  }
}
```

---

## Inventory

### Get Inventory

| | |
|---|---|
| **Endpoint** | `GET /api/v1/characters/<character_id>/inventory` |
| **Description** | Get character inventory and equipped items |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "inventory": [
      {
        "item_id": "gen_abc123",
        "name": "Flaming Dagger",
        "item_type": "weapon",
        "rarity": "rare",
        "value": 250,
        "description": "A dagger imbued with fire magic"
      }
    ],
    "equipped": {
      "weapon": {
        "item_id": "rusty_sword",
        "name": "Rusty Sword",
        "item_type": "weapon",
        "rarity": "common"
      },
      "helmet": null,
      "chest": null,
      "legs": null,
      "boots": null,
      "gloves": null,
      "ring1": null,
      "ring2": null,
      "amulet": null
    },
    "inventory_count": 5,
    "max_inventory": 100
  }
}
```

### Equip Item

| | |
|---|---|
| **Endpoint** | `POST /api/v1/characters/<character_id>/inventory/equip` |
| **Description** | Equip an item from inventory to a specified slot |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "item_id": "gen_abc123",
  "slot": "weapon"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "message": "Equipped Flaming Dagger to weapon slot",
    "equipped": {
      "weapon": {...},
      "helmet": null
    },
    "unequipped_item": {
      "item_id": "rusty_sword",
      "name": "Rusty Sword"
    }
  }
}
```

### Unequip Item

| | |
|---|---|
| **Endpoint** | `POST /api/v1/characters/<character_id>/inventory/unequip` |
| **Description** | Unequip an item from a specified slot (returns to inventory) |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "slot": "weapon"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "message": "Unequipped Flaming Dagger from weapon slot",
    "unequipped_item": {...},
    "equipped": {...}
  }
}
```

### Use Item

| | |
|---|---|
| **Endpoint** | `POST /api/v1/characters/<character_id>/inventory/use` |
| **Description** | Use a consumable item from inventory |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "item_id": "health_potion_small"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "item_used": "Small Health Potion",
    "effects_applied": [
      {
        "effect_name": "Healing",
        "effect_type": "hot",
        "value": 25,
        "message": "Restored 25 HP"
      }
    ],
    "hp_restored": 25,
    "mp_restored": 0,
    "message": "Used Small Health Potion: Restored 25 HP"
  }
}
```

### Drop Item

| | |
|---|---|
| **Endpoint** | `DELETE /api/v1/characters/<character_id>/inventory/<item_id>` |
| **Description** | Drop (remove) an item from inventory |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "message": "Dropped Rusty Sword",
    "dropped_item": {...},
    "inventory_count": 4
  }
}
```

---

## Health

### Health Check

| | |
|---|---|
| **Endpoint** | `GET /api/v1/health` |
| **Description** | Check API service status and version |
| **Auth Required** | No |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-16T10:30:00Z",
  "result": {
    "status": "ok",
    "service": "Code of Conquest API",
    "version": "0.1.0"
  },
  "error": null,
  "meta": {}
}
```

---

## Sessions

### List Sessions

| | |
|---|---|
| **Endpoint** | `GET /api/v1/sessions` |
| **Description** | Get all active sessions for current user |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-16T10:30:00Z",
  "result": [
    {
      "session_id": "sess_789",
      "character_id": "char_456",
      "character_name": "Thorin",
      "turn_number": 5,
      "status": "active",
      "created_at": "2025-11-16T10:00:00Z",
      "last_activity": "2025-11-16T10:25:00Z",
      "in_combat": false,
      "game_state": {
        "current_location": "crossville_village",
        "location_type": "town",
        "in_combat": false,
        "combat_round": null
      }
    }
  ]
}
```

### Create Session

| | |
|---|---|
| **Endpoint** | `POST /api/v1/sessions` |
| **Description** | Create new solo game session |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "character_id": "char_456"
}
```

**Response (201 Created):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 201,
  "timestamp": "2025-11-16T10:30:00Z",
  "result": {
    "session_id": "sess_789",
    "character_id": "char_456",
    "turn_number": 0,
    "game_state": {
      "current_location": "Crossroads Village",
      "location_type": "town",
      "active_quests": []
    }
  }
}
```

**Session Limits by Tier:**
| Tier | Max Active Sessions |
|------|---------------------|
| FREE | 1 |
| BASIC | 2 |
| PREMIUM | 3 |
| ELITE | 5 |

### Get Session State

| | |
|---|---|
| **Endpoint** | `GET /api/v1/sessions/<session_id>` |
| **Description** | Get current session state with available actions |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-16T10:30:00Z",
  "result": {
    "session_id": "sess_789",
    "character_id": "char_456",
    "turn_number": 5,
    "status": "active",
    "in_combat": false,
    "game_state": {
      "current_location": "The Rusty Anchor",
      "location_type": "tavern",
      "active_quests": ["quest_goblin_cave"],
      "in_combat": false
    },
    "available_actions": [
      {
        "prompt_id": "ask_locals",
        "display_text": "Ask locals for information",
        "description": "Talk to NPCs to learn about quests and rumors",
        "category": "ask_question"
      },
      {
        "prompt_id": "rest",
        "display_text": "Rest and recover",
        "description": "Take a short rest to recover health",
        "category": "rest"
      }
    ]
  }
}
```

### Take Action

| | |
|---|---|
| **Endpoint** | `POST /api/v1/sessions/<session_id>/action` |
| **Description** | Submit action for AI processing (async) |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "action_type": "button",
  "prompt_id": "ask_locals"
}
```

**Response (202 Accepted):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 202,
  "timestamp": "2025-11-16T10:30:00Z",
  "result": {
    "job_id": "abc-123",
    "status": "queued",
    "message": "Your action is being processed..."
  }
}
```

**Notes:**
- Only button actions with predefined prompts are supported
- Poll `/api/v1/jobs/<job_id>/status` to check processing status
- Rate limits apply based on subscription tier

### Get Job Status

| | |
|---|---|
| **Endpoint** | `GET /api/v1/jobs/<job_id>/status` |
| **Description** | Get status of an async AI processing job |
| **Auth Required** | Yes |

**Response (200 OK - Processing):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-16T10:30:05Z",
  "result": {
    "job_id": "ai_TaskType.NARRATIVE_abc123",
    "status": "processing"
  }
}
```

**Response (200 OK - Completed):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-16T10:30:10Z",
  "result": {
    "job_id": "ai_TaskType.NARRATIVE_abc123",
    "status": "completed",
    "dm_response": "As you search for supplies in the village, you notice...",
    "tokens_used": 273,
    "model": "meta/meta-llama-3-8b-instruct"
  }
}
```

### Get Conversation History

| | |
|---|---|
| **Endpoint** | `GET /api/v1/sessions/<session_id>/history` |
| **Description** | Get paginated conversation history |
| **Auth Required** | Yes |

**Query Parameters:**
- `limit` (int, default 20, max 100) - Number of entries to return
- `offset` (int, default 0) - Number of entries to skip

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-16T10:30:00Z",
  "result": {
    "total_turns": 5,
    "history": [
      {
        "turn": 1,
        "action": "I explore the tavern",
        "dm_response": "You enter a smoky tavern filled with weary travelers...",
        "timestamp": "2025-11-16T10:30:00Z"
      }
    ],
    "pagination": {
      "limit": 20,
      "offset": 0,
      "has_more": false
    }
  }
}
```

### Delete Session

| | |
|---|---|
| **Endpoint** | `DELETE /api/v1/sessions/<session_id>` |
| **Description** | Permanently delete a session and all associated chat messages |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-16T10:30:00Z",
  "result": {
    "message": "Session deleted successfully",
    "session_id": "sess_789"
  }
}
```

### Get Usage Information

| | |
|---|---|
| **Endpoint** | `GET /api/v1/usage` |
| **Description** | Get user's daily usage information (turn limits) |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "user_id": "user_123",
    "user_tier": "free",
    "current_usage": 15,
    "daily_limit": 50,
    "remaining": 35,
    "reset_time": "2025-11-27T00:00:00+00:00",
    "is_limited": false,
    "is_unlimited": false
  }
}
```

---

## Travel

### Get Available Destinations

| | |
|---|---|
| **Endpoint** | `GET /api/v1/travel/available` |
| **Description** | Get all locations the player can travel to |
| **Auth Required** | Yes |

**Query Parameters:**
- `session_id` (required) - Active session ID

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "current_location": "crossville_village",
    "destinations": [
      {
        "location_id": "crossville_tavern",
        "name": "The Rusty Anchor Tavern",
        "location_type": "tavern",
        "region_id": "crossville",
        "description": "A cozy tavern where travelers share tales..."
      }
    ]
  }
}
```

### Travel to Location

| | |
|---|---|
| **Endpoint** | `POST /api/v1/travel` |
| **Description** | Travel to a new location (must be discovered) |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "session_id": "sess_789",
  "location_id": "crossville_tavern"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "location": {
      "location_id": "crossville_tavern",
      "name": "The Rusty Anchor Tavern",
      "location_type": "tavern",
      "region_id": "crossville",
      "description": "A cozy tavern where travelers share tales...",
      "lore": "Founded decades ago by a retired adventurer...",
      "ambient_description": "The scent of ale and roasting meat fills the air...",
      "available_quests": ["quest_missing_trader"],
      "npc_ids": ["npc_grom_ironbeard"],
      "discoverable_locations": ["crossville_forest"],
      "is_starting_location": false,
      "tags": ["tavern", "social", "merchant", "safe"]
    },
    "npcs_present": [
      {
        "npc_id": "npc_grom_ironbeard",
        "name": "Grom Ironbeard",
        "role": "bartender",
        "appearance": "Stout dwarf with a braided grey beard"
      }
    ],
    "game_state": {
      "current_location": "crossville_tavern",
      "location_type": "tavern",
      "active_quests": []
    }
  }
}
```

### Get Location Details

| | |
|---|---|
| **Endpoint** | `GET /api/v1/travel/location/<location_id>` |
| **Description** | Get full details for a specific location |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "location": {...},
    "npcs_present": [...]
  }
}
```

### Get Current Location

| | |
|---|---|
| **Endpoint** | `GET /api/v1/travel/current` |
| **Description** | Get details about the current location in a session |
| **Auth Required** | Yes |

**Query Parameters:**
- `session_id` (required) - Active session ID

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "location": {...},
    "npcs_present": [...]
  }
}
```

---

## NPCs

### Get NPC Details

| | |
|---|---|
| **Endpoint** | `GET /api/v1/npcs/<npc_id>` |
| **Description** | Get NPC details with knowledge filtered by relationship |
| **Auth Required** | Yes |

**Query Parameters:**
- `character_id` (optional) - Filter knowledge by character's relationship

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "npc_id": "npc_grom_ironbeard",
    "name": "Grom Ironbeard",
    "role": "bartender",
    "location_id": "crossville_tavern",
    "image_url": "/static/images/npcs/crossville/grom_ironbeard.png",
    "personality": {
      "traits": ["gruff", "observant", "secretly kind"],
      "speech_style": "Uses dwarven expressions, speaks in short sentences",
      "quirks": ["Polishes same mug when thinking", "Snorts when amused"]
    },
    "appearance": {
      "brief": "Stout dwarf with a braided grey beard",
      "detailed": "A weathered dwarf with deep-set eyes..."
    },
    "available_knowledge": [
      "The mayor has been acting strange lately",
      "Strange lights seen in the forest"
    ],
    "interaction_summary": {
      "interaction_count": 3,
      "relationship_level": 65,
      "first_met": "2025-11-20T10:30:00Z"
    },
    "tags": ["merchant", "quest_giver", "information"]
  }
}
```

### Talk to NPC

| | |
|---|---|
| **Endpoint** | `POST /api/v1/npcs/<npc_id>/talk` |
| **Description** | Start or continue conversation with NPC (generates AI dialogue) |
| **Auth Required** | Yes |

**Request Body (Initial Greeting):**
```json
{
  "session_id": "sess_789",
  "topic": "greeting"
}
```

**Request Body (Player Response - Bidirectional Dialogue):**
```json
{
  "session_id": "sess_789",
  "player_response": "What can you tell me about the bandits?"
}
```

**Response (202 Accepted):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 202,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "job_id": "ai_npc_dialogue_xyz789",
    "status": "queued",
    "message": "Starting conversation with Grom Ironbeard...",
    "npc_name": "Grom Ironbeard",
    "npc_role": "bartender"
  }
}
```

**Job Result (when completed):**
```json
{
  "job_id": "ai_npc_dialogue_xyz789",
  "status": "completed",
  "result": {
    "context_type": "npc_dialogue",
    "dialogue": "*polishes mug thoughtfully* \"Ah, another adventurer. What'll it be?\"",
    "tokens_used": 728,
    "npc_name": "Grom Ironbeard",
    "npc_id": "npc_grom_ironbeard",
    "character_name": "Thorin",
    "player_line": "greeting",
    "conversation_history": [
      {
        "player_line": "Hello there!",
        "npc_response": "*nods gruffly* \"Welcome to the Rusty Anchor.\""
      }
    ]
  }
}
```

### Get NPCs at Location

| | |
|---|---|
| **Endpoint** | `GET /api/v1/npcs/at-location/<location_id>` |
| **Description** | Get all NPCs present at a location |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "location_id": "crossville_tavern",
    "npcs": [
      {
        "npc_id": "npc_grom_ironbeard",
        "name": "Grom Ironbeard",
        "role": "bartender",
        "appearance": "Stout dwarf with a braided grey beard",
        "tags": ["merchant", "quest_giver"],
        "image_url": "/static/images/npcs/crossville/grom_ironbeard.png"
      }
    ]
  }
}
```

### Adjust NPC Relationship

| | |
|---|---|
| **Endpoint** | `POST /api/v1/npcs/<npc_id>/relationship` |
| **Description** | Adjust relationship level with NPC |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "character_id": "char_123",
  "adjustment": 10
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "npc_id": "npc_grom_ironbeard",
    "relationship_level": 75
  }
}
```

### Set NPC Custom Flag

| | |
|---|---|
| **Endpoint** | `POST /api/v1/npcs/<npc_id>/flag` |
| **Description** | Set custom flag on NPC interaction (for conditional secrets) |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "character_id": "char_123",
  "flag_name": "helped_with_rats",
  "flag_value": true
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T10:30:00Z",
  "result": {
    "npc_id": "npc_grom_ironbeard",
    "flag_name": "helped_with_rats",
    "flag_value": true
  }
}
```

---

## Chat / Conversation History

The Chat API provides access to complete player-NPC conversation history. All dialogue exchanges are stored in the `chat_messages` collection for unlimited history.

### Get All Conversations Summary

| | |
|---|---|
| **Endpoint** | `GET /api/v1/characters/<character_id>/chats` |
| **Description** | Get summary of all NPC conversations for a character |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T14:30:00Z",
  "result": {
    "conversations": [
      {
        "npc_id": "npc_grom_ironbeard",
        "npc_name": "Grom Ironbeard",
        "last_message_timestamp": "2025-11-25T14:30:00Z",
        "message_count": 15,
        "recent_preview": "Aye, the rats in the cellar have been causing trouble..."
      }
    ]
  }
}
```

### Get Conversation with Specific NPC

| | |
|---|---|
| **Endpoint** | `GET /api/v1/characters/<character_id>/chats/<npc_id>` |
| **Description** | Get paginated conversation history with a specific NPC |
| **Auth Required** | Yes |

**Query Parameters:**
- `limit` (optional): Maximum messages to return (default: 50, max: 100)
- `offset` (optional): Number of messages to skip (default: 0)

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T14:30:00Z",
  "result": {
    "npc_id": "npc_grom_ironbeard",
    "npc_name": "Grom Ironbeard",
    "total_messages": 15,
    "messages": [
      {
        "message_id": "msg_abc123",
        "character_id": "char_123",
        "npc_id": "npc_grom_ironbeard",
        "player_message": "What rumors have you heard?",
        "npc_response": "*leans in* Strange folk been coming through lately...",
        "timestamp": "2025-11-25T14:30:00Z",
        "context": "dialogue",
        "location_id": "crossville_tavern",
        "session_id": "sess_789",
        "metadata": {},
        "is_deleted": false
      }
    ],
    "pagination": {
      "limit": 50,
      "offset": 0,
      "has_more": false
    }
  }
}
```

### Search Messages

| | |
|---|---|
| **Endpoint** | `GET /api/v1/characters/<character_id>/chats/search` |
| **Description** | Search messages by text with optional filters |
| **Auth Required** | Yes |

**Query Parameters:**
- `q` (required): Search text to find in player_message and npc_response
- `npc_id` (optional): Filter by specific NPC
- `context` (optional): Filter by message context type
- `date_from` (optional): Start date in ISO format
- `date_to` (optional): End date in ISO format
- `limit` (optional): Maximum messages to return (default: 50, max: 100)
- `offset` (optional): Number of messages to skip (default: 0)

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T14:30:00Z",
  "result": {
    "search_text": "quest",
    "filters": {
      "npc_id": "npc_grom_ironbeard",
      "context": "quest_offered",
      "date_from": null,
      "date_to": null
    },
    "total_results": 2,
    "messages": [...],
    "pagination": {
      "limit": 50,
      "offset": 0,
      "has_more": false
    }
  }
}
```

### Delete Message

| | |
|---|---|
| **Endpoint** | `DELETE /api/v1/characters/<character_id>/chats/<message_id>` |
| **Description** | Soft delete a message (privacy/moderation) |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-25T14:30:00Z",
  "result": {
    "message_id": "msg_abc123",
    "deleted": true
  }
}
```

---

## Combat

### Start Combat

| | |
|---|---|
| **Endpoint** | `POST /api/v1/combat/start` |
| **Description** | Start a new combat encounter |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "session_id": "sess_123",
  "enemy_ids": ["goblin", "goblin", "goblin_shaman"]
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "encounter_id": "enc_abc123",
    "combatants": [
      {
        "combatant_id": "char_456",
        "name": "Thorin",
        "is_player": true,
        "current_hp": 100,
        "max_hp": 100,
        "current_mp": 50,
        "max_mp": 50,
        "initiative": 15,
        "abilities": ["basic_attack", "shield_bash"]
      }
    ],
    "turn_order": ["char_456", "goblin_0", "goblin_shaman_0", "goblin_1"],
    "current_turn": "char_456",
    "round_number": 1,
    "status": "active"
  }
}
```

### Get Combat State

| | |
|---|---|
| **Endpoint** | `GET /api/v1/combat/<session_id>/state` |
| **Description** | Get current combat state for a session |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "in_combat": true,
    "encounter": {
      "encounter_id": "enc_abc123",
      "combatants": [...],
      "turn_order": [...],
      "current_turn": "char_456",
      "round_number": 2,
      "status": "active",
      "combat_log": [
        "Thorin attacks Goblin for 15 damage!",
        "Goblin attacks Thorin for 5 damage!"
      ]
    }
  }
}
```

### Execute Combat Action

| | |
|---|---|
| **Endpoint** | `POST /api/v1/combat/<session_id>/action` |
| **Description** | Execute a combat action for a combatant |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "combatant_id": "char_456",
  "action_type": "attack",
  "target_ids": ["goblin_0"],
  "ability_id": "shield_bash"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "success": true,
    "message": "Shield Bash hits Goblin for 18 damage and stuns!",
    "damage_results": [
      {
        "target_id": "goblin_0",
        "damage": 18,
        "is_critical": false
      }
    ],
    "effects_applied": [
      {
        "target_id": "goblin_0",
        "effect": "stunned",
        "duration": 1
      }
    ],
    "combat_ended": false,
    "combat_status": null,
    "next_combatant_id": "goblin_1"
  }
}
```

### Execute Enemy Turn

| | |
|---|---|
| **Endpoint** | `POST /api/v1/combat/<session_id>/enemy-turn` |
| **Description** | Execute the current enemy's turn using AI logic |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "success": true,
    "message": "Goblin attacks Thorin for 8 damage!",
    "damage_results": [...],
    "effects_applied": [],
    "combat_ended": false,
    "combat_status": null,
    "next_combatant_id": "char_456"
  }
}
```

### Attempt Flee

| | |
|---|---|
| **Endpoint** | `POST /api/v1/combat/<session_id>/flee` |
| **Description** | Attempt to flee from combat |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "combatant_id": "char_456"
}
```

**Response (200 OK - Success):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "success": true,
    "message": "Successfully fled from combat!",
    "combat_ended": true,
    "combat_status": "fled"
  }
}
```

### End Combat

| | |
|---|---|
| **Endpoint** | `POST /api/v1/combat/<session_id>/end` |
| **Description** | Force end the current combat (debug/admin endpoint) |
| **Auth Required** | Yes |

**Request Body:**
```json
{
  "outcome": "victory"
}
```

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "outcome": "victory",
    "rewards": {
      "experience": 100,
      "gold": 50,
      "items": [...],
      "level_ups": []
    }
  }
}
```

### List Enemies

| | |
|---|---|
| **Endpoint** | `GET /api/v1/combat/enemies` |
| **Description** | List all available enemy templates |
| **Auth Required** | No |

**Query Parameters:**
- `difficulty` (optional): Filter by difficulty (easy, medium, hard, boss)
- `tag` (optional): Filter by tag (undead, beast, humanoid, etc.)

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "enemies": [
      {
        "enemy_id": "goblin",
        "name": "Goblin Scout",
        "description": "A small, cunning creature...",
        "difficulty": "easy",
        "tags": ["humanoid", "goblinoid"],
        "experience_reward": 15,
        "gold_reward_range": [5, 15]
      }
    ]
  }
}
```

### Get Enemy Details

| | |
|---|---|
| **Endpoint** | `GET /api/v1/combat/enemies/<enemy_id>` |
| **Description** | Get detailed information about a specific enemy template |
| **Auth Required** | No |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "enemy_id": "goblin",
    "name": "Goblin Scout",
    "description": "A small, cunning creature with sharp claws",
    "base_stats": {
      "strength": 8,
      "dexterity": 14,
      "constitution": 10
    },
    "abilities": ["quick_strike", "dodge"],
    "loot_table": [...],
    "difficulty": "easy",
    "experience_reward": 15,
    "gold_reward_min": 5,
    "gold_reward_max": 15
  }
}
```

### Debug: Reset HP/MP

| | |
|---|---|
| **Endpoint** | `POST /api/v1/combat/<session_id>/debug/reset-hp-mp` |
| **Description** | Reset player combatant's HP and MP to full (debug endpoint) |
| **Auth Required** | Yes |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-27T12:00:00Z",
  "result": {
    "success": true,
    "message": "HP and MP reset to full",
    "current_hp": 100,
    "max_hp": 100,
    "current_mp": 50,
    "max_mp": 50
  }
}
```

---

## Game Mechanics

### Perform Skill Check or Search

| | |
|---|---|
| **Endpoint** | `POST /api/v1/game/check` |
| **Description** | Perform a skill check or search action and get deterministic dice roll results |
| **Auth Required** | Yes |

**Request Body (Skill Check):**
```json
{
  "character_id": "char_123",
  "check_type": "skill",
  "skill": "persuasion",
  "dc": 15,
  "bonus": 2,
  "context": {
    "npc_name": "Guard Captain"
  }
}
```

**Request Body (Search Action):**
```json
{
  "character_id": "char_123",
  "check_type": "search",
  "location_type": "forest",
  "dc": 12,
  "bonus": 0
}
```

**Request Body (Using Difficulty Name):**
```json
{
  "character_id": "char_123",
  "check_type": "skill",
  "skill": "stealth",
  "difficulty": "hard",
  "bonus": 1
}
```

**Response (200 OK - Skill Check Success):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-23T10:30:00Z",
  "result": {
    "check_result": {
      "roll": 16,
      "modifier": 3,
      "total": 19,
      "dc": 15,
      "success": true,
      "margin": 4,
      "skill_type": "persuasion"
    },
    "context": {
      "skill_used": "persuasion",
      "stat_used": "charisma",
      "npc_name": "Guard Captain"
    }
  }
}
```

**Response (200 OK - Search Success):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-23T10:30:00Z",
  "result": {
    "check_result": {
      "roll": 18,
      "modifier": 2,
      "total": 20,
      "dc": 12,
      "success": true,
      "margin": 8,
      "skill_type": "perception"
    },
    "items_found": [
      {
        "template_key": "ancient_coin",
        "name": "Ancient Coin",
        "description": "A weathered coin from ages past",
        "value": 25
      }
    ],
    "gold_found": 15
  }
}
```

**Notes:**
- `check_type` must be "search" or "skill"
- For skill checks, `skill` is required
- `dc` or `difficulty` must be provided (dc takes precedence)
- Valid difficulty values: trivial (5), easy (10), medium (15), hard (20), very_hard (25), nearly_impossible (30)

### List Available Skills

| | |
|---|---|
| **Endpoint** | `GET /api/v1/game/skills` |
| **Description** | Get all available skill types with their associated stats |
| **Auth Required** | No |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-23T10:30:00Z",
  "result": {
    "skills": [
      {"name": "perception", "stat": "wisdom"},
      {"name": "insight", "stat": "wisdom"},
      {"name": "stealth", "stat": "dexterity"},
      {"name": "persuasion", "stat": "charisma"},
      {"name": "athletics", "stat": "strength"}
    ]
  }
}
```

### List Difficulty Levels

| | |
|---|---|
| **Endpoint** | `GET /api/v1/game/difficulties` |
| **Description** | Get all difficulty levels and their DC values |
| **Auth Required** | No |

**Response (200 OK):**
```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 200,
  "timestamp": "2025-11-23T10:30:00Z",
  "result": {
    "difficulties": [
      {"name": "trivial", "dc": 5},
      {"name": "easy", "dc": 10},
      {"name": "medium", "dc": 15},
      {"name": "hard", "dc": 20},
      {"name": "very_hard", "dc": 25},
      {"name": "nearly_impossible", "dc": 30}
    ]
  }
}
```

---

## Error Responses

### Standard Error Format

```json
{
  "app": "Code of Conquest",
  "version": "0.1.0",
  "status": 400,
  "timestamp": "2025-11-14T12:00:00Z",
  "result": null,
  "error": {
    "code": "INVALID_ACTION",
    "message": "Not your turn",
    "details": {
      "current_turn": "char456",
      "your_character": "char123"
    }
  }
}
```

### Common Error Codes

| Code | Status | Description |
|------|--------|-------------|
| `UNAUTHORIZED` | 401 | Invalid or missing auth token |
| `FORBIDDEN` | 403 | Insufficient permissions |
| `NOT_FOUND` | 404 | Resource not found |
| `INVALID_INPUT` | 400 | Validation error |
| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests |
| `CHARACTER_LIMIT_EXCEEDED` | 400 | User has reached character limit for their tier |
| `SESSION_LIMIT_EXCEEDED` | 409 | User has reached session limit for their tier |
| `CHARACTER_NOT_FOUND` | 404 | Character does not exist or not accessible |
| `SKILL_UNLOCK_ERROR` | 400 | Skill unlock failed (prerequisites, points, or tier) |
| `INSUFFICIENT_GOLD` | 400 | Not enough gold |
| `INSUFFICIENT_RESOURCES` | 400 | Not enough MP/items for action |
| `INVALID_ACTION` | 400 | Action not allowed |
| `SESSION_FULL` | 400 | Session at max capacity |
| `NOT_YOUR_TURN` | 400 | Not active player's turn |
| `AI_LIMIT_EXCEEDED` | 429 | Daily AI call limit reached |
| `PREMIUM_REQUIRED` | 403 | Feature requires premium subscription |
| `ALREADY_IN_COMBAT` | 400 | Session is already in combat |
| `NOT_IN_COMBAT` | 404 | Session is not in combat |
| `INVENTORY_FULL` | 400 | Inventory is full |
| `CANNOT_EQUIP` | 400 | Item cannot be equipped |
| `CANNOT_USE_ITEM` | 400 | Item cannot be used |

---

## Rate Limiting

| Tier | Requests/Minute | AI Calls/Day | Max Sessions |
|------|-----------------|--------------|--------------|
| FREE | 30 | 50 | 1 |
| BASIC | 60 | 200 | 2 |
| PREMIUM | 120 | 1000 | 3 |
| ELITE | 300 | Unlimited | 5 |

**Rate Limit Headers:**
```
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1699999999
```

---

## Pagination

Endpoints that return lists support pagination:

**Query Parameters:**
- `page` - Page number (default: 1)
- `limit` - Items per page (default: 20, max: 100)

**Response Meta:**
```json
{
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 100,
    "pages": 5
  }
}
```
