46 KiB
API Reference
All API responses follow standardized format:
{
"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:
{
"email": "player@example.com",
"password": "SecurePass123!",
"name": "Adventurer"
}
Response (201 Created):
{
"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:
{
"email": "player@example.com",
"password": "SecurePass123!",
"remember_me": true
}
Response (200 OK):
{
"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):
{
"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):
{
"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 emailsecret- 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:
{
"email": "player@example.com"
}
Response (200 OK):
{
"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:
{
"user_id": "user_id_123",
"secret": "reset_secret",
"password": "NewSecurePass123!"
}
Response (200 OK):
{
"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):
{
"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):
{
"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):
{
"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:
{
"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):
{
"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):
{
"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):
{
"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:
{
"skill_id": "shield_bash"
}
Response (200 OK):
{
"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):
{
"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):
{
"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):
{
"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):
{
"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):
{
"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:
{
"item_id": "gen_abc123",
"slot": "weapon"
}
Response (200 OK):
{
"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:
{
"slot": "weapon"
}
Response (200 OK):
{
"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:
{
"item_id": "health_potion_small"
}
Response (200 OK):
{
"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):
{
"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):
{
"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):
{
"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:
{
"character_id": "char_456"
}
Response (201 Created):
{
"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):
{
"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:
{
"action_type": "button",
"prompt_id": "ask_locals"
}
Response (202 Accepted):
{
"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>/statusto 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):
{
"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):
{
"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 returnoffset(int, default 0) - Number of entries to skip
Response (200 OK):
{
"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):
{
"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):
{
"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):
{
"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:
{
"session_id": "sess_789",
"location_id": "crossville_tavern"
}
Response (200 OK):
{
"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):
{
"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):
{
"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):
{
"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):
{
"session_id": "sess_789",
"topic": "greeting"
}
Request Body (Player Response - Bidirectional Dialogue):
{
"session_id": "sess_789",
"player_response": "What can you tell me about the bandits?"
}
Response (202 Accepted):
{
"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):
{
"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):
{
"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:
{
"character_id": "char_123",
"adjustment": 10
}
Response (200 OK):
{
"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:
{
"character_id": "char_123",
"flag_name": "helped_with_rats",
"flag_value": true
}
Response (200 OK):
{
"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):
{
"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):
{
"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_responsenpc_id(optional): Filter by specific NPCcontext(optional): Filter by message context typedate_from(optional): Start date in ISO formatdate_to(optional): End date in ISO formatlimit(optional): Maximum messages to return (default: 50, max: 100)offset(optional): Number of messages to skip (default: 0)
Response (200 OK):
{
"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):
{
"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:
{
"session_id": "sess_123",
"enemy_ids": ["goblin", "goblin", "goblin_shaman"]
}
Response (200 OK):
{
"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):
{
"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:
{
"combatant_id": "char_456",
"action_type": "attack",
"target_ids": ["goblin_0"],
"ability_id": "shield_bash"
}
Response (200 OK):
{
"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):
{
"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:
{
"combatant_id": "char_456"
}
Response (200 OK - Success):
{
"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:
{
"outcome": "victory"
}
Response (200 OK):
{
"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):
{
"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):
{
"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):
{
"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):
{
"character_id": "char_123",
"check_type": "skill",
"skill": "persuasion",
"dc": 15,
"bonus": 2,
"context": {
"npc_name": "Guard Captain"
}
}
Request Body (Search Action):
{
"character_id": "char_123",
"check_type": "search",
"location_type": "forest",
"dc": 12,
"bonus": 0
}
Request Body (Using Difficulty Name):
{
"character_id": "char_123",
"check_type": "skill",
"skill": "stealth",
"difficulty": "hard",
"bonus": 1
}
Response (200 OK - Skill Check Success):
{
"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):
{
"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_typemust be "search" or "skill"- For skill checks,
skillis required dcordifficultymust 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):
{
"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):
{
"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
{
"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:
{
"meta": {
"page": 1,
"limit": 20,
"total": 100,
"pages": 5
}
}