Characters Service
Manage characters within a campaign, including their statistics, assets, notes, inventory, and supernatural abilities.
Usage
from vclient import characters_service
characters = characters_service(
on_behalf_of="USER_ID",
company_id="COMPANY_ID"
)
The on_behalf_of user ID is required and is sent on every request, including reads. The acting user's role determines what is visible.
Access control
Visibility of STORYTELLER characters depends on the acting user's role:
PLAYERandNPCcharacters are visible to every approved company member.STORYTELLERcharacters are visible only toSTORYTELLERandADMINroles. For aPLAYER:- List results omit
STORYTELLERcharacters (filtering withcharacter_type="STORYTELLER"returns an empty page). - Fetching a
STORYTELLERcharacter by ID, or any of its sub-resources (traits, inventory, notes, assets, statistics), raisesAuthorizationError(403). - Only
STORYTELLERandADMINroles maycreate()a character withtype="STORYTELLER"orupdate()an existing character'stypetoSTORYTELLER. APLAYERattempting either raisesAuthorizationError(403).
NPC management
NPC management is gated by the company's settings.permission_manage_npc setting (ManageNPCPermission):
UNRESTRICTED(default): any approved company member may manage NPC characters.STORYTELLER: onlySTORYTELLERandADMINroles may manage NPC characters. APLAYERattempting any of the following raisesAuthorizationError(403):- Creating an
NPCcharacter, or converting an existing character totype="NPC". - Updating or deleting an
NPCcharacter. - Adding, updating, or deleting an
NPCcharacter's traits or inventory items. - Uploading or deleting an
NPCcharacter's images.
Viewing an NPC character and rolling dice for an NPC are never affected by this setting; any approved member may always access those endpoints.
Player and creator assignment
The user_player_id and user_creator_id fields on Character are both nullable, and their values are constrained by character type:
PLAYERcharacters always have a non-nulluser_player_id.NPCandSTORYTELLERcharacters always haveuser_player_id = null.user_creator_idmay benullif the creating user was deleted.
Assignment rules are enforced server-side and surface as ValidationError (400):
- Creating a
PLAYERcharacter without auser_player_iddefaults it to the acting user. - Creating an
NPCorSTORYTELLERcharacter with a non-nulluser_player_idraisesValidationError(400). - Assigning a
user_player_idto an existingNPCorSTORYTELLERcharacter raisesValidationError(400). - Converting a
PLAYERcharacter toNPCorSTORYTELLERautomatically clearsuser_player_idtonull. No explicituser_player_id=Noneis required in the request. - Converting an
NPCorSTORYTELLERcharacter toPLAYERrequires auser_player_idin the sameupdate()call; omitting it raisesValidationError(400).
Methods
CRUD Operations
| Method | Returns | Description |
|---|---|---|
get(character_id) |
Character |
Get a character by ID |
create(CharacterCreate, **kwargs) |
Character |
Create a new character |
update(character_id, CharacterUpdate, **kwargs) |
Character |
Update a character |
delete(character_id) |
None |
Delete a character |
Pagination
| Method | Returns | Description |
|---|---|---|
get_page(limit?, offset?, campaign_id?, user_player_id?, character_class?, character_type?, status?, is_temporary?) |
PaginatedResponse[Character] |
Get a page of characters with optional filters |
list_all(...) |
list[Character] |
Get all characters (supports same filters) |
iter_all(limit?, ...) |
AsyncIterator[Character] |
Iterate through all characters |
Statistics
| Method | Returns | Description |
|---|---|---|
get_statistics(character_id, num_top_traits?) |
RollStatistics |
Get dice roll statistics |
Roll Analytics
Statistics include success rates, critical frequencies, and most-used traits. Use this data to understand how a character performs in gameplay.
Full Sheet
| Method | Returns | Description |
|---|---|---|
get_full_sheet(character_id, include_available_traits?) |
CharacterFullSheet |
Get hierarchical character sheet with all traits |
get_full_sheet_category(character_id, category_id, include_available_traits?) |
FullSheetTraitCategory |
Get a single category slice of the full sheet |
Sheet Structure
The full sheet returns all traits organized as sections > categories > subcategories > character traits. The skeleton includes all structures for the character's class and game version, even if empty. Use this to render a complete character sheet UI.
Available Traits
Set include_available_traits=True to populate the available_traits field on each category and subcategory with standard traits the character could add. When not set, these lists are always empty. Custom traits are excluded.
Assets
| Method | Returns | Description |
|---|---|---|
get_assets_page(character_id, limit?, offset?) |
PaginatedResponse[Asset] |
Get a page of assets |
list_all_assets(character_id) |
list[Asset] |
Get all assets |
iter_all_assets(character_id, limit?) |
AsyncIterator[Asset] |
Iterate through assets |
get_asset(character_id, asset_id) |
Asset |
Get an asset |
upload_asset(character_id, filename, content) |
Asset |
Upload an asset |
delete_asset(character_id, asset_id) |
None |
Delete an asset |
Notes
| Method | Returns | Description |
|---|---|---|
get_notes_page(character_id, limit?, offset?) |
PaginatedResponse[Note] |
Get a page of notes |
list_all_notes(character_id) |
list[Note] |
Get all notes |
iter_all_notes(character_id, limit?) |
AsyncIterator[Note] |
Iterate through notes |
get_note(character_id, note_id) |
Note |
Get a note |
create_note(character_id, NoteCreate, **kwargs) |
Note |
Create a note |
update_note(character_id, note_id, NoteUpdate, **kwargs) |
Note |
Update a note |
delete_note(character_id, note_id) |
None |
Delete a note |
Inventory
| Method | Returns | Description |
|---|---|---|
get_inventory_page(character_id, limit?, offset?) |
PaginatedResponse[InventoryItem] |
Get a page of inventory items |
list_all_inventory(character_id) |
list[InventoryItem] |
Get all inventory items |
iter_all_inventory(character_id, limit?) |
AsyncIterator[InventoryItem] |
Iterate through items |
get_inventory_item(character_id, item_id) |
InventoryItem |
Get an item |
create_inventory_item(character_id, InventoryItemCreate, **kwargs) |
InventoryItem |
Create an item |
update_inventory_item(character_id, item_id, InventoryItemUpdate, **kwargs) |
InventoryItem |
Update an item |
delete_inventory_item(character_id, item_id) |
None |
Delete an item |
Example
from vclient.models import CharacterCreate, CharacterUpdate, InventoryItemCreate, NoteCreate
# Create a vampire character (preferred method: use model object)
request = CharacterCreate(
campaign_id="campaign_id",
character_class="VAMPIRE",
game_version="V5",
name_first="Marcus",
name_last="Blackwood",
user_player_id="player_user_id"
)
character = await characters.create(request)
# Alternative: pass fields as keyword arguments
character = await characters.create(
campaign_id="campaign_id",
character_class="VAMPIRE",
game_version="V5",
name_first="Marcus",
name_last="Blackwood",
user_player_id="player_user_id"
)
# Update character details
update = CharacterUpdate(name_first="Marcus", name_last="Black")
updated = await characters.update(character.id, update)
# Retrieve roll statistics
stats = await characters.get_statistics(character.id)
print(f"Total rolls: {stats.total_rolls}")
print(f"Success rate: {stats.success_percentage}%")
# Add an inventory item
item_request = InventoryItemCreate(
name="Silver Dagger",
type="weapon",
description="An ornate blade"
)
item = await characters.create_inventory_item(character.id, item_request)
# Create a character note
note_request = NoteCreate(title="Background", content="Born in Victorian London...")
note = await characters.create_note(character.id, note_request)
See Response Models for Character and related types.