# 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=; 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/` | | **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/` | | **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//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//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/` | | **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//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//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//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//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//inventory/` | | **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/` | | **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//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//status` to check processing status - Rate limits apply based on subscription tier ### Get Job Status | | | |---|---| | **Endpoint** | `GET /api/v1/jobs//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//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/` | | **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/` | | **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/` | | **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//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/` | | **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//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//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//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//chats/` | | **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//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//chats/` | | **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//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//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//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//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//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/` | | **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//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 } } ```