Repository navigation
WebSocket API
The module connects to your server as a WebSocket client. Your server sends commands, Foundry executes them and returns results.
The module is the WebSocket client. It opens two outgoing connections, authenticated with the API key from the module settings, and keeps them open for as long as the world is open on the GM's client:
| Channel | Default URL | Serves |
|---|---|---|
| MCP | wss://foundry-mcp.com/ws |
AI assistants over MCP (https://foundry-mcp.com/mcp and the code-mode endpoint https://foundry-mcp.com/mcp/code) |
| API | wss://api.foundry-mcp.com/v1/connect |
The public REST API at api.foundry-mcp.com
|
- Protocol: JSON text frames, one message per frame.
- Reconnect: the delay doubles from the configured base (5 s) up to a 60 s ceiling with up to 1 s of jitter. By default the module keeps trying while the world is open (Max Reconnect Attempts = 0).
- Close codes (since 8.15.0): two application close codes from the server change that schedule.
4001(reasonMissing API key/Invalid API key) means the key was rejected after the upgrade: the module stops reconnecting, shows a permanent red notification and waits for the GM to fix the key and reload the world.4002(reasonreplaced by a newer connection with the same API key) means another Foundry client took over the key: the module waits 120 s (plus jitter) before reconnecting, without counting the wait as a backoff attempt, and shows a yellow notification. Every other code (1000,1005,1006, …) keeps the exponential backoff above. A server that closes without a code is treated like a network drop. - API key format: the module normalizes the key from the settings before connecting (all whitespace and invisible characters removed, anywhere in the string) and refuses to connect when the result does not look like a key: no
pk_prefix, characters outsideA-Za-z0-9_-+/=, or a length outside 20..64. The server's4001remains the final check. - Heartbeat: the module sends
{ "type": "ping", "ts": <ms> }every 25 s (the first one a second after connecting) and expects{ "type": "pong" }. Once a server has answered a ping, an unanswered one (10 s) closes the socket and the module reconnects at once. Ping and pong frames never reach the command router. - Legacy aliases:
roll-ability,roll-skill,roll-save,roll-attack,roll-damage,roll-perception,use-itemandactivate-itemare aliases of thednd5e/*commands and require the D&D 5e system.
interface Command {
id: string; // Unique identifier for correlation
type: CommandType;
params: object;
}interface CommandResponse {
id: string; // Same as command ID
success: boolean;
data?: object; // Result on success
error?: string; // Error message on failure
}Execute arbitrary dice formula. Formulas longer than 200 characters or rolling more than 1000 dice are rejected before evaluation (Roll formula asks for too many dice (N > 1000)).
Params:
interface RollDiceParams {
formula: string; // "1d20+5", "2d6", "4d6kh3"
showInChat?: boolean; // Display in Foundry chat (default: false)
flavor?: string; // Chat message flavor text
}Result:
interface RollResult {
total: number;
formula: string;
dice: DiceResult[];
isCritical?: boolean;
isFumble?: boolean;
mode?: 'advantage' | 'normal' | 'disadvantage'; // What dnd5e applied to a d20 roll (module 8.14+)
kept?: number; // The d20 result that stayed after kh/kl
}
interface DiceResult {
type: string; // "d20", "d6"
count: number;
results: number[];
}Example:
// Command
{ "id": "1", "type": "roll-dice", "params": { "formula": "1d20+5", "showInChat": true } }
// Response
{ "id": "1", "success": true, "data": { "total": 18, "formula": "1d20+5", "dice": [{ "type": "d20", "count": 1, "results": [13] }] } }Roll a D&D 5e skill check.
Naming (v8.6.0+): This is the canonical, system-namespaced command (
<system>/<command>). The legacyroll-skillname still works as a deprecated alias that routes to the same handler, so existing integrations are unaffected — new integrations should preferdnd5e/roll-skill.
Params:
interface RollSkillParams {
actorId: string;
skill: SkillKey; // See skill keys table
advantage?: boolean; // Roll 2d20kh (module 8.13+)
disadvantage?: boolean; // Roll 2d20kl; both flags at once → validation error
showInChat?: boolean;
}Skill Keys:
| Key | Skill | Key | Skill |
|---|---|---|---|
acr |
Acrobatics | med |
Medicine |
ani |
Animal Handling | nat |
Nature |
arc |
Arcana | prc |
Perception |
ath |
Athletics | prf |
Performance |
dec |
Deception | per |
Persuasion |
his |
History | rel |
Religion |
ins |
Insight | slt |
Sleight of Hand |
itm |
Intimidation | ste |
Stealth |
inv |
Investigation | sur |
Survival |
Example:
// Canonical name (v8.6.0+); the legacy "roll-skill" is still accepted as a deprecated alias
{ "id": "2", "type": "dnd5e/roll-skill", "params": { "actorId": "abc123", "skill": "ste" } }
{ "id": "3", "type": "dnd5e/roll-skill", "params": { "actorId": "abc123", "skill": "ste", "disadvantage": true } }Roll a D&D 5e saving throw.
Naming (v8.7.0+): Canonical, system-namespaced name. The legacy
roll-savestill works as a deprecated alias routing to the same handler.
Params:
interface RollSaveParams {
actorId: string;
ability: AbilityKey; // "str", "dex", "con", "int", "wis", "cha"
advantage?: boolean; // Roll 2d20kh (module 8.13+)
disadvantage?: boolean; // Roll 2d20kl; both flags at once → validation error
showInChat?: boolean;
}Roll a D&D 5e ability check.
Naming (v8.7.0+): Canonical, system-namespaced name. The legacy
roll-abilitystill works as a deprecated alias routing to the same handler.
Params:
interface RollAbilityParams {
actorId: string;
ability: AbilityKey;
advantage?: boolean; // Roll 2d20kh (module 8.13+)
disadvantage?: boolean; // Roll 2d20kl; both flags at once → validation error
showInChat?: boolean;
}Roll an attack with a weapon/spell.
Naming (v8.7.0+): Canonical, system-namespaced name. The legacy
roll-attackstill works as a deprecated alias routing to the same handler.
Params:
interface RollAttackParams {
actorId: string;
itemId: string; // Weapon/spell item ID
advantage?: boolean;
disadvantage?: boolean;
showInChat?: boolean;
}With Midi-QOL active the flags are also passed as midiOptions.advantage/disadvantage and midiOptions.workflowOptions.advantage/disadvantage (module 8.14+): Midi rebuilds advantage from its own tracker, which only reads those. The result's mode and kept show what was applied.
Roll damage for a weapon/spell.
Naming (v8.7.0+): Canonical, system-namespaced name. The legacy
roll-damagestill works as a deprecated alias routing to the same handler.
Params:
interface RollDamageParams {
actorId: string;
itemId: string;
critical?: boolean; // Double dice
showInChat?: boolean;
}Roll the actor's Perception (prc) skill check. Returns the standard RollResult. The bare roll-perception is a legacy alias.
Params:
interface RollPerceptionParams {
actorId: string;
advantage?: boolean; // Roll 2d20kh (module 8.13+)
disadvantage?: boolean; // Roll 2d20kl; both flags at once → validation error
showInChat?: boolean;
}Example:
{ "id": "1", "type": "dnd5e/roll-perception", "params": { "actorId": "abc123", "showInChat": true } }Specification-based search over Actor compendium packs (bestiaries) with the filter-actors filter surface. D&D 5e worlds only (Operation … is not supported by game system '<world>' otherwise). World-only filters (folder, hasPlayerOwner, currentHp) are intentionally absent.
Params:
interface FilterCompendiumActorsParams {
packIds?: string[]; // Explicit Actor packs (unknown/wrong-type pack → error); omitted → all Actor packs
name?: string; // Substring, case-insensitive
type?: string[]; // "character" | "npc" | "vehicle" | "group"
creatureType?: string[]; // 14 D&D creature types, e.g. ["undead"]
size?: string[]; // "tiny" | "sm" | "med" | "lg" | "huge" | "grg"
disposition?: string[]; // "hostile" | "neutral" | "friendly" | "secret"
cr?: { min?: number; max?: number }; // Bounds must be legal CRs (0, 0.125, 0.25, 0.5, 1..30)
level?: { min?: number; max?: number }; // Character levels
maxHp?: { min?: number; max?: number };
ac?: { min?: number; max?: number };
abilities?: { str?: {min?: number; max?: number}; /* dex, con, int, wis, cha */ };
limit?: number; // 1..200, default 50
offset?: number;
}Result:
interface FilterCompendiumActorsResult {
results: { id: string; name: string; packId: string; uuid: string }[];
total: number;
hasMore: boolean;
}Example:
{ "id": "1", "type": "dnd5e/filter-compendium-actors",
"params": { "creatureType": ["undead"], "cr": { "min": 5, "max": 10 } } }The filter-items surface over Item compendium packs: type (13 D&D item types), rarity, spellSchool, requiresAttunement, identified, hasActivities, isContainer, weight/price/spellLevel ranges, plus packIds/name/limit/offset as above (the world-only folder filter is absent). Result shape matches dnd5e/filter-compendium-actors — entries carry packId and uuid for follow-up get-compendium-document / import calls.
{ "id": "1", "type": "dnd5e/filter-compendium-items",
"params": { "type": ["spell"], "spellLevel": { "min": 1, "max": 3 }, "spellSchool": ["evocation"] } }Get information about the current Foundry world, system, version, entity counts, and available compendiums.
Params:
// No parameters required
interface GetWorldInfoParams {}Result:
interface WorldInfoResult {
world: WorldInfoData;
counts: WorldCounts;
compendiumMeta: CompendiumMetaSummary[];
}
interface WorldInfoData {
id: string;
title: string;
system: string; // e.g., "dnd5e"
systemVersion: string; // e.g., "4.3.0"
foundryVersion: string; // e.g., "13.331"
}
interface WorldCounts {
journals: number;
actors: number;
items: number;
scenes: number;
}
interface CompendiumMetaSummary {
id: string; // e.g., "dnd5e.monsters"
label: string; // e.g., "Monsters (SRD)"
type: string; // "Actor", "Item", "JournalEntry", etc.
system: string;
count: number; // Number of entries in the compendium
}Example:
// Command
{ "id": "1", "type": "get-world-info", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": {
"world": {
"id": "my-campaign",
"title": "Curse of Strahd",
"system": "dnd5e",
"systemVersion": "4.3.0",
"foundryVersion": "13.331"
},
"counts": { "journals": 42, "actors": 85, "items": 120, "scenes": 15 },
"compendiumMeta": [
{ "id": "dnd5e.monsters", "label": "Monsters (SRD)", "type": "Actor", "system": "dnd5e", "count": 325 },
{ "id": "dnd5e.items", "label": "Items (SRD)", "type": "Item", "system": "dnd5e", "count": 680 }
]
}
}Get a summary list of all actors in the world.
Params:
// No parameters required
interface GetActorsParams {}Result:
type GetActorsResult = ActorSummary[];
interface ActorSummary {
id: string;
name: string;
type: string; // "character", "npc", "vehicle", etc.
img: string;
}Example:
// Command
{ "id": "1", "type": "get-actors", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": [
{ "id": "actor-1", "name": "Aragorn", "type": "character", "img": "portraits/aragorn.png" },
{ "id": "actor-2", "name": "Goblin", "type": "npc", "img": "tokens/goblin.png" }
]
}Get detailed information about a single actor, including system data and items.
Params:
interface GetActorParams {
actorId: string;
}Result:
interface ActorDetailResult {
id: string;
name: string;
type: string;
img: string;
system: Record<string, unknown>; // Full system data (abilities, HP, AC, etc.)
items: ItemSummary[];
}
interface ItemSummary {
id: string;
name: string;
type: string;
img: string;
system: Record<string, unknown>;
}Example:
// Command
{ "id": "1", "type": "get-actor", "params": { "actorId": "actor-1" } }
// Response
{
"id": "1",
"success": true,
"data": {
"id": "actor-1",
"name": "Aragorn",
"type": "character",
"img": "portraits/aragorn.png",
"system": {
"abilities": { "str": { "value": 18 }, "dex": { "value": 14 } },
"attributes": { "hp": { "value": 85, "max": 85 }, "ac": { "value": 18 } }
},
"items": [
{ "id": "item-1", "name": "Longsword", "type": "weapon", "img": "icons/sword.png", "system": {} }
]
}
}Search for actors with structured D&D 5e-aware filters. All filters combined with AND, multiple values within array filters with OR. Returns minimal { id, name } summaries — use get-actor for details.
Params:
interface FilterActorsParams {
// String filters
name?: string; // substring, case-insensitive, trimmed
type?: string[]; // strict enum: 'character' | 'npc' | 'vehicle' | 'group'
creatureType?: string[]; // 14 SRD types: 'aberration', 'beast', 'celestial', 'construct',
// 'dragon', 'elemental', 'fey', 'fiend', 'giant', 'humanoid',
// 'monstrosity', 'ooze', 'plant', 'undead'
size?: string[]; // strict codes: 'tiny' | 'sm' | 'med' | 'lg' | 'huge' | 'grg'
disposition?: string[]; // 'hostile' | 'neutral' | 'friendly' | 'secret'
// Boolean
hasPlayerOwner?: boolean;
// Numeric range filters (inclusive bounds, both fields optional)
cr?: { min?: number; max?: number }; // strict D&D values: 0, 0.125, 0.25, 0.5, 1, 2, ..., 30
level?: { min?: number; max?: number }; // integer ≥ 0 (PC level)
maxHp?: { min?: number; max?: number };
currentHp?: { min?: number; max?: number };
ac?: { min?: number; max?: number };
abilities?: {
str?: { min?: number; max?: number };
dex?: { min?: number; max?: number };
con?: { min?: number; max?: number };
int?: { min?: number; max?: number };
wis?: { min?: number; max?: number };
cha?: { min?: number; max?: number };
};
// Folder
folder?: {
id?: string;
name?: string;
recursive?: boolean; // default false
};
// Pagination
limit?: number; // 1-200, default 50
offset?: number; // ≥ 0, default 0
}Result:
interface FilterActorsResult {
results: Array<{ id: string; name: string }>;
total: number; // total count after filtering, before pagination
hasMore: boolean; // total > offset + results.length
}Behavior notes:
- All filters combine via AND. Within array filters, values combine via OR.
- "Silent exclude": if an actor lacks the field being filtered (e.g. PC has no CR, vehicle has no abilities), it's excluded from results without error.
- Sorting: name ASC, id as tiebreaker. Stable across pages.
- No filters → returns full list paginated (use
get-actorsfor the simpler full-list contract). - Strict validation: invalid CR values, out-of-range limits, unknown enum values produce errors with descriptive messages.
- World actors only (compendium search is a separate concern).
Examples:
// Find CR 1/4 monsters in the "Goblins" folder
{
"id": "1",
"type": "filter-actors",
"params": {
"type": ["npc"],
"cr": { "min": 0.25, "max": 0.25 },
"folder": { "name": "Goblins", "recursive": true }
}
}
// Find tough hostile dragons (CR 15+, AC 18+)
{
"id": "2",
"type": "filter-actors",
"params": {
"creatureType": ["dragon"],
"disposition": ["hostile"],
"cr": { "min": 15 },
"ac": { "min": 18 }
}
}
// Find PCs by name fragment
{
"id": "3",
"type": "filter-actors",
"params": {
"type": ["character"],
"name": "fro"
}
}
// Response (minimal entries — call get-actor for details)
{
"id": "3",
"success": true,
"data": {
"results": [
{ "id": "actor-frodo", "name": "Frodo Baggins" }
],
"total": 1,
"hasMore": false
}
}
// Pagination through a large set
{
"id": "4",
"type": "filter-actors",
"params": {
"type": ["npc"],
"limit": 50,
"offset": 100
}
}Create a new actor in the world.
Params:
interface CreateActorParams {
name: string;
type: string; // "character", "npc", "vehicle", etc.
folder?: string; // Folder ID
img?: string; // Portrait image path
system?: object; // System-specific data (abilities, hp, etc.)
}Result:
interface ActorResult {
id: string;
uuid: string;
name: string;
type: string;
img: string;
folder: string | null;
}Example:
// Command - create NPC
{
"id": "1",
"type": "create-actor",
"params": {
"name": "Goblin Archer",
"type": "npc",
"system": {
"abilities": { "str": { "value": 8 }, "dex": { "value": 14 } },
"attributes": { "hp": { "value": 7, "max": 7 } }
}
}
}
// Response
{
"id": "1",
"success": true,
"data": {
"id": "actor-xyz",
"uuid": "Actor.actor-xyz",
"name": "Goblin Archer",
"type": "npc",
"img": "icons/svg/mystery-man.svg",
"folder": null
}
}Import actor from a compendium pack.
Params:
interface CreateActorFromCompendiumParams {
packId: string; // Compendium ID, e.g., "dnd5e.monsters"
actorId: string; // Actor ID within compendium
name?: string; // Override name
folder?: string; // Target folder ID
}Result: ActorResult
Example:
// Command - import Goblin from SRD
{
"id": "1",
"type": "create-actor-from-compendium",
"params": {
"packId": "dnd5e.monsters",
"actorId": "goblin-id-in-compendium",
"name": "Sneaky Goblin"
}
}Update actor properties.
Params:
interface UpdateActorParams {
actorId?: string; // World actor. Either this or tokenId is required.
tokenId?: string; // One token's actor instead (module 8.14+): the synthetic actor of an unlinked token
sceneId?: string; // Scene the token is on; defaults to the active scene
name?: string;
img?: string;
folder?: string;
system?: object; // Partial update of system data
}Result: ActorResult, with tokenId echoed when a token was addressed.
Example:
// Command - update HP
{
"id": "1",
"type": "update-actor",
"params": {
"actorId": "abc123",
"system": {
"attributes": { "hp": { "value": 25 } }
}
}
}Apply damage through dnd5e's Actor5e#applyDamage (module 8.14+): temp HP absorbs it first, and resistances, immunities and vulnerabilities apply when a damage type is given. The bare apply-damage is an alias.
Params:
interface ApplyDamageParams {
actorId?: string; // World actor. Either this or tokenId is required.
tokenId?: string; // One token's actor: an unlinked token takes the hit alone
sceneId?: string; // Defaults to the active scene
amount: number; // Before resistances
type?: string; // dnd5e damage type: "slashing", "fire", …
}Result:
interface HitPointsChangeResult {
actorId: string;
tokenId?: string;
hpBefore: number;
hpAfter: number;
tempBefore: number;
tempAfter: number;
maxHp: number;
}Example:
{ "id": "1", "type": "apply-damage", "params": { "tokenId": "goblin-3", "amount": 7, "type": "slashing" } }Heal through Actor5e#applyDamage with the healing type (module 8.14+). The bare apply-healing is an alias.
Params: { actorId?: string, tokenId?: string, sceneId?: string, amount: number }
Result: HitPointsChangeResult (see dnd5e/apply-damage).
Delete actor from the world.
Params:
interface DeleteActorParams {
actorId: string;
}Result: DeleteResult
Send a message to Foundry VTT chat. Supports in-character, out-of-character, and emote styles. Can optionally whisper to specific users.
Params:
interface SendChatMessageParams {
content: string; // Message text (HTML supported)
speaker?: string; // Speaker alias override
actorId?: string; // Actor ID — sets speaker to actor's name
flavor?: string; // Flavor text shown above the message
whisperTo?: string[]; // User IDs to whisper to
type?: 'ic' | 'ooc' | 'emote'; // Message style (default: standard)
}Result:
interface SendChatMessageResult {
messageId: string;
sent: boolean;
}Example:
// Command - in-character speech
{
"id": "1",
"type": "send-chat-message",
"params": {
"content": "You shall not pass!",
"actorId": "gandalf-actor-id",
"type": "ic"
}
}
// Response
{ "id": "1", "success": true, "data": { "messageId": "msg-001", "sent": true } }
// Command - whisper to player
{
"id": "2",
"type": "send-chat-message",
"params": {
"content": "You notice a hidden trap in the floor.",
"whisperTo": ["player-user-id"],
"flavor": "Perception DC 15"
}
}Retrieve chat messages with filtering and pagination. Returns messages from most recent, with configurable limit.
Params:
interface GetChatMessagesParams {
limit?: number; // Number of messages to return (default: 20, max: 100)
since?: string; // Message ID — return messages after this one
before?: string; // Message ID — return messages before this one
includeRolls?: boolean; // Include roll formula/total summaries (default: false)
authorId?: string; // Filter by author user ID
actorId?: string; // Filter by speaker actor ID
type?: 'ic' | 'ooc' | 'emote' | 'roll'; // Filter by message style
search?: string; // Full-text search in message content
}Result:
interface GetChatMessagesResult {
messages: ChatMessageData[];
total: number; // Total messages in chat log
hasMore: boolean; // More messages available beyond limit
}
interface ChatMessageData {
id: string;
timestamp: number;
author: {
userId: string;
name: string;
};
speaker: ChatMessageSpeaker;
content: string; // HTML stripped to plain text
flavor: string | null;
style: 'other' | 'ooc' | 'ic' | 'emote';
isRoll: boolean;
rolls?: ChatMessageRollSummary[]; // Only when includeRolls: true
whisper: string[];
isWhisper: boolean;
}
interface ChatMessageSpeaker {
alias: string;
actorId: string | null;
tokenId: string | null;
}
interface ChatMessageRollSummary {
formula: string;
total: number;
}Example:
// Command - get last 10 messages with rolls
{
"id": "1",
"type": "get-chat-messages",
"params": { "limit": 10, "includeRolls": true }
}
// Response
{
"id": "1",
"success": true,
"data": {
"messages": [
{
"id": "msg-100",
"timestamp": 1713200000,
"author": { "userId": "gm-user", "name": "Game Master" },
"speaker": { "alias": "GM", "actorId": null, "tokenId": null },
"content": "Roll for initiative!",
"flavor": null,
"style": "ooc",
"isRoll": false,
"whisper": [],
"isWhisper": false
}
],
"total": 250,
"hasMore": true
}
}
// Command - search for messages mentioning "dragon"
{ "id": "2", "type": "get-chat-messages", "params": { "search": "dragon", "limit": 5 } }Update the content or flavor of an existing chat message.
Params:
interface UpdateChatMessageParams {
messageId: string;
content?: string; // New message content
flavor?: string; // New flavor text
}Result:
interface SendChatMessageResult {
messageId: string;
sent: boolean;
}Example:
// Command
{ "id": "1", "type": "update-chat-message", "params": { "messageId": "msg-100", "content": "Updated text" } }
// Response
{ "id": "1", "success": true, "data": { "messageId": "msg-100", "sent": true } }Delete a single chat message by ID.
Params:
interface DeleteChatMessageParams {
messageId: string;
}Result: DeleteResult
Example:
{ "id": "1", "type": "delete-chat-message", "params": { "messageId": "msg-100" } }
{ "id": "1", "success": true, "data": { "deleted": true } }Delete all messages from the chat log.
Params:
// No parameters requiredResult:
interface ClearChatResult {
deletedCount: number;
}Example:
// Command
{ "id": "1", "type": "clear-chat", "params": {} }
// Response
{ "id": "1", "success": true, "data": { "deletedCount": 142 } }Export the entire chat log as text or JSON.
Params:
interface ExportChatParams {
format?: 'text' | 'json'; // Default: 'text'
}Result:
interface ExportChatResult {
content: string; // Exported content (plain text or JSON string)
messageCount: number;
}Example:
// Command - export as text
{ "id": "1", "type": "export-chat", "params": { "format": "text" } }
// Response
{
"id": "1",
"success": true,
"data": {
"content": "[Game Master] Roll for initiative!\n[Aragorn] I attack the goblin!",
"messageCount": 2
}
}
// Command - export as JSON
{ "id": "2", "type": "export-chat", "params": { "format": "json" } }Get a list of all journal entries in the world. With light: true the result is an index without page text, which is what the server uses for lists and search.
Params:
interface GetJournalsParams {
light?: boolean; // Index only: pages carry id/name/type/src but no text, markdown or enrichedText,
// and enrichment is skipped — fast on large worlds (module 8.12+)
}Result:
type GetJournalsResult = JournalData[];
interface JournalData {
id: string;
uuid: string;
name: string;
folder: string | null;
pages: JournalPageData[];
}
interface JournalPageData {
id: string;
name: string;
type: string; // "text", "image", "video", "pdf"
text: string | null;
markdown: string | null;
enrichedText: string | null; // Foundry-enriched HTML (@UUID links resolved, inline rolls, etc.)
src: string | null; // Source URL for image/video/pdf pages
}Example:
// Command
{ "id": "1", "type": "get-journals", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": [
{
"id": "journal-1",
"uuid": "JournalEntry.journal-1",
"name": "Session Notes",
"folder": null,
"pages": [
{ "id": "page-1", "name": "Session 1", "type": "text", "text": "<p>The party met at the tavern...</p>", "markdown": null }
]
}
]
}Get a single journal entry with full page content.
Params:
interface GetJournalParams {
journalId: string;
}Result: JournalData
Example:
{ "id": "1", "type": "get-journal", "params": { "journalId": "journal-1" } }Create new journal entry.
Params:
interface CreateJournalParams {
name: string;
folder?: string; // Folder ID
content?: string; // HTML content for first page
pageType?: "text" | "image" | "video" | "pdf";
}Result:
interface JournalResult {
id: string;
name: string;
folder: string | null;
pages: JournalPageResult[];
}Update journal properties.
Params:
interface UpdateJournalParams {
journalId: string;
name?: string;
folder?: string;
}Delete journal entry.
Params:
interface DeleteJournalParams {
journalId: string;
}Result:
interface DeleteResult {
deleted: boolean;
}Add page to existing journal.
Params:
interface CreateJournalPageParams {
journalId: string;
name: string;
type?: "text" | "image" | "video" | "pdf";
content?: string; // HTML for text pages
src?: string; // Source URL for image/video/pdf pages
}Update page content.
Params:
interface UpdateJournalPageParams {
journalId: string;
pageId: string;
name?: string;
content?: string;
src?: string; // Source URL for image/video/pdf pages
}Remove page from journal.
Params:
interface DeleteJournalPageParams {
journalId: string;
pageId: string;
}Show a journal entry or specific page to players. Pushes the journal to their screens.
Params:
interface ShowJournalParams {
journalId: string;
pageId?: string; // Show specific page (default: entire journal)
force?: boolean; // Override permissions (default: false)
users?: string[]; // Specific user IDs (default: all players)
}Result:
interface ShowJournalResult {
shown: boolean;
journalId: string;
journalName: string;
pageId?: string;
}Example:
// Command - show journal to all players
{ "id": "1", "type": "show-journal", "params": { "journalId": "journal-quest" } }
// Response
{ "id": "1", "success": true, "data": { "shown": true, "journalId": "journal-quest", "journalName": "Quest Log" } }
// Command - show specific page, force override permissions
{
"id": "2",
"type": "show-journal",
"params": {
"journalId": "journal-quest",
"pageId": "page-handout",
"force": true
}
}
// Command - show to specific player
{
"id": "3",
"type": "show-journal",
"params": {
"journalId": "journal-secret",
"users": ["player-rogue-id"]
}
}Create new combat encounter.
Params:
interface CreateCombatParams {
sceneId?: string; // Defaults to active scene
activate?: boolean; // Make active combat
}Result:
interface CombatResult {
id: string;
round: number;
turn: number;
started: boolean;
combatants: CombatantResult[];
current: CombatantResult | null;
}
interface CombatantResult {
id: string;
actorId: string;
tokenId: string | null;
name: string;
img: string;
initiative: number | null;
defeated: boolean;
hidden: boolean;
}Add actor to combat.
Params:
interface AddCombatantParams {
actorId: string;
combatId?: string; // Defaults to active combat
tokenId?: string;
initiative?: number; // Pre-set initiative
hidden?: boolean;
}Remove from combat.
Params:
interface RemoveCombatantParams {
combatantId: string;
combatId?: string;
}Begin combat (sets round to 1).
Params:
interface CombatIdParams {
combatId?: string;
}End and delete combat. Shows confirmation dialog in Foundry VTT v13.
Params: CombatIdParams
Delete combat directly without confirmation dialog. Useful for programmatic combat management, especially in Foundry VTT v13 where end-combat shows a confirmation dialog.
Params: CombatIdParams
Result:
interface DeleteResult {
deleted: boolean;
}Example:
// Command
{ "id": "1", "type": "delete-combat", "params": {} }
// Response
{ "id": "1", "success": true, "data": { "deleted": true } }
// With specific combat ID
{ "id": "2", "type": "delete-combat", "params": { "combatId": "abc123" } }Navigate turn order.
Params: CombatIdParams
Set the current turn to a specific combatant.
Params:
interface SetTurnParams {
combatantId: string; // Combatant to set as current turn
combatId?: string;
}Result: CombatResult
Example:
// Command
{ "id": "1", "type": "set-turn", "params": { "combatantId": "combatant-xyz" } }
// Response
{
"id": "1",
"success": true,
"data": {
"id": "combat-1",
"round": 2,
"turn": 3,
"started": true,
"combatants": [],
"current": { "id": "combatant-xyz", "actorId": "actor-1", "tokenId": "token-1", "name": "Goblin", "img": "tokens/goblin.png", "initiative": 15, "defeated": false, "hidden": false }
}
}Get current combat state.
Params: CombatIdParams
Result: CombatResult
Get rich contextual information about the current combat turn, including the active combatant's stats, nearby tokens with distances, line-of-sight data, and an ASCII map of the battlefield zoomed around the current combatant.
Params:
interface GetCombatTurnContextParams {
combatId?: string; // Defaults to active combat
}Result:
interface CombatTurnContext {
round: number;
turn: number;
currentCombatant: TurnCombatantInfo;
nearbyTokens: NearbyTokenInfo[];
asciiMap: string; // ASCII art map of the battlefield (zoomed to 12-grid radius)
}
interface TurnCombatantInfo {
id: string; // Combatant ID
actorId: string;
tokenId: string;
name: string;
gridX: number; // Grid coordinates
gridY: number;
hp?: { value: number; max: number };
ac?: number;
conditions: string[]; // Active status effect IDs
}
interface NearbyTokenInfo {
tokenId: string;
actorId: string | null;
name: string;
gridX: number;
gridY: number;
distanceFt: number; // Distance in feet (Chebyshev / grid distance)
disposition: string; // "hostile", "neutral", "friendly"
hp?: { value: number; max: number };
ac?: number;
conditions: string[];
lineOfSight: boolean; // Whether the current combatant can see this token
}Example:
// Command
{ "id": "1", "type": "get-combat-turn-context", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": {
"round": 2,
"turn": 0,
"currentCombatant": {
"id": "combatant-1",
"actorId": "actor-fighter",
"tokenId": "token-fighter",
"name": "Fighter",
"gridX": 5,
"gridY": 5,
"hp": { "value": 45, "max": 52 },
"ac": 18,
"conditions": []
},
"nearbyTokens": [
{
"tokenId": "token-goblin-1",
"actorId": "actor-goblin",
"name": "Goblin",
"gridX": 6,
"gridY": 5,
"distanceFt": 5,
"disposition": "hostile",
"hp": { "value": 5, "max": 7 },
"ac": 15,
"conditions": [],
"lineOfSight": true
}
],
"asciiMap": "... (ASCII representation of battlefield) ..."
}
}Roll initiative for specific combatants.
Params:
interface RollInitiativeParams {
combatantIds: string[];
combatId?: string;
formula?: string; // Custom formula, uses system default if omitted
}Result:
interface InitiativeRollResult {
results: Array<{
combatantId: string;
name: string;
initiative: number;
}>;
}Manually set initiative value.
Params:
interface SetInitiativeParams {
combatantId: string;
initiative: number;
combatId?: string;
}Roll initiative for all combatants.
Params:
interface RollAllInitiativeParams {
combatId?: string;
formula?: string;
npcsOnly?: boolean; // Roll only for NPCs
}Update combatant properties.
Params:
interface UpdateCombatantParams {
combatantId: string;
combatId?: string;
initiative?: number;
defeated?: boolean;
hidden?: boolean;
}Mark combatant as defeated.
Params:
interface SetCombatantDefeatedParams {
combatantId: string;
defeated: boolean;
combatId?: string;
}Toggle hidden status.
Params:
interface ToggleCombatantVisibilityParams {
combatantId: string;
combatId?: string;
}Place token on scene.
Params:
interface CreateTokenParams {
actorId: string;
x: number;
y: number;
sceneId?: string; // Defaults to active scene
hidden?: boolean;
elevation?: number;
rotation?: number; // Degrees
scale?: number;
actorLink?: boolean; // Linked to the actor (shared HP/effects) or an independent copy (module 8.14+)
}Result:
interface TokenResult {
id: string;
name: string;
actorId: string | null;
x: number;
y: number;
elevation: number;
rotation: number;
hidden: boolean;
img: string;
disposition: number; // -1: hostile, 0: neutral, 1: friendly
actorLink: boolean; // Shares its actor's data (linked) or is an independent copy (module 8.14+)
hp?: { value: number; max: number };
ac?: number;
conditions: string[];
warning?: string; // "unlinked_character_token": a player character placed as an unlinked copy
}Remove token from scene.
Params:
interface DeleteTokenParams {
tokenId: string;
sceneId?: string;
}Move token to new position. Uses A* pathfinding to navigate around walls and optionally through doors.
Params:
interface MoveTokenParams {
tokenId: string;
x: number;
y: number;
sceneId?: string;
elevation?: number;
rotation?: number;
animate?: boolean; // Default: true
canOpenDoors?: boolean; // Allow opening doors along path
}Result:
interface TokenResult {
id: string;
name: string;
actorId: string | null;
x: number;
y: number;
width: number;
height: number;
elevation: number;
rotation: number;
hidden: boolean;
img: string;
disposition: number;
actorLink: boolean; // Linked to its actor or an independent copy (module 8.14+)
hp?: { value: number; max: number };
ac?: number;
conditions: string[];
pathCost?: number; // Movement cost in grid units (when pathfinding used)
doorsOpened?: string[]; // Wall IDs of doors opened during movement
warning?: string; // "unlinked_character_token"
}Update token properties.
Params:
interface UpdateTokenParams {
tokenId: string;
sceneId?: string;
hidden?: boolean;
elevation?: number;
rotation?: number;
scale?: number;
name?: string;
displayName?: number;
disposition?: number; // -1, 0, 1
lockRotation?: boolean;
actorLink?: boolean; // Link or unlink the token from its actor (module 8.14+)
}Get all tokens from scene.
Params:
interface GetSceneTokensParams {
sceneId?: string; // Defaults to active scene
}Result:
interface SceneTokensResult {
sceneId: string;
sceneName: string;
tokens: TokenResult[];
}Get one token on a scene with its actor link, position, HP and AC.
Params:
interface GetTokenParams {
sceneId?: string; // Defaults to the active scene
tokenId: string;
}Result:
interface TokenDetail {
id: string;
sceneId: string;
name: string;
x: number;
y: number;
width: number;
height: number;
elevation: number;
rotation: number;
hidden: boolean;
disposition: 'hostile' | 'neutral' | 'friendly' | 'secret';
actorId: string | null;
actorLink: boolean;
textureSrc: string;
hp: { current: number; max: number } | null;
ac: number | null;
}Find the token that represents an actor on a scene.
Params:
interface GetTokenByActorParams {
sceneId?: string; // Defaults to the active scene
actorId: string;
}Result: TokenDetail (see get-token).
List the tokens within a distance of a point, nearest first. The origin is in pixels; range and distance are in the scene's grid units. Distance uses Chebyshev geometry (a diagonal step counts as one grid space), measured from the origin to the nearest edge of each token.
Params:
interface GetTokensInRangeParams {
sceneId?: string; // Defaults to the active scene
originX: number; // Pixels
originY: number;
range: number; // Grid units, e.g. feet
excludeTokenId?: string; // Leave out the origin token itself
}Result:
interface GetTokensInRangeResult {
sceneId: string;
origin: { x: number; y: number };
range: number;
units: string; // Scene grid units, e.g. "ft"
tokens: Array<{
id: string;
name: string;
x: number;
y: number;
distance: number;
actorId: string | null;
disposition: 'hostile' | 'neutral' | 'friendly' | 'secret';
}>;
}Target or untarget a token on the active scene for the GM user.
Params:
interface SetTokenTargetParams {
tokenId: string;
targeted: boolean;
releaseOthers?: boolean; // Drop the existing targets first (default true)
}Result:
interface SetTokenTargetResult {
tokenId: string;
targeted: boolean;
}Release every token the GM user is targeting.
Params: none ({}).
Result:
interface ClearTargetsResult {
cleared: true;
count: number; // Targets released
}Get detailed information about a scene, including grid, darkness, notes, walls, lights, tiles, drawings, regions, and tokens with grid coordinates.
Params:
interface GetSceneParams {
sceneId?: string; // Defaults to active scene
includeScreenshot?: boolean; // Include base64 screenshot of the scene
}Result:
interface SceneDetailResult {
id: string;
name: string;
active: boolean;
img: string;
width: number;
height: number;
grid: {
size: number; // Grid size in pixels (default: 100)
type: number; // 1 = square, 2 = hex rows, 3 = hex columns
units: string; // "ft", "m", etc.
distance: number; // Distance per grid square (e.g., 5)
};
darkness: number; // 0 (bright) to 1 (fully dark)
notes: SceneNote[];
walls: SceneWall[];
lights: SceneLight[];
tiles: SceneTile[];
drawings: SceneDrawing[];
regions: SceneRegion[];
tokens: SceneTokenSummary[];
asciiMap: string; // ASCII art representation of the scene
screenshot?: SceneScreenshot; // Only when includeScreenshot: true
}
interface SceneScreenshot {
image: string; // Base64-encoded image data
mimeType: string; // e.g., "image/webp"
width: number;
height: number;
}
interface SceneNote {
x: number;
y: number;
text: string;
label: string;
entryId: string | null;
}
interface SceneWall {
id: string;
c: number[]; // [x1, y1, x2, y2]
move: number; // 0 = none, 20 = blocks movement
sense: number; // 0 = none, 20 = blocks vision
door: number; // 0 = wall, 1 = door, 2 = secret door
ds: number; // Door state: 0 = closed, 1 = open, 2 = locked
}
interface SceneLight {
x: number;
y: number;
bright: number; // Bright light radius in grid units
dim: number; // Dim light radius in grid units
color: string | null; // e.g., "#ff9900", null = white
angle: number; // Emission angle (360 = omnidirectional)
walls: boolean; // Blocked by walls
hidden: boolean;
}
interface SceneTile {
x: number;
y: number;
width: number;
height: number;
img: string; // Image file path
hidden: boolean;
elevation: number;
rotation: number; // Degrees
}
interface SceneDrawing {
x: number;
y: number;
shape: {
type: string; // "r" = rectangle, "e" = ellipse, "p" = polygon, "f" = freehand
width: number;
height: number;
points: number[]; // Vertex coordinates [x1,y1,x2,y2,...] for polygon/freehand
};
text: string;
hidden: boolean;
fillColor: string | null;
strokeColor: string | null;
}
interface SceneRegion {
id: string;
name: string; // e.g., "Throne Room", "Trap Zone"
color: string | null;
shapes: Array<{ type: string }>;
}
interface SceneTokenSummary {
id: string;
name: string;
actorId: string | null;
gridX: number; // Grid position (Math.floor(x / gridSize))
gridY: number;
x: number; // Original pixel position
y: number;
elevation: number;
hidden: boolean;
disposition: number; // -1: hostile, 0: neutral, 1: friendly
hp?: { value: number; max: number };
ac?: number;
conditions: string[];
}Example:
// Command - get active scene
{ "id": "1", "type": "get-scene", "params": {} }
// Command - get specific scene
{ "id": "2", "type": "get-scene", "params": { "sceneId": "scene-abc" } }
// Response
{
"id": "1",
"success": true,
"data": {
"id": "scene-abc",
"name": "Tavern - Ground Floor",
"active": true,
"img": "scenes/tavern.jpg",
"width": 4000,
"height": 3000,
"grid": { "size": 100, "type": 1, "units": "ft", "distance": 5 },
"darkness": 0.3,
"notes": [
{ "x": 500, "y": 300, "text": "Bar counter", "label": "Bar", "entryId": null },
{ "x": 1200, "y": 800, "text": "Secret trapdoor", "label": "Trapdoor", "entryId": "journal-cellar" }
],
"walls": [
{ "id": "wall-1", "c": [0, 0, 4000, 0], "move": 20, "sense": 20, "door": 0, "ds": 0 },
{ "id": "wall-2", "c": [800, 500, 800, 700], "move": 20, "sense": 20, "door": 1, "ds": 0 }
],
"lights": [
{ "x": 500, "y": 500, "bright": 20, "dim": 40, "color": "#ff9900", "angle": 360, "walls": true, "hidden": false }
],
"tiles": [
{ "x": 200, "y": 300, "width": 100, "height": 100, "img": "tiles/table.png", "hidden": false, "elevation": 0, "rotation": 0 }
],
"drawings": [
{ "x": 400, "y": 400, "shape": { "type": "r", "width": 200, "height": 100, "points": [] }, "text": "", "hidden": false, "fillColor": "#00ff00", "strokeColor": "#000000" }
],
"regions": [
{ "id": "region1", "name": "Trap Zone", "color": "#ff0000", "shapes": [{ "type": "rectangle" }] }
],
"tokens": [
{ "id": "token-1", "name": "Goblin", "actorId": "actor-1", "gridX": 2, "gridY": 3, "x": 250, "y": 350, "elevation": 0, "hidden": false, "disposition": -1 },
{ "id": "token-2", "name": "Fighter", "actorId": "actor-2", "gridX": 5, "gridY": 5, "x": 500, "y": 500, "elevation": 0, "hidden": false, "disposition": 1 }
],
"asciiMap": "... (ASCII map) ..."
}
}Get a summary list of all scenes in the world.
Params:
interface GetScenesListParams {
// No parameters
}Result:
interface SceneListResult {
scenes: SceneSummary[];
}
interface SceneSummary {
id: string;
name: string;
active: boolean;
img: string;
}Example:
// Command
{ "id": "1", "type": "get-scenes-list", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": {
"scenes": [
{ "id": "scene-1", "name": "Tavern - Ground Floor", "active": true, "img": "scenes/tavern.jpg" },
{ "id": "scene-2", "name": "Dark Forest", "active": false, "img": "scenes/forest.jpg" },
{ "id": "scene-3", "name": "Dragon's Lair", "active": false, "img": "scenes/lair.jpg" }
]
}
}Switch the active scene. All players will be moved to this scene.
Params:
interface ActivateSceneParams {
sceneId: string; // Required - scene ID to activate
}Result:
interface ActivateSceneResult {
id: string;
name: string;
active: boolean; // Always true after activation
}Example:
// Command
{ "id": "1", "type": "activate-scene", "params": { "sceneId": "scene-2" } }
// Response
{ "id": "1", "success": true, "data": { "id": "scene-2", "name": "Dark Forest", "active": true } }Capture a screenshot of the current active scene canvas, including a grid overlay. Returns a base64-encoded WebP image.
Params:
// No parameters required
interface CaptureSceneParams {}Result:
interface CaptureSceneResult {
sceneId: string;
sceneName: string;
image: string; // Base64-encoded image data (no data URI prefix)
mimeType: string; // "image/webp"
width: number; // Canvas width in pixels
height: number; // Canvas height in pixels
}Example:
// Command
{ "id": "1", "type": "capture-scene", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": {
"sceneId": "scene-abc",
"sceneName": "Tavern - Ground Floor",
"image": "UklGRh4AAABXRUJQVlA4IBIAAAAwAQCdASoBAAEAAQ...",
"mimeType": "image/webp",
"width": 1920,
"height": 1080
}
}Create a scene.
Params:
interface CreateSceneParams {
name: string;
width?: number;
height?: number;
grid?: {
type?: 'gridless' | 'square' | 'hexPointyOdd' | 'hexPointyEven' | 'hexFlatOdd' | 'hexFlatEven';
size?: number; // Pixels per grid space
distance?: number; // Units per grid space
units?: string; // e.g. "ft"
};
background?: string; // Image path or URL
foreground?: string;
padding?: number;
navigation?: boolean; // Show in the scene navigation bar
navName?: string;
navOrder?: number;
fogExploration?: boolean;
darkness?: number; // 0-1
folder?: string; // Folder ID
}Result:
interface SceneSummary {
id: string;
uuid: string;
name: string;
active: boolean;
width: number;
height: number;
background: string | null;
navigation: boolean;
navName: string | null;
navOrder: number;
folder: string | null;
grid: { type: SceneGridType; size: number; distance: number; units: string };
}Update a scene. Same fields as create-scene, all optional, plus sceneId. navName: null clears the navigation name and folder: null moves the scene to the root.
Params:
interface UpdateSceneParams {
sceneId: string;
name?: string;
width?: number;
height?: number;
grid?: SceneGridConfig;
background?: string;
foreground?: string;
padding?: number;
navigation?: boolean;
navName?: string | null;
navOrder?: number;
fogExploration?: boolean;
darkness?: number;
folder?: string | null;
}Result: SceneSummary (see create-scene).
Params: { sceneId: string }
Result: { deleted: true, sceneId: string }
Duplicate a scene with everything placed on it.
Params:
interface CloneSceneParams {
sourceId: string;
name?: string; // Name of the copy
folder?: string; // Folder ID
}Result: SceneSummary of the new scene.
Show a scene on the GM's canvas without activating it for players (compare activate-scene).
Params: { sceneId: string }
Result: { viewed: true, sceneId: string }
Get a list of all world-level items (not in actors' inventories).
Params:
// No parameters required
interface GetItemsParams {}Result:
type GetItemsResult = ItemData[];
interface ItemData {
id: string;
uuid?: string;
name: string;
type: string;
img: string;
folder?: string | null;
system: Record<string, unknown>;
}Example:
// Command
{ "id": "1", "type": "get-items", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": [
{ "id": "item-1", "name": "Healing Potion", "type": "consumable", "img": "icons/potions/healing.png", "system": { "quantity": 5 } },
{ "id": "item-2", "name": "Longsword +1", "type": "weapon", "img": "icons/weapons/sword.png", "system": {} }
]
}Get detailed information about a single world-level item.
Params:
interface GetItemParams {
itemId: string;
}Result: ItemData
Example:
{ "id": "1", "type": "get-item", "params": { "itemId": "item-1" } }Search world-level items with structured D&D 5e-aware filters. All filters combine with AND; values inside an array filter combine with OR. Returns minimal { id, name } summaries — use get-item for details. An item that lacks a filtered field is silently excluded.
Params:
interface FilterItemsParams {
name?: string; // substring, case-insensitive
type?: string[]; // 'weapon' | 'equipment' | 'consumable' | 'tool' | 'container' | 'loot' | 'spell' |
// 'feat' | 'background' | 'race' | 'class' | 'subclass' | 'feature'
rarity?: string[]; // 'common' | 'uncommon' | 'rare' | 'veryRare' | 'legendary' | 'artifact'
spellSchool?: string[]; // 'abjuration' | 'conjuration' | 'divination' | 'enchantment' | 'evocation' |
// 'illusion' | 'necromancy' | 'transmutation'
requiresAttunement?: boolean;
identified?: boolean;
hasActivities?: boolean;
isContainer?: boolean;
weight?: { min?: number; max?: number };
price?: { min?: number; max?: number };
spellLevel?: { min?: number; max?: number };
folder?: { id?: string; name?: string; recursive?: boolean };
limit?: number; // 1-200, default 50
offset?: number; // ≥ 0, default 0
}Result:
interface FilterItemsResult {
results: Array<{ id: string; name: string }>;
total: number; // count after filtering, before pagination
hasMore: boolean;
}Example:
{ "id": "1", "type": "filter-items", "params": { "type": ["spell"], "spellLevel": { "min": 1, "max": 3 }, "spellSchool": ["evocation"] } }Create a world-level item (not in an actor's inventory).
Params:
interface CreateItemParams {
name: string;
type: string; // System item type, e.g. "weapon", "spell"
folder?: string; // Folder ID
img?: string;
system?: Record<string, unknown>; // System-specific data
}Result:
interface WorldItemResult {
id: string;
uuid: string;
name: string;
type: string;
img: string;
folder: string | null;
}Import an item from a compendium pack into the world (not onto an actor; see add-item-from-compendium for that).
Params:
interface CreateItemFromCompendiumParams {
packId: string; // e.g. "dnd5e.items"
itemId: string; // Document ID inside the pack
name?: string; // Rename the imported copy
folder?: string; // Folder ID
}Result: WorldItemResult (see create-item). Errors: Compendium pack not found: <packId>, Item not found in compendium: <itemId>.
Update a world-level item.
Params:
interface UpdateItemParams {
itemId: string;
name?: string;
img?: string;
folder?: string | null; // null = move to root
system?: Record<string, unknown>;
}Result: WorldItemResult (see create-item).
Delete a world-level item.
Params:
interface DeleteItemParams {
itemId: string;
}Result:
interface DeleteItemResult {
deleted: true;
itemId: string;
}Get items from actor's inventory with optional filtering.
Params:
interface GetActorItemsParams {
actorId: string;
type?: string; // Filter by item type (weapon, spell, consumable, etc.)
equipped?: boolean; // Filter by equipped status
hasActivities?: boolean; // Filter items with activities (D&D 5e 2024)
}Result:
interface ActorItemsResult {
actorId: string;
actorName: string;
items: ItemDetailSummary[];
}
interface ItemDetailSummary {
id: string;
name: string;
type: string;
img: string;
equipped: boolean;
quantity: number;
hasActivities: boolean;
activityTypes: string[]; // e.g., ["attack", "damage", "save"]
description: string;
damage: object | null;
range: object | null;
}Example:
// Command - get all weapons
{ "id": "1", "type": "get-actor-items", "params": { "actorId": "abc123", "type": "weapon" } }
// Response
{
"id": "1",
"success": true,
"data": {
"actorId": "abc123",
"actorName": "Aragorn",
"items": [
{
"id": "item-456",
"name": "Longsword",
"type": "weapon",
"img": "icons/weapons/swords/sword.png",
"equipped": true,
"quantity": 1,
"hasActivities": true,
"activityTypes": ["attack", "damage"],
"description": "A fine longsword",
"damage": {},
"range": {}
}
]
}
}Use/activate an item (cast spell, use consumable, attack with weapon).
Naming (v8.7.0+): Canonical, system-namespaced name. The legacy
use-itemstill works as a deprecated alias routing to the same handler.
Params:
interface UseItemParams {
actorId: string;
itemId: string;
activityId?: string; // Specific activity ID (D&D 5e 2024)
activityType?: string; // Activity type filter (attack, damage, save, heal)
consume?: boolean; // Consume resources (default: true)
scaling?: number; // Spell slot level for scaling
showInChat?: boolean; // Show in chat (default: true)
}Result:
interface UseItemResult {
itemId: string;
itemName: string;
itemType: string;
activityUsed?: ActivityInfo;
rolls: RollResult[];
chatMessageId?: string;
status?: ActivationStatus; // Only for a self-targeted heal (see below)
dialog?: UserDialogKind;
warning?: string; // "no_roll_performed": the card was posted but nothing was rolled
appliedHealing?: AppliedHitPoints; // HP before/after per token, when the module or Midi-QOL applied it
}
interface ActivityInfo {
id: string;
name: string;
type: string;
}A self-targeted heal activity (Second Wind, Lay on Hands on yourself) runs through the dnd5e/activate-item pipeline instead (module 8.14+): the healing roll is made and applied — by Midi-QOL, or by the module without it — and the answer carries status, appliedHealing and, when nothing was rolled, warning: "no_roll_performed" rather than a silent success.
Example:
// Command - cast Fireball at 4th level
{
"id": "1",
"type": "use-item",
"params": {
"actorId": "abc123",
"itemId": "spell-fireball",
"scaling": 4,
"showInChat": true
}
}
// Response
{
"id": "1",
"success": true,
"data": {
"itemId": "spell-fireball",
"itemName": "Fireball",
"itemType": "spell",
"activityUsed": { "id": "act-1", "name": "Cast", "type": "save" },
"rolls": [{ "total": 32, "formula": "9d6", "dice": [{ "type": "d6", "count": 9, "results": [4,5,3,6,2,4,3,3,2] }] }],
"chatMessageId": "msg-xyz"
}
}Naming (v8.7.0+): Canonical, system-namespaced name. The legacy
activate-itemstill works as a deprecated alias routing to the same handler.
Activate an item natively, triggering the full Foundry automation pipeline. Unlike use-item, this command does not suppress hooks or dialogs, allowing automation modules like Midi-QOL to intercept the call and execute the full combat sequence (attack roll -> AC check -> damage -> HP reduction -> effects).
Supports programmatic targeting -- set targetTokenIds so automation modules know which tokens to resolve attacks against.
Params:
interface ActivateItemParams {
actorId: string; // ID of the actor using the item
itemId: string; // ID of the item in the actor's inventory
activityId?: string; // Specific activity ID (D&D 5e 2024 activities system)
activityType?: string; // Activity type filter: "attack", "damage", "save", "heal", "check", "utility"
targetTokenIds?: string[]; // Token IDs on the active scene to set as targets before activation
templatePosition?: { x: number; y: number; direction?: number }; // Position for spell template
spellLevel?: number; // Cast at a specific spell level
// module 8.14+
attackerTokenId?: string; // Act as this token of the actor (its synthetic actor when unlinked); controlled first
attackMode?: string; // "oneHanded" | "twoHanded" | "offhand" | "thrown" | "thrown-offhand"
ammunition?: string | false; // Ammunition item id, or false to attack without consuming any
consume?: { spellSlot?: boolean; itemUses?: boolean; ammunition?: boolean };
fastForward?: boolean; // Default true: skip every dialog, auto-roll attack and damage
advantage?: boolean; // One-off, this use only — no effect document is created
disadvantage?: boolean;
attackBonus?: number | string; // Added to this attack roll: 2, "1d4"
damageBonus?: string; // Added to the first damage part: "1d6" for Sneak Attack
targetAcBonus?: number; // Added to every target's AC for this hit check (Midi-QOL only)
}Both flags at once are a validation error (Cannot have both advantage and disadvantage).
Result:
interface ActivateItemResult {
itemId: string; // ID of the used item
itemName: string; // Display name of the item (e.g., "Longsword", "Fireball")
itemType: string; // Item type: "weapon", "spell", "consumable", "equipment", "feat", etc.
activityUsed?: ActivityInfo; // Present when item has D&D 5e activities. Absent when item has no activities.
activated: boolean; // false only when a dialog opened before the use went through
status: ActivationStatus; // module 8.14+, see below
dialog?: UserDialogKind; // The dialog that opened, when status is "awaiting_user_dialog"
targetsSet: number; // Number of targets set before activation (0 if targetTokenIds not provided)
rolls: RollResult[]; // Rolls from use() plus, without Midi-QOL, the attack or damage roll dnd5e made afterwards
chatMessageId?: string; // ID of the chat message created in Foundry. Present when Foundry creates a chat card.
workflow?: MidiWorkflowResult; // Midi-QOL automation results. ONLY present when Midi-QOL module is installed and active.
appliedDamage?: AppliedHitPoints; // HP before/after per token id (Midi-QOL damage list, checked against the actor)
appliedHealing?: AppliedHitPoints; // Same for healing; without Midi-QOL the module applies heal activities itself
warning?: string; // "no_roll_performed" | "midi_workflow_timeout" | "midi_workflow_aborted"
}
type ActivationStatus = 'completed' | 'awaiting_user_dialog' | 'workflow_timeout' | 'workflow_aborted';
type UserDialogKind = 'attack-roll' | 'consume' | 'damage' | 'other';
type AppliedHitPoints = Record<string, {
applied: boolean; // HP on the actor now matches what the workflow computed
hpBefore: number;
hpAfter: number;
tempBefore?: number;
tempAfter?: number;
}>;
interface ActivityInfo {
id: string; // Activity ID within the item
name: string; // Activity display name (e.g., "Attack", "Cast", "Heal")
type: string; // Activity type: "attack", "damage", "save", "heal", "check", "utility"
}
interface RollResult {
total: number; // Final roll total (e.g., 18 for "1d20+5" roll of 13)
formula: string; // Roll formula as string (e.g., "1d20+5", "2d6+3")
dice: DiceResult[]; // Individual dice breakdowns
isCritical?: boolean; // true if natural 20 on d20 attack roll
isFumble?: boolean; // true if natural 1 on d20 attack roll
mode?: 'advantage' | 'normal' | 'disadvantage'; // What dnd5e applied to the d20
kept?: number; // The d20 result that stayed after kh/kl
}
interface DiceResult {
type: string; // Dice type: "d20", "d6", "d8", "d10", "d12", etc.
count: number; // Number of dice rolled (e.g., 2 for "2d6")
results: number[]; // Individual die results (e.g., [4, 6] for 2d6)
}
interface MidiWorkflowResult {
attackTotal: number | undefined; // Total attack roll value. undefined for non-attack items (potions, spells with saves only).
damageTotal: number | undefined; // Total damage dealt. undefined if attack missed or no damage component.
isCritical: boolean; // true if the attack was a critical hit (natural 20)
isFumble: boolean; // true if the attack was a fumble (natural 1)
hitTargetIds: string[]; // Token IDs that were HIT by the attack. Empty array if attack missed or spell with saves.
saveTargetIds: string[]; // Token IDs that SUCCEEDED their saving throw. For spells like Fireball.
failedSaveTargetIds: string[]; // Token IDs that FAILED their saving throw. These take full damage/effects.
}Presence rules for optional fields:
| Field | When present | When absent |
|---|---|---|
activityUsed |
Item has D&D 5e activities | Item has no activities (falls back to item.use()) |
chatMessageId |
Foundry created a chat card | No chat message was created |
rolls[] |
Always present | Empty array [] if no rolls (Midi-QOL handles rolling) |
workflow |
Midi-QOL module is installed, active, and completed | Midi-QOL not installed, inactive, timed out (25 s → status: "workflow_timeout") or aborted ("workflow_aborted") |
appliedDamage / appliedHealing
|
A workflow or the module changed HP | Nothing landed |
dialog |
status is awaiting_user_dialog
|
Otherwise |
workflow.attackTotal |
Attack item (weapon, spell attack) | Non-attack items (potion, spell with save only) |
workflow.damageTotal |
Attack hit or AoE damage applied | Attack missed with no damage |
Difference from use-item:
use-item |
activate-item |
|
|---|---|---|
| Foundry hooks | Suppressed (configure: false, shiftKey: true) |
Native (not suppressed) |
| Midi-QOL | Does not trigger | Full automation pipeline |
| Targeting | Not supported |
targetTokenIds sets targets before activation |
| Returns rolls | Always (from suppressed Foundry call) | From native call + Midi-QOL workflow data |
| Dialogs | Suppressed | Skipped and auto-rolled by default (fastForward: true); a dialog that still opens is reported at once as status: "awaiting_user_dialog" and the use finishes when the GM clicks |
| Use case | Silent/programmatic operations | Combat actions, spellcasting, item usage with full visualization |
Midi-QOL detection: The module checks game.modules.get('midi-qol')?.active before activation. If Midi-QOL is active, it arms a midi-qol.RollComplete listener before calling activity.use() and resolves when the workflow completes, when Midi deletes the usage card (abort), or after 25 s (timeout) — below the platform's 30 s command gate. attackMode, advantage / disadvantage and the fast-forward flags reach Midi through usage.midiOptions.workflowOptions; targetAcBonus through workflow.targetACModifiers in midi-qol.preCheckHits. Without Midi-QOL the module hooks dnd5e's own preRollAttackV2 / preRollDamageV2 for the same choices, waits up to 5 s for the roll dnd5e triggers after use(), and applies heal activities itself. A consumable destroyed by its last use is kept at quantity 0 until the rolls are over, then deleted.
Examples:
// === Melee attack with Midi-QOL active ===
// Command
{
"id": "1",
"type": "activate-item",
"params": {
"actorId": "goblin-actor",
"itemId": "scimitar-id",
"targetTokenIds": ["fighter-token"]
}
}
// Response -- attack HIT
{
"id": "1",
"success": true,
"data": {
"itemId": "scimitar-id",
"itemName": "Scimitar",
"itemType": "weapon",
"activityUsed": { "id": "act-1", "name": "Attack", "type": "attack" },
"activated": true,
"targetsSet": 1,
"rolls": [
{
"total": 17,
"formula": "1d20+4",
"dice": [{ "type": "d20", "count": 1, "results": [13] }]
}
],
"chatMessageId": "msg-abc",
"workflow": {
"attackTotal": 17,
"damageTotal": 8,
"isCritical": false,
"isFumble": false,
"hitTargetIds": ["fighter-token"],
"saveTargetIds": [],
"failedSaveTargetIds": []
}
}
}
// === Critical hit ===
{
"id": "2",
"success": true,
"data": {
"itemId": "scimitar-id",
"itemName": "Scimitar",
"itemType": "weapon",
"activityUsed": { "id": "act-1", "name": "Attack", "type": "attack" },
"activated": true,
"targetsSet": 1,
"rolls": [
{ "total": 24, "formula": "1d20+4", "dice": [{ "type": "d20", "count": 1, "results": [20] }], "isCritical": true }
],
"chatMessageId": "msg-def",
"workflow": {
"attackTotal": 24,
"damageTotal": 14,
"isCritical": true,
"isFumble": false,
"hitTargetIds": ["fighter-token"],
"saveTargetIds": [],
"failedSaveTargetIds": []
}
}
}
// === Attack MISSED ===
{
"id": "3",
"success": true,
"data": {
"itemId": "scimitar-id",
"itemName": "Scimitar",
"itemType": "weapon",
"activityUsed": { "id": "act-1", "name": "Attack", "type": "attack" },
"activated": true,
"targetsSet": 1,
"rolls": [
{ "total": 8, "formula": "1d20+4", "dice": [{ "type": "d20", "count": 1, "results": [4] }] }
],
"chatMessageId": "msg-ghi",
"workflow": {
"attackTotal": 8,
"isCritical": false,
"isFumble": false,
"hitTargetIds": [],
"saveTargetIds": [],
"failedSaveTargetIds": []
}
}
}
// === Fireball (save-based AoE) with Midi-QOL ===
{
"id": "4",
"type": "activate-item",
"params": {
"actorId": "wizard-actor",
"itemId": "fireball-id",
"targetTokenIds": ["goblin-1", "goblin-2", "goblin-3"]
}
}
// Response -- 2 failed saves, 1 saved
{
"id": "4",
"success": true,
"data": {
"itemId": "fireball-id",
"itemName": "Fireball",
"itemType": "spell",
"activityUsed": { "id": "act-1", "name": "Cast", "type": "save" },
"activated": true,
"targetsSet": 3,
"rolls": [],
"chatMessageId": "msg-jkl",
"workflow": {
"damageTotal": 28,
"isCritical": false,
"isFumble": false,
"hitTargetIds": [],
"saveTargetIds": ["goblin-2"],
"failedSaveTargetIds": ["goblin-1", "goblin-3"]
}
}
}
// === Healing Potion (no target, no Midi-QOL workflow) ===
{
"id": "5",
"type": "activate-item",
"params": {
"actorId": "fighter-actor",
"itemId": "healing-potion-id"
}
}
// Response -- no targets, basic rolls only
{
"id": "5",
"success": true,
"data": {
"itemId": "healing-potion-id",
"itemName": "Potion of Healing",
"itemType": "consumable",
"activated": true,
"targetsSet": 0,
"rolls": [
{ "total": 7, "formula": "2d4+2", "dice": [{ "type": "d4", "count": 2, "results": [3, 2] }] }
],
"chatMessageId": "msg-mno"
}
}
// === Without Midi-QOL installed (vanilla Foundry) ===
{
"id": "6",
"success": true,
"data": {
"itemId": "scimitar-id",
"itemName": "Scimitar",
"itemType": "weapon",
"activityUsed": { "id": "act-1", "name": "Attack", "type": "attack" },
"activated": true,
"targetsSet": 1,
"rolls": [
{ "total": 15, "formula": "1d20+4", "dice": [{ "type": "d20", "count": 1, "results": [11] }] }
],
"chatMessageId": "msg-pqr"
}
}Create a new item directly in an actor's inventory.
Params:
interface AddItemToActorParams {
actorId: string;
name: string;
type: string; // "weapon", "equipment", "consumable", "loot", "spell", etc.
img?: string; // Item icon path
system?: ItemSystemData; // System-specific data
}
interface ItemSystemData {
description?: { value: string };
quantity?: number;
weight?: { value: number; units?: string };
price?: { value: number; denomination?: string };
rarity?: string; // "common", "uncommon", "rare", "veryRare", "legendary"
identified?: boolean;
equipped?: boolean;
attunement?: number;
}Result:
interface ItemResult {
id: string;
name: string;
type: string;
img: string;
actorId: string;
actorName: string;
}Example:
// Command - give 50 gold pieces
{
"id": "1",
"type": "add-item-to-actor",
"params": {
"actorId": "abc123",
"name": "Gold Coins",
"type": "loot",
"system": {
"quantity": 50,
"price": { "value": 1, "denomination": "gp" }
}
}
}
// Response
{
"id": "1",
"success": true,
"data": {
"id": "item-new-001",
"name": "Gold Coins",
"type": "loot",
"img": "icons/commodities/currency/coins-gold.png",
"actorId": "abc123",
"actorName": "Aragorn"
}
}Add an item from a compendium pack to an actor's inventory. Useful for giving standard SRD equipment, weapons, potions, etc.
Params:
interface AddItemFromCompendiumParams {
actorId: string;
packId: string; // Compendium ID, e.g., "dnd5e.items"
itemId: string; // Item ID within compendium
name?: string; // Override item name
quantity?: number; // Override quantity
}Result: ItemResult
Example:
// Command - give Healing Potion from SRD
{
"id": "1",
"type": "add-item-from-compendium",
"params": {
"actorId": "abc123",
"packId": "dnd5e.items",
"itemId": "potion-of-healing-id",
"quantity": 3
}
}
// Command - give custom-named weapon
{
"id": "2",
"type": "add-item-from-compendium",
"params": {
"actorId": "abc123",
"packId": "dnd5e.items",
"itemId": "longsword-id",
"name": "Flame Tongue Longsword"
}
}Update an existing item in an actor's inventory.
Params:
interface UpdateActorItemParams {
actorId: string;
itemId: string;
name?: string;
img?: string;
system?: Partial<ItemSystemData>; // Partial update
}Result: ItemResult
Example:
// Command - update quantity (consume 2 potions)
{
"id": "1",
"type": "update-actor-item",
"params": {
"actorId": "abc123",
"itemId": "potion-item-id",
"system": { "quantity": 1 }
}
}
// Command - equip item
{
"id": "2",
"type": "update-actor-item",
"params": {
"actorId": "abc123",
"itemId": "armor-item-id",
"system": { "equipped": true }
}
}Remove an item from an actor's inventory.
Params:
interface DeleteActorItemParams {
actorId: string;
itemId: string;
}Result: DeleteResult
Example:
{ "id": "1", "type": "delete-actor-item", "params": { "actorId": "abc123", "itemId": "broken-sword-id" } }
{ "id": "1", "success": true, "data": { "deleted": true } }Get all active effects on an actor.
Params:
interface GetActorEffectsParams {
actorId?: string; // World actor. Either this or tokenId is required.
tokenId?: string; // One token's actor instead (module 8.14+): unlinked tokens keep their own effects
sceneId?: string; // Defaults to the active scene
includeDisabled?: boolean; // Include disabled effects (default: true)
}Result:
interface ActorEffectsResult {
actorId: string;
actorName: string;
effects: EffectSummary[];
activeStatuses: string[]; // Currently active status IDs
}
interface EffectSummary {
id: string;
name: string;
img: string;
disabled: boolean;
isTemporary: boolean;
statuses: string[];
origin: string | null;
changes?: EffectChangeData[];
duration?: EffectDurationData;
}
interface EffectChangeData {
key: string; // e.g., "system.attributes.ac.bonus"
value: string;
mode: number; // See change modes below
}
interface EffectDurationData {
seconds?: number;
rounds?: number;
turns?: number;
}Change Modes:
| Mode | Name | Description |
|---|---|---|
| 0 | CUSTOM | Custom handling |
| 1 | MULTIPLY | Multiply value |
| 2 | ADD | Add to value |
| 3 | DOWNGRADE | Use lower value |
| 4 | UPGRADE | Use higher value |
| 5 | OVERRIDE | Replace value |
Example:
// Command
{ "id": "1", "type": "get-actor-effects", "params": { "actorId": "abc123" } }
// Response
{
"id": "1",
"success": true,
"data": {
"actorId": "abc123",
"actorName": "Aragorn",
"effects": [
{
"id": "effect-001",
"name": "Bless",
"img": "icons/magic/holy/bless.png",
"disabled": false,
"isTemporary": true,
"statuses": [],
"origin": "Item.spell-123",
"changes": [{ "key": "system.bonuses.abilities.save", "value": "1d4", "mode": 2 }],
"duration": { "rounds": 10 }
},
{
"id": "effect-002",
"name": "Prone",
"img": "icons/conditions/prone.png",
"disabled": false,
"isTemporary": false,
"statuses": ["prone"],
"origin": null
}
],
"activeStatuses": ["prone"]
}
}Toggle a D&D 5e status condition on/off.
Params:
interface ToggleActorStatusParams {
actorId?: string; // World actor. Either this or tokenId is required.
tokenId?: string; // One token's actor instead (module 8.14+): unlinked tokens keep their own effects
sceneId?: string; // Defaults to the active scene
statusId: string; // See available statuses below
active?: boolean; // Force active/inactive state
overlay?: boolean; // Show as large overlay on token
}Available Status IDs:
| Status | Status | Status |
|---|---|---|
blinded |
frightened |
petrified |
charmed |
grappled |
poisoned |
concentrating |
incapacitated |
prone |
dead |
invisible |
restrained |
deafened |
paralyzed |
stunned |
exhaustion |
unconscious |
Result:
interface ToggleStatusResult {
actorId: string;
statusId: string;
active: boolean; // New state
effectId?: string; // Effect ID if created
}Example:
// Command - apply stunned condition
{ "id": "1", "type": "toggle-actor-status", "params": { "actorId": "abc123", "statusId": "stunned", "active": true } }
// Response
{ "id": "1", "success": true, "data": { "actorId": "abc123", "statusId": "stunned", "active": true, "effectId": "effect-new-001" } }
// Command - remove stunned condition
{ "id": "2", "type": "toggle-actor-status", "params": { "actorId": "abc123", "statusId": "stunned", "active": false } }
// Response
{ "id": "2", "success": true, "data": { "actorId": "abc123", "statusId": "stunned", "active": false } }Create a custom ActiveEffect on an actor.
Params:
interface AddActorEffectParams {
actorId?: string; // World actor. Either this or tokenId is required.
tokenId?: string; // One token's actor instead (module 8.14+): unlinked tokens keep their own effects
sceneId?: string; // Defaults to the active scene
name: string;
img?: string;
disabled?: boolean;
origin?: string; // Source reference, e.g., "Item.spell-id"
statuses?: string[]; // Associated status IDs
changes?: EffectChangeData[];
duration?: EffectDurationData;
}Result:
interface AddEffectResult {
actorId: string;
effectId: string;
name: string;
}Example:
// Command - add Shield of Faith effect (+2 AC for 10 minutes)
{
"id": "1",
"type": "add-actor-effect",
"params": {
"actorId": "abc123",
"name": "Shield of Faith",
"img": "icons/magic/defensive/shield.png",
"changes": [{ "key": "system.attributes.ac.bonus", "value": "2", "mode": 2 }],
"duration": { "seconds": 600 }
}
}
// Response
{ "id": "1", "success": true, "data": { "actorId": "abc123", "effectId": "effect-new-002", "name": "Shield of Faith" } }Remove an effect from an actor.
Params:
interface RemoveActorEffectParams {
actorId?: string; // World actor. Either this or tokenId is required.
tokenId?: string; // One token's actor instead (module 8.14+): unlinked tokens keep their own effects
sceneId?: string; // Defaults to the active scene
effectId: string;
}Result:
interface RemoveEffectResult {
actorId: string;
effectId: string;
removed: boolean;
}Example:
{ "id": "1", "type": "remove-actor-effect", "params": { "actorId": "abc123", "effectId": "effect-001" } }
{ "id": "1", "success": true, "data": { "actorId": "abc123", "effectId": "effect-001", "removed": true } }Update an existing effect's properties.
Params:
interface UpdateActorEffectParams {
actorId?: string; // World actor. Either this or tokenId is required.
tokenId?: string; // One token's actor instead (module 8.14+): unlinked tokens keep their own effects
sceneId?: string; // Defaults to the active scene
effectId: string;
name?: string;
img?: string;
disabled?: boolean;
changes?: EffectChangeData[];
duration?: EffectDurationData;
}Result:
interface UpdateEffectResult {
actorId: string;
effectId: string;
name: string;
}Example:
// Command - disable an effect
{ "id": "1", "type": "update-actor-effect", "params": { "actorId": "abc123", "effectId": "effect-001", "disabled": true } }
// Response
{ "id": "1", "success": true, "data": { "actorId": "abc123", "effectId": "effect-001", "name": "Bless" } }Get a list of all available compendium packs.
Params:
// No parameters required
interface GetCompendiumsParams {}Result:
type GetCompendiumsResult = CompendiumMetadata[];
interface CompendiumMetadata {
id: string; // e.g., "dnd5e.monsters"
label: string; // e.g., "Monsters (SRD)"
type: string; // "Actor", "Item", "JournalEntry", "Scene", etc.
system: string; // e.g., "dnd5e"
packageName: string; // e.g., "dnd5e"
documentCount: number; // Number of entries
}Example:
// Command
{ "id": "1", "type": "get-compendiums", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": [
{ "id": "dnd5e.monsters", "label": "Monsters (SRD)", "type": "Actor", "system": "dnd5e", "packageName": "dnd5e", "documentCount": 325 },
{ "id": "dnd5e.items", "label": "Items (SRD)", "type": "Item", "system": "dnd5e", "packageName": "dnd5e", "documentCount": 680 },
{ "id": "dnd5e.spells", "label": "Spells (SRD)", "type": "Item", "system": "dnd5e", "packageName": "dnd5e", "documentCount": 319 }
]
}Get contents of a compendium pack, including documents with their data. Optional types / ids select a subset server-side (Foundry type__in / _id__in database queries on v13+; on v11/v12 the module transparently falls back to a full load with client-side filtering — the result is identical). Batch-fetching several known documents is one get-compendium call with ids instead of N get-compendium-document calls.
Params:
interface GetCompendiumParams {
packId: string; // Compendium ID, e.g., "dnd5e.monsters"
types?: string[]; // Only documents of these sub-types, e.g. ["spell"] (empty array matches nothing)
ids?: string[]; // Only documents with these _ids (empty array matches nothing)
}documentCount in the result reflects the returned (filtered) set. Omitting both params keeps the classic full-pack behavior.
Result:
interface CompendiumData {
id: string;
label: string;
type: string;
system: string;
documentCount: number;
documents: CompendiumDocument[];
}
interface CompendiumDocument {
id: string;
uuid: string;
name: string;
type: string;
img: string;
system?: Record<string, unknown>;
items?: ItemData[]; // For Actor documents
pages?: JournalPageData[]; // For JournalEntry documents
}Example:
// Command
{ "id": "1", "type": "get-compendium", "params": { "packId": "dnd5e.monsters" } }
// Response
{
"id": "1",
"success": true,
"data": {
"id": "dnd5e.monsters",
"label": "Monsters (SRD)",
"type": "Actor",
"system": "dnd5e",
"documentCount": 325,
"documents": [
{
"id": "goblin-001",
"uuid": "Compendium.dnd5e.monsters.goblin-001",
"name": "Goblin",
"type": "npc",
"img": "tokens/goblin.png",
"system": { "attributes": { "hp": { "value": 7, "max": 7 } } }
}
]
}
}Get the lightweight index of a pack — id/name/img/type per entry — without loading documents. Optional dot-path fields are re-indexed server-side and returned per entry (e.g. system.level, system.details.cr).
Params:
interface GetCompendiumIndexParams {
packId: string;
fields?: string[]; // Extra index fields as dot-paths
}Result:
interface GetCompendiumIndexResult {
packId: string;
packType: string; // Pack document type, e.g. "Actor"
packLabel: string;
total: number;
entries: CompendiumIndexEntry[];
}
interface CompendiumIndexEntry {
id: string;
name: string;
img: string | null;
type: string | null; // Document sub-type, e.g. "npc", "spell"
fields?: Record<string, unknown>; // Present when `fields` was requested
}Foundry's native index search within one pack: full-text query over searchable fields plus structured field filters with operators. Wire operator names are translated to Foundry's internal values by the module.
Params:
interface SearchCompendiumParams {
packId: string;
query?: string; // Full-text query
filters?: CompendiumFilter[];
exclude?: string[]; // Document _ids to exclude
fields?: string[]; // Dot-paths to index before filtering and return per entry
limit?: number; // Default 50, max 500
offset?: number; // Default 0
}
interface CompendiumFilter {
field: string; // Dot-path, e.g. "system.level"
operator?: 'EQUALS' | 'CONTAINS' | 'STARTS_WITH' | 'ENDS_WITH' | 'LESS_THAN'
| 'LESS_THAN_EQUAL' | 'GREATER_THAN' | 'GREATER_THAN_EQUAL' | 'BETWEEN' | 'IS_EMPTY';
value?: unknown; // BETWEEN expects [min, max]
negate?: boolean; // Default false
}Note: CONTAINS follows Foundry semantics — membership of the document value in an array value, not substring matching.
Result:
interface SearchCompendiumResult {
packId: string;
results: CompendiumIndexEntry[];
total: number;
hasMore: boolean;
}Get a single document from a pack by id, with its full raw data (toObject()), without loading the whole pack.
Params:
interface GetCompendiumDocumentParams {
packId: string;
documentId: string;
}Result:
interface CompendiumDocumentResult {
id: string;
uuid: string;
name: string;
type: string; // Document sub-type ("" when absent)
img: string | null;
documentType: string; // Pack document type, e.g. "Item"
data: Record<string, unknown>; // Full document source
}Import a document from a pack into the world. Works for any pack type except Adventure (use the Foundry UI for adventures). Optional name / folder overrides.
Params:
interface ImportFromCompendiumParams {
packId: string;
documentId: string;
name?: string; // Override the imported document's name
folder?: string; // World folder id
}Result:
interface ImportFromCompendiumResult {
imported: true;
worldId: string; // Id of the created world document
uuid: string;
name: string;
documentType: string;
}Search the index of every compendium pack by document name (case-insensitive substring). Reads only pack indexes — never full documents — and returns lightweight matches, so it stays fast across large packs. Use this to find a document by name across all packs (e.g. "goblin"); use get-compendium / get-compendium-index when you already know the pack.
Params:
interface SearchCompendiumsParams {
query: string; // Name substring, case-insensitive (required)
type?: string; // Filter by pack document type: "Actor" | "Item" | "JournalEntry" | "RollTable" | "Scene" | "ActiveEffect"
system?: string; // Filter by system, e.g. "dnd5e"
limit?: number; // Max matches, default 100, capped at 500
}Result:
type SearchCompendiumsResult = CompendiumSearchMatch[];
interface CompendiumSearchMatch {
packId: string; // pack collection, e.g. "dnd5e.monsters"
packLabel: string; // pack label
packType: string; // pack document type, e.g. "Actor"
system: string; // pack system, "" if none
id: string; // document _id within the pack
name: string;
documentType?: string; // index sub-type if present, e.g. "npc", "weapon", "spell"
}An empty result is [], never an error.
Example:
// Command
{ "id": "1", "type": "search-compendiums", "params": { "query": "goblin", "type": "Actor" } }
// Response
{
"id": "1",
"success": true,
"data": [
{ "packId": "dnd5e.monsters", "packLabel": "Monsters (SRD)", "packType": "Actor", "system": "dnd5e", "id": "goblin-001", "name": "Goblin", "documentType": "npc" },
{ "packId": "dnd5e.monsters", "packLabel": "Monsters (SRD)", "packType": "Actor", "system": "dnd5e", "id": "hobgoblin-001", "name": "Hobgoblin", "documentType": "npc" }
]
}Full-text search inside JournalEntry compendium packs — rules text and spell lists live in journal pages, which name-based index search cannot see. Matches the query against page names and (unless searchContent: false) page HTML content; content matches return a plain-text snippet (±100 chars around the first hit). Packs are scanned lazily — the search stops loading further packs once limit is reached.
Params:
interface SearchCompendiumPagesParams {
query: string; // Substring, case-insensitive; blank → []
packIds?: string[]; // Explicit JournalEntry packs (unknown/non-journal pack → error); omitted → all journal packs
pageTypes?: string[]; // Page sub-types, e.g. ["rule", "spells"] (empty array matches nothing)
searchContent?: boolean; // Default true; false = names only (cheaper)
limit?: number; // Default 25, capped at 200
}Result:
type SearchCompendiumPagesResult = CompendiumPageMatch[];
interface CompendiumPageMatch {
packId: string;
packLabel: string;
journalId: string;
journalName: string;
pageId: string;
pageName: string;
pageType: string; // e.g. "rule", "spells", "class", "text"
uuid: string; // Page uuid — feed it to resolve-uuid for the full text
matchedIn: 'name' | 'content';
snippet: string | null; // Context around the content match; null for name matches
}Example:
// Command
{ "id": "1", "type": "search-compendium-pages", "params": { "query": "grappled", "pageTypes": ["rule"] } }
// Response
{
"id": "1",
"success": true,
"data": [
{
"packId": "dnd5e.rules", "packLabel": "Rules (SRD)",
"journalId": "j1", "journalName": "Appendix A: Conditions",
"pageId": "p2", "pageName": "Grappled", "pageType": "rule",
"uuid": "Compendium.dnd5e.rules.JournalEntry.j1.JournalEntryPage.p2",
"matchedIn": "name", "snippet": null
}
]
}Resolve any absolute Foundry UUID to its document: world documents (Actor.x), compendium documents (Compendium.scope.pack.Item.x), and embedded documents (Actor.x.Item.y, journal pages). This makes reference graphs walkable — class → ItemGrant advancement → features → effects, spell lists → spells — one call per hop.
Params:
interface ResolveUuidParams {
uuid: string;
}Result:
interface ResolveUuidResult {
uuid: string; // Normalized uuid
documentName: string; // Primary type, e.g. "Item", "Actor", "JournalEntryPage"
id: string;
name: string | null;
type: string | null; // Sub-type, e.g. "spell"; null when the document has none
img: string | null;
pack: string | null; // Pack collection for compendium documents, null for world
parentUuid: string | null; // Parent uuid for embedded documents
data: Record<string, unknown>; // Full document source (toObject())
}An unresolvable or malformed UUID returns Document not found for UUID: <uuid> as the error.
Get a summary list of all roll tables in the world.
Params:
// No parameters required
interface ListRollTablesParams {}Result:
type ListRollTablesResult = RollTableSummary[];
interface RollTableSummary {
id: string;
name: string;
img: string;
description: string;
formula: string; // e.g., "1d20", "1d100"
replacement: boolean; // Draw with replacement
totalResults: number; // Total number of entries
drawnResults: number; // Number already drawn (when replacement: false)
}Example:
// Command
{ "id": "1", "type": "list-roll-tables", "params": {} }
// Response
{
"id": "1",
"success": true,
"data": [
{
"id": "table-1",
"name": "Random Encounters",
"img": "icons/svg/d20.svg",
"description": "Forest encounter table",
"formula": "1d20",
"replacement": true,
"totalResults": 20,
"drawnResults": 0
}
]
}Get full details of a roll table including all result entries.
Params:
interface GetRollTableParams {
tableId: string;
}Result:
interface RollTableResult {
id: string;
name: string;
img: string;
description: string;
formula: string;
replacement: boolean;
displayRoll: boolean; // Show roll in chat
results: TableResultData[];
}
interface TableResultData {
id: string;
type: number; // 0 = text, 1 = document, 2 = compendium
text: string;
img: string;
range: [number, number]; // [min, max] roll range
weight: number;
drawn: boolean; // Already drawn (for no-replacement tables)
documentCollection: string | null; // For document/compendium references
documentId: string | null;
}Example:
// Command
{ "id": "1", "type": "get-roll-table", "params": { "tableId": "table-1" } }
// Response
{
"id": "1",
"success": true,
"data": {
"id": "table-1",
"name": "Random Encounters",
"img": "icons/svg/d20.svg",
"description": "Forest encounter table",
"formula": "1d20",
"replacement": true,
"displayRoll": true,
"results": [
{ "id": "res-1", "type": 0, "text": "2d4 Wolves", "img": "", "range": [1, 5], "weight": 1, "drawn": false, "documentCollection": null, "documentId": null },
{ "id": "res-2", "type": 0, "text": "1 Owlbear", "img": "", "range": [6, 10], "weight": 1, "drawn": false, "documentCollection": null, "documentId": null }
]
}
}Roll on a table and get the result. Optionally displays the roll in Foundry chat.
Params:
interface RollOnTableParams {
tableId: string;
displayChat?: boolean; // Show in chat (default: true)
}Result:
interface RollOnTableResult {
tableId: string;
tableName: string;
roll: {
formula: string;
total: number;
};
results: TableResultData[]; // Matched result entries
}Example:
// Command
{ "id": "1", "type": "roll-on-table", "params": { "tableId": "table-1" } }
// Response
{
"id": "1",
"success": true,
"data": {
"tableId": "table-1",
"tableName": "Random Encounters",
"roll": { "formula": "1d20", "total": 7 },
"results": [
{ "id": "res-2", "type": 0, "text": "1 Owlbear", "img": "", "range": [6, 10], "weight": 1, "drawn": false, "documentCollection": null, "documentId": null }
]
}
}Reset a roll table, marking all results as not drawn. Useful for tables with replacement: false.
Params:
interface ResetTableParams {
tableId: string;
}Result:
interface ResetTableResult {
tableId: string;
tableName: string;
resetCount: number; // Number of results reset
}Example:
// Command
{ "id": "1", "type": "reset-table", "params": { "tableId": "table-1" } }
// Response
{ "id": "1", "success": true, "data": { "tableId": "table-1", "tableName": "Random Encounters", "resetCount": 20 } }Create a new roll table with optional pre-defined results.
Params:
interface CreateRollTableParams {
name: string;
formula?: string; // Default: "1d20"
replacement?: boolean; // Default: true
displayRoll?: boolean; // Default: true
description?: string;
img?: string;
folder?: string; // Folder ID
results?: CreateTableResultData[];
}
interface CreateTableResultData {
text: string;
range: [number, number]; // [min, max]
weight?: number; // Default: 1
type?: number; // 0 = text (default), 1 = document, 2 = compendium
documentCollection?: string;
documentId?: string;
img?: string;
}Result: RollTableResult
Example:
// Command
{
"id": "1",
"type": "create-roll-table",
"params": {
"name": "Treasure Hoard",
"formula": "1d6",
"description": "Loot from dragon's hoard",
"results": [
{ "text": "100 gold pieces", "range": [1, 2] },
{ "text": "Potion of Healing", "range": [3, 4] },
{ "text": "Magic Sword +1", "range": [5, 5] },
{ "text": "Diamond worth 500gp", "range": [6, 6] }
]
}
}Update roll table properties (does not modify individual results).
Params:
interface UpdateRollTableParams {
tableId: string;
name?: string;
formula?: string;
replacement?: boolean;
displayRoll?: boolean;
description?: string;
img?: string;
}Result: RollTableResult
Example:
// Command
{ "id": "1", "type": "update-roll-table", "params": { "tableId": "table-1", "name": "Updated Encounters", "formula": "1d100" } }Delete a roll table from the world.
Params:
interface DeleteRollTableParams {
tableId: string;
}Result: DeleteResult
Example:
{ "id": "1", "type": "delete-roll-table", "params": { "tableId": "table-1" } }
{ "id": "1", "success": true, "data": { "deleted": true } }Set the state of a door on a scene wall. The wall must be a door (door type 1 or 2).
Params:
interface SetDoorStateParams {
sceneId?: string; // Defaults to active scene
wallId: string; // Wall ID (must be a door)
state: number; // 0 = closed, 1 = open, 2 = locked
}Result:
interface SetDoorStateResult {
wallId: string;
door: number; // Door type (1 = door, 2 = secret door)
previousState: number; // State before the change
newState: number; // State after the change
}Example:
// Command - open a door
{ "id": "1", "type": "set-door-state", "params": { "wallId": "wall-door-1", "state": 1 } }
// Response
{
"id": "1",
"success": true,
"data": {
"wallId": "wall-door-1",
"door": 1,
"previousState": 0,
"newState": 1
}
}
// Command - lock a door
{ "id": "2", "type": "set-door-state", "params": { "wallId": "wall-door-1", "state": 2 } }
// Response
{ "id": "2", "success": true, "data": { "wallId": "wall-door-1", "door": 1, "previousState": 1, "newState": 2 } }List the walls of a scene.
Params: { sceneId?: string } — defaults to the active scene.
Result:
interface GetWallsResult {
sceneId: string;
walls: WallSummary[];
}
interface WallSummary {
id: string;
c: [number, number, number, number]; // x1, y1, x2, y2 in pixels
door: 'none' | 'door' | 'secret';
doorState: 'closed' | 'open' | 'locked';
move: 'none' | 'normal' | 'limited'; // Movement restriction
sense: 'none' | 'normal' | 'limited'; // Vision restriction
sound: 'none' | 'normal' | 'limited';
light: 'none' | 'normal' | 'limited';
dir: 'both' | 'left' | 'right'; // Side the wall blocks
}Params:
interface CreateWallParams {
sceneId?: string; // Defaults to the active scene
c: [number, number, number, number];
door?: 'none' | 'door' | 'secret';
doorState?: 'closed' | 'open' | 'locked';
move?: 'none' | 'normal' | 'limited';
sense?: 'none' | 'normal' | 'limited';
sound?: 'none' | 'normal' | 'limited';
light?: 'none' | 'normal' | 'limited';
dir?: 'both' | 'left' | 'right';
}Result: WallSummary (see get-walls).
Same fields as create-wall, all optional, plus wallId.
Params: { sceneId?: string, wallId: string, c?, door?, doorState?, move?, sense?, sound?, light?, dir? }
Result: WallSummary.
Params: { sceneId?: string, wallId: string }
Result: { deleted: true, wallId: string, sceneId: string }
Map notes (journal pins) on a scene. sceneId defaults to the active scene everywhere.
Params: { sceneId?: string }
Result:
interface GetNotesResult {
sceneId: string;
notes: NoteSummary[];
}
interface NoteSummary {
id: string;
x: number;
y: number;
entryId: string | null; // Linked JournalEntry
pageId: string | null; // Linked page inside the entry
text: string | null; // Label override
iconSrc: string;
iconTint: string | null;
iconSize: number;
fontSize: number;
textAnchor: 'center' | 'bottom' | 'top' | 'left' | 'right';
textColor: string | null;
global: boolean; // Visible to everyone regardless of vision
}Params:
interface CreateNoteParams {
sceneId?: string;
x: number;
y: number;
entryId?: string;
pageId?: string;
text?: string;
iconSrc?: string;
iconTint?: string;
iconSize?: number;
fontSize?: number;
textAnchor?: 'center' | 'bottom' | 'top' | 'left' | 'right';
textColor?: string;
global?: boolean;
}Result: NoteSummary (see get-notes).
Same fields as create-note, all optional, plus noteId. entryId, pageId, text, iconTint and textColor accept null to clear the value.
Result: NoteSummary.
Params: { sceneId?: string, noteId: string }
Result: { deleted: true, noteId: string, sceneId: string }
Params:
interface GetFoldersParams {
type?: 'Actor' | 'Item' | 'Scene' | 'JournalEntry' | 'RollTable' | 'Macro' | 'Cards' | 'Playlist' | 'Compendium';
}Result: FolderSummary[]
interface FolderSummary {
id: string;
name: string;
type: FolderDocumentType;
color: string | null;
description: string | null;
parentId: string | null;
sort: number;
}Params:
interface GetFolderParams {
folderId: string;
includeSubfolders?: boolean; // Nest child folders in `subfolders`
includeContents?: boolean; // Add `contentIds` of the documents inside
}Result:
interface FolderTreeEntry extends FolderSummary {
subfolders: FolderTreeEntry[];
contentIds?: string[];
}Params:
interface CreateFolderParams {
name: string;
type: FolderDocumentType;
parentId?: string;
color?: string; // e.g. "#ff0000"
description?: string;
sort?: number;
}Result: FolderSummary.
Params:
interface UpdateFolderParams {
folderId: string;
name?: string;
parentId?: string | null; // null = move to root
color?: string;
description?: string;
sort?: number;
}Result: FolderSummary.
Params:
interface DeleteFolderParams {
folderId: string;
deleteSubfolders?: boolean; // Omitted → Foundry's default: subfolders are kept and moved up one level
deleteContents?: boolean; // Omitted → Foundry's default: documents are kept and moved up one level
}Result: { deleted: true, folderId: string }
Script macros (type: "script") are arbitrary JavaScript with GM rights. Creating, updating or executing one is refused unless Allow Script Macros is on in the module settings, with the error Script macros are disabled. Enable "Allow Script Macros" in module settings to manage script-type macros via API. Chat macros are always allowed.
Params: { type?: 'chat' | 'script' }
Result: MacroSummary[]
interface MacroSummary {
id: string;
uuid: string;
name: string;
type: 'chat' | 'script';
img: string;
scope: 'global' | 'actors' | 'actor';
folder: string | null;
authorId: string | null;
}Params: { macroId: string }
Result: MacroDetail — MacroSummary plus command: string (the macro body).
Params:
interface CreateMacroParams {
name: string;
type: 'chat' | 'script';
command: string;
scope?: 'global' | 'actors' | 'actor';
img?: string;
folder?: string;
}Result: MacroDetail.
Same fields as create-macro, all optional, plus macroId; folder: null moves the macro to the root.
Result: MacroDetail.
Params: { macroId: string }
Result: { deleted: true, macroId: string }
Run a macro. actorId and tokenId set the actor and token the macro sees; the token must be on the active scene. Script macros need Allow Script Macros in the module settings.
Since 8.18.0 the call also takes args (any JSON object) and answers with the macro's return value.
-
argsreaches a script macro as the variableargsand asscope.args. Its keys are not spread into the macro's own variables, so the one reserved name cannot collide withspeaker,actor,token,characterorscope. Withoutargsthe macro sees exactly the scope earlier versions sent. A chat macro acceptsargsand ignores it (the core ignores the scope for chat macros). Anything but a JSON object (null, an array, a string, a number) fails withargs must be a JSON objectbefore the macro runs. -
resultis the macro'sreturnvalue made JSON-safe:undefined, functions and symbols are dropped (absent at the root, omitted from objects,nullinside arrays);NaN/±Infinitybecomenull; bigints their decimal string;Dateits ISO string;Error{ name, message }; a Foundry Document (anything with a stringdocumentNameanduuid) is reduced to{ documentName, id, uuid, name }and never expanded;Map/Collection/Setbecome arrays of their values; any other object withtoJSON()(aRoll) uses it; a value already on the current path becomes"[Circular]"and nesting deeper than 32 levels"[MaxDepth]". When the serialized JSON exceeds 256 KB (262 144 UTF-8 bytes)resultis absent and the answer carriesresultTruncated: trueandresultBytesinstead. A macro that returns nothing gets noresultkey; a chat macro's result is the created message's reference ({ documentName: 'ChatMessage', id, uuid }) or absent. - Before a script macro runs the module compiles its text (without executing it) and answers
Macro "<name>" has a syntax error: <message>; a runtime error inside the macro answersMacro "<name>" failed: <message>. Checks run in this order: macro exists → script permission →argsshape → syntax →actorId/tokenId→ execution, so a bad call never half-executes. - A macro that waits for a dialog or a long-running promise does not answer in time: the platform waits 30 s and reports a timeout while the macro keeps running in Foundry. Return from the macro instead of waiting on the user.
Params:
interface ExecuteMacroParams {
macroId: string;
actorId?: string;
tokenId?: string;
/** Since 8.18.0. Any JSON object; a script macro reads it as `args` / `scope.args`, a chat macro ignores it. */
args?: Record<string, unknown>;
}Result:
interface ExecuteMacroResult {
executed: true;
macroId: string;
macroName: string;
macroType: 'chat' | 'script';
/** Since 8.18.0. The macro's return value made JSON-safe; absent when it returned undefined or was truncated. */
result?: unknown;
/** Since 8.18.0. Present only when the serialized result exceeded 256 KB; `result` is then absent. */
resultTruncated?: true;
/** Since 8.18.0. UTF-8 size of the serialized result; present only with resultTruncated. */
resultBytes?: number;
}Example:
// → { "type": "execute-macro", "params": { "macroId": "aB3dE5fG7hI9jK1l", "args": { "itemId": "sword1", "targetTokenId": "tok42", "advantage": true } } }
// ← { "success": true, "data": { "executed": true, "macroId": "aB3dE5fG7hI9jK1l", "macroName": "Attack", "macroType": "script",
// "result": { "hit": true, "roll": 18, "ac": 15, "damage": [{ "type": "slashing", "amount": 7 }], "hpBefore": 22, "hpAfter": 15 } } }Errors: Macro not found: <id>, Script macros are disabled. Enable "Allow Script Macros" in module settings to manage script-type macros via API., args must be a JSON object, Macro "<name>" has a syntax error: <message>, Actor not found: <id>, Token not found on active scene: <id>, Macro "<name>" failed: <message>.
Params: none ({}).
Result: PlaylistSummary[]
interface PlaylistSummary {
id: string;
uuid: string;
name: string;
mode: 'disabled' | 'sequential' | 'shuffle' | 'simultaneous' | 'soundboard';
channel: 'music' | 'environment' | 'interface' | null;
fade: number | null; // Fade duration in ms
playing: boolean;
description: string | null;
folder: string | null;
soundCount: number;
}Params: { playlistId: string }
Result: PlaylistDetail — PlaylistSummary plus sounds:
interface PlaylistSoundSummary {
id: string;
name: string;
path: string;
playing: boolean;
volume: number; // 0-1
repeat: boolean;
}Params: { playlistId: string }
Result: { playing: true, playlistId: string, soundCount: number } / { stopped: true, playlistId: string }
Params: { playlistId: string, soundId: string }
Result: { playing: true, playlistId, soundId, soundName } / { stopped: true, playlistId, soundId }
Play an audio file once, outside any playlist.
Params:
interface PlaySoundOnceParams {
src: string; // Audio file path or URL
volume?: number; // 0-1, default 0.8
loop?: boolean; // default false
broadcast?: boolean; // Play for every connected client, default true
}Result: { playing: true, src: string, broadcast: boolean }
Params:
interface AddSoundToPlaylistParams {
playlistId: string;
name: string;
path: string;
volume?: number;
repeat?: boolean;
description?: string;
}Result: PlaylistSoundSummary (see get-playlist).
Params: none. Result: { worldTime: number } — game time in seconds.
Params: { seconds: number } — seconds to add to the world time. Result: { worldTime: number, advancedBy: number }
Params: { worldTime: number }. Result: { worldTime: number }
Params: none. Result: { paused: true } / { paused: false } / { paused: boolean }
Show a Foundry notification on the GM's client.
Params:
interface NotifyParams {
message: string;
type?: 'info' | 'warn' | 'error' | 'success'; // default "info"; "success" falls back to "info" on cores without it
permanent?: boolean; // Stay until dismissed
}Result: { shown: true, type: NotifyType }
Pan or zoom the GM's canvas (canvas.animatePan).
Params: { x?: number, y?: number, scale?: number, duration?: number } — target pixels, zoom factor, animation length in ms.
Result: { panned: true }
Ping a point on the canvas for every connected user, like Foundry's long-press ping.
Params:
interface PingLocationParams {
x: number;
y: number;
style?: 'pulse' | 'arrow' | 'alert' | 'chevron';
color?: string;
duration?: number; // ms
}Result: { pinged: true, x: number, y: number }
PF2e commands run only when the world's game system is pf2e. Each is a fully separate implementation from the D&D 5e commands. Calling a pf2e/* command in a non-PF2e world (or a dnd5e/* / legacy bare command in a PF2e world) returns Operation '<command>' is not supported by game system '<world-system>'. PF2e API verified against pf2e 7.12.2.
pf2e/roll-skill, pf2e/roll-save, pf2e/roll-perception — actor checks via the PF2e Statistic API. Each returns the standard RollResult (degree of success → isCritical on a critical success, isFumble on a critical failure). PF2e has no bare ability checks, so there is no pf2e/roll-ability.
interface RollSkillParams {
actorId: string;
skill: string; // one of the 16 PF2e slugs: acrobatics, arcana, athletics, crafting,
// deception, diplomacy, intimidation, medicine, nature, occultism,
// performance, religion, society, stealth, survival, thievery
showInChat?: boolean;
}
interface Pf2eRollSaveParams {
actorId: string;
save: "fortitude" | "reflex" | "will";
showInChat?: boolean;
}
interface RollPerceptionParams {
actorId: string;
showInChat?: boolean;
}dnd5e/roll-perception (and the neutral roll-perception) also exist for D&D 5e, rolling the prc skill.
Example:
{ "id": "10", "type": "pf2e/roll-skill", "params": { "actorId": "abc", "skill": "acrobatics" } }
{ "id": "11", "type": "pf2e/roll-save", "params": { "actorId": "abc", "save": "reflex" } }interface SetConditionParams { actorId: string; slug: string; value?: number; } // valued → exact value
interface ConditionSlugParams { actorId: string; slug: string; } // remove / increase / decrease
interface GetConditionsParams { actorId: string; }-
pf2e/set-condition— apply or set a condition; for valued conditionsvaluesets the exact value. -
pf2e/increase-condition/pf2e/decrease-condition— raise/lower a valued condition by 1 (decrease removes at 0). -
pf2e/remove-condition— remove the condition entirely. -
pf2e/get-conditions— list the actor's active conditions with their values.
43 supported slugs (e.g. frightened, prone, clumsy, sickened, slowed); persistent-damage and malevolence are not supported. 12 are valued: frightened, clumsy, drained, enfeebled, sickened, stupefied, slowed, stunned, doomed, dying, wounded, cursebound.
Result (set / increase / decrease):
interface ConditionMutationResult {
actorId: string;
condition: { slug: string; name: string; value: number | null; active: boolean } | null;
}interface ListStrikesParams { actorId: string; }
interface RollStrikeParams { actorId: string; slug: string; mapIncrease?: 0 | 1 | 2; showInChat?: boolean; }
interface RollStrikeDamageParams { actorId: string; slug: string; critical?: boolean; showInChat?: boolean; }-
pf2e/list-strikes— available strikes (slug,label,ready, variant labels). -
pf2e/roll-strike— attack roll;mapIncreaseselects the multiple-attack-penalty step (0 = none, 1 = −5, 2 = −10). ReturnsRollResultwith degree-of-success flags. -
pf2e/roll-strike-damage— damage roll;critical: truerolls critical damage.
PF2e has no generic "use item", so entry points are per item type.
interface UseConsumableParams { actorId: string; itemId: string; quantity?: number; } // potions, elixirs, scrolls…
interface CastSpellParams { actorId: string; spellId: string; rank?: number; showInChat?: boolean; } // rank = heightened level 1–10
interface PostItemParams { actorId: string; itemId: string; showInChat?: boolean; } // post any item card to chat-
pf2e/use-consumable— consume a consumable (decrements uses/quantity, posts to chat, applies embedded effects). Returns remaining uses/quantity. -
pf2e/cast-spell— cast a spell via its spellcasting entry, with optional heightenedrank. -
pf2e/post-item— post any item's card to chat (returns the chat message id).
pf2e/filter-compendium-actors, pf2e/filter-compendium-items — specification search over compendium packs with a PF2e-native filter surface. Data paths verified against pf2e 7.12.2.
interface Pf2eFilterCompendiumActorsParams {
packIds?: string[]; // Explicit Actor packs; omitted → all Actor packs (bestiaries)
name?: string;
type?: string[]; // "army" | "character" | "familiar" | "hazard" | "loot" | "npc" | "party" | "vehicle"
level?: { min?: number; max?: number }; // Negative creature levels are valid (e.g. min: -1)
traits?: string[]; // ALL listed traits must be present, e.g. ["undead", "incorporeal"]
rarity?: string[]; // "common" | "uncommon" | "rare" | "unique"
size?: string[]; // "tiny" | "sm" | "med" | "lg" | "huge" | "grg"
maxHp?: { min?: number; max?: number };
ac?: { min?: number; max?: number };
limit?: number; // 1..200, default 50
offset?: number;
}
interface Pf2eFilterCompendiumItemsParams {
packIds?: string[];
name?: string;
type?: string[]; // All 25 PF2e item types: "feat", "spell", "equipment", "weapon", "ancestry", …
level?: { min?: number; max?: number }; // Item/feat level; spell rank for spells
traits?: string[]; // ALL-of
rarity?: string[];
category?: string[]; // Feat category: "class", "skill", "general", "ancestry", …
traditions?: string[]; // ANY-of, spells: "arcane" | "divine" | "occult" | "primal"
priceGold?: { min?: number; max?: number }; // Price normalized to gp from the {pp,gp,sp,cp} coins object
limit?: number;
offset?: number;
}Both return { results: { id, name, level, packId, uuid }[], total, hasMore } — entries carry level (the primary PF2e content-selection axis) and are sorted by level → name → id.
{ "id": "1", "type": "pf2e/filter-compendium-items",
"params": { "type": ["feat"], "level": { "min": 1, "max": 4 }, "traits": ["fighter"], "category": ["class"] } }SWADE commands run only when the world's game system is swade (since 8.16.0, verified against swade 6.0.4 on Foundry v14). Each is a separate implementation under src/systems/swade/; calling a swade/* command in another world returns Operation '<command>' is not supported by game system '<world-system>'. Every actor-bound command addresses a world actor by actorId or one token's actor by tokenId (+ optional sceneId, default the active scene), so an unlinked token takes the hit alone; results echo actorId and, when given, tokenId. Errors: Actor not found: <id>, Token not found on active scene: <id>, Scene not found: <id>, Skill not found: <name or id>, Item not found: <id>, Item is not a weapon: <id>, Either actorId or tokenId is required.
swade/roll-attribute and swade/roll-skill build the roll through the system (actor.rollAttribute / rollSkill / makeUnskilledAttempt with suppressChat), so the RollDialog never opens; the system still adds its own modifiers (wounds, fatigue, Distracted, conviction, global modifiers).
interface SwadeActorTargetParams {
actorId?: string; // either this …
tokenId?: string; // … or this (one token's actor)
sceneId?: string; // default: active scene
}
interface SwadeRollAttributeParams extends SwadeActorTargetParams {
attribute: "agility" | "smarts" | "spirit" | "strength" | "vigor";
modifier?: number;
modifierLabel?: string;
targetNumber?: number; // default 4
ignoreWounds?: boolean;
showInChat?: boolean; // default false; true posts the system's own card
}
interface SwadeRollSkillParams extends SwadeActorTargetParams {
skill: string; // skill item id OR name (case-insensitive)
unskilled?: boolean; // roll d4−2 when the actor lacks the skill (else "Skill not found")
modifier?: number; modifierLabel?: string; targetNumber?: number; ignoreWounds?: boolean; showInChat?: boolean;
}
interface SwadeTraitRollResult {
actorId: string; tokenId?: string;
trait: string; // "vigor", the skill name, or "Unskilled"
total: number; // kept die + modifiers
formula: string;
dice: { type: string; results: number[]; total: number; wild: boolean; kept: boolean }[]; // every die incl. explosions
modifiers: { label: string; value: number | string }[];
modifierTotal: number; // total − kept die
targetNumber: number;
successes: number; // −1 critical failure, 0 failure, 1 success, 2+ raises
outcome: "critical-failure" | "failure" | "success" | "raise";
raises: number;
isCriticalFailure: boolean;
wildDie: boolean; // a wild die was in the pool (Wild Cards)
isCritical: boolean; // raises > 0
isFumble: boolean; // isCriticalFailure
messageId?: string; // when showInChat
}Example:
{ "id": "1", "type": "swade/roll-attribute", "params": { "actorId": "abc", "attribute": "vigor", "targetNumber": 6 } }
{ "id": "2", "type": "swade/roll-skill", "params": { "tokenId": "tok1", "skill": "Notice", "modifier": -2, "showInChat": true } }swade/roll-attack rolls the weapon's skill (system.actions.trait; unskilled when the actor lacks it) with the weapon passed to the system, which adds its default attack modifiers (range, cover, illumination, prone) for the GM's current targets — target with set-token-target first. A melee attack on targetTokenId uses the target's Parry as the target number. No ammo is consumed.
interface SwadeRollAttackParams extends SwadeActorTargetParams {
itemId: string; // weapon item id
targetTokenId?: string;
targetNumber?: number; // explicit > target's Parry (melee) > 4
modifier?: number; modifierLabel?: string; showInChat?: boolean;
}
interface SwadeAttackRollResult extends SwadeTraitRollResult {
weapon: { id: string; name: string; damage: string; ap: number; range: string; isMelee: boolean };
target?: { tokenId: string; actorId: string; name: string; parry: number };
hit: boolean; // successes >= 1
}
interface SwadeRollDamageParams extends SwadeActorTargetParams {
itemId: string;
raise?: boolean; // adds the weapon's bonus damage die (default +1d6x)
modifier?: number | string;
modifierLabel?: string;
ap?: number; // overrides the weapon's AP
targetTokenId?: string; // preview (never apply) the damage rule against that token
showInChat?: boolean;
}
interface SwadeDamageRollResult {
actorId: string; tokenId?: string;
weapon: { id: string; name: string };
total: number; formula: string;
dice: { type: string; results: number[]; total: number; wild: false; kept: true }[];
ap: number; isHeavyWeapon: boolean; raiseDamageIncluded: boolean;
versusTarget?: SwadeDamagePreview;
isCritical: false; isFumble: false;
messageId?: string;
}swade/apply-damage applies the Toughness rule (as the system's damage applicator computes it): AP eats armor, a hit without a raise Shakes, every raise is a Wound, a second Shaken becomes a Wound unless the actor has the hardy flag, the Wound Cap setting limits Wounds to 4, Wounds past the maximum Incapacitate; vehicles are never Shaken. apply: false previews without touching the actor.
interface SwadeApplyDamageParams extends SwadeActorTargetParams {
damage: number; // >= 0
ap?: number; // default 0
isHeavyWeapon?: boolean;
apply?: boolean; // default true
}
interface SwadeDamagePreview {
toughness: number; armor: number; apApplied: number; effectiveToughness: number;
excess: number; // damage − effectiveToughness (may be negative)
outcome: "none" | "shaken" | "wounded";
woundsInflicted: number; // after the wound cap
doubleShaken: boolean;
wouldIncapacitate: boolean;
}
interface SwadeApplyDamageResult extends SwadeDamagePreview {
actorId: string; tokenId?: string; applied: boolean;
shakenBefore: boolean; shakenAfter: boolean;
woundsBefore: number; woundsAfter: number; woundsMax: number;
incapacitated: boolean;
canSoak: boolean; // applied && woundsInflicted > 0 && wild card && bennies > 0
}
interface SwadeSoakParams extends SwadeActorTargetParams {
wounds: number; // >= 1, the wounds just inflicted
spendBenny?: boolean; // default true ("No bennies left: <name>" when empty)
modifier?: number; modifierLabel?: string;
clearIncapacitated?: boolean; // also clear Incapacitated on a full soak
showInChat?: boolean;
}
interface SwadeSoakResult {
actorId: string; tokenId?: string;
roll: SwadeTraitRollResult; // the Vigor roll (soak bonus included)
woundsSoaked: number; woundsRemaining: number; woundsAfter: number;
bennySpent: boolean; benniesLeft: number;
shakenRemoved: boolean; // full soak also removes Shaken
}Only Wild Cards can soak (Only Wild Cards can soak: <name>).
Example:
{ "id": "3", "type": "swade/apply-damage", "params": { "tokenId": "gob3", "damage": 12, "ap": 2 } }
{ "id": "4", "type": "swade/soak", "params": { "actorId": "abc", "wounds": 1 } }interface SwadeBenniesParams extends SwadeActorTargetParams {
gm?: true; // the GM's bennies on game.user instead of an actor's
action: "spend" | "give" | "refresh";
}
interface SwadeBenniesResult {
owner: "actor" | "gm"; actorId?: string; tokenId?: string;
action: "spend" | "give" | "refresh";
before: number; after: number; max?: number;
}swade/set-status toggles one of the 32 status ids the system registers through actor.toggleActiveEffect; linked statuses (Stunned → Distracted, Prone, Vulnerable; Bound → Entangled, Distracted; Entangled → Vulnerable) come from the system and appear in statuses.
interface SwadeSetStatusParams extends SwadeActorTargetParams {
status: "shaken" | "incapacitated" | "dead" | "aiming" | "berserk" | "wild-attack" | "defending" | "holding"
| "bound" | "entangled" | "frightened" | "distracted" | "encumbered" | "prone" | "stunned" | "vulnerable"
| "bleeding-out" | "diseased" | "heart-attack" | "on-fire" | "poisoned" | "cover-shield" | "cover" | "reach"
| "torch" | "burrowing" | "flying" | "invisible" | "blind" | "cold-bodied" | "smite" | "protection";
active: boolean;
overlay?: boolean;
}
interface SwadeSetStatusResult { actorId: string; tokenId?: string; status: string; active: boolean; statuses: string[] }swade/get-actor-summary { actorId | tokenId, sceneId? } is a side-effect-free normalized read of the sheet. Sections an actor type lacks are omitted (vehicles have no attributes, skills or bennies; groups carry almost nothing).
interface SwadeActorSummary {
actorId: string; tokenId?: string; name: string;
type: "character" | "npc" | "vehicle" | "group"; wildcard: boolean;
attributes?: Record<"agility" | "smarts" | "spirit" | "strength" | "vigor", { die: number; modifier: number; wildDie: number }>;
skills?: { id: string; name: string; die: number; modifier: number; wildDie: number; attribute: string }[];
derived?: { parry: number; toughness: number; armor: number; size: number; pace: number; runningDie: string | null };
bennies?: { value: number; max: number };
wounds?: { value: number; max: number; ignored: number; penalty: number }; // penalty from calcWoundPenalties()
fatigue?: { value: number; max: number; penalty: number }; // calcFatiguePenalties()
powerPoints?: Record<string, { value: number; max: number }>;
status: { shaken: boolean; stunned: boolean; distracted: boolean; vulnerable: boolean; entangled: boolean; bound: boolean; incapacitated: boolean; active: string[] };
conviction?: { value: number; active: boolean };
rank?: string;
initiative?: { levelHeaded: boolean; improvedLevelHeaded: boolean; quick: boolean; hesitant: boolean };
weapons: { id: string; name: string; damage: string; ap: number; rof: number; range: string; parry: number; minStr: string; trait: string; traitMod: string; dmgMod: string; equipStatus: number; shots: number; currentShots: number; isMelee: boolean; isHeavyWeapon: boolean }[];
powers: { id: string; name: string; pp: number; arcane: string; rank: string; range: string; duration: string; damage: string }[];
vehicle?: { toughness: number; armor: number; wounds: { value: number; max: number }; handling: number; topSpeed: { value: number; unit: string }; outOfControl: boolean; wrecked: boolean };
}swade/get-action-cards { combatId? } (default: the active combat, else No active combat) lists the cards the combat dealt. The neutral roll-initiative / roll-all-initiative draw action cards in a SWADE world; initiative is cardValue + suitValue / 10.
interface SwadeActionCardsResult {
combatId: string; round: number; turn: number;
combatants: {
combatantId: string; name: string; actorId: string | null; tokenId: string | null;
initiative: number | null; cardValue: number | null; suitValue: number | null; cardString: string | null;
hasJoker: boolean; roundHeld: number | null; turnLost: boolean; isGroupLeader: boolean; isDefeated: boolean;
}[];
}CoC7 commands run only when the world's game system is CoC7 (capitals; the wire prefix stays lowercase coc7/). Since 8.17.0, verified against CoC7 8.16 on Foundry 12–14. Each is a separate implementation under src/systems/coc7/; calling a coc7/* command in another world returns Operation '<command>' is not supported by game system '<world-system>'. Every actor-bound command addresses a world actor by actorId or one token's actor by tokenId (+ optional sceneId, default the active scene); results echo actorId and, when given, tokenId.
The system keeps its check classes internal (CoC7Check, CoC7DicePool, CoC7SanCheckCard), so the module rolls the d100 pool itself through Foundry's Roll (1d10 unit, one d10 per tens die; Dice So Nice animates them) and resolves it with a port of CoC7DicePool. showInChat therefore posts the module's own card (title, threshold with difficulty, the dice, the total, the outcome; the Roll objects attached), not the system's; blind: true whispers it to the GMs. Damage, hit points, Luck, conditions and bouts of madness go through the system's public Actor API.
Errors: Either actorId or tokenId is required, Actor not found: <id>, Token not found on active scene: <id> / Token not found on scene <sceneId>: <id>, Token has no actor: <id>, Scene not found: <id>, No active scene, Skill not found: <skill>, Weapon not found: <itemId>, Item is not a weapon: <name>, Weapon has no skill: <name>, Weapon has no damage for range: <band>, Actor has no characteristics: <name>, Actor has no sanity: <name>, Actor has no luck: <name>, Actor has no hit points: <name>, Actor has no conditions: <name>, Actor type is not supported: container, bonusDice and penaltyDice are exclusive, Either damage or heal is required, damage and heal are exclusive, amount must be at least 1 for spend and add, Not enough Luck: has <n>, needs <amount>, Bout of Madness table is not configured, Target token not found: <id>.
interface Coc7ActorTargetParams {
actorId?: string; // either this …
tokenId?: string; // … or a token's actor (an unlinked token takes the hit alone)
sceneId?: string; // scene of the token; default the active scene
}
type Coc7DifficultyWire = 'unknown' | 'regular' | 'hard' | 'extreme' | 'critical';
interface Coc7CheckOptions {
difficulty?: Coc7DifficultyWire; // default: the world setting defaultCheckDifficulty
bonusDice?: number; // 0–2, exclusive with penaltyDice
penaltyDice?: number; // 0–2, exclusive with bonusDice
flatDiceModifier?: number; // added to the rolled number (house rule)
flatThresholdModifier?: number; // added to the threshold (house rule)
blind?: boolean; // whisper the card to GMs
showInChat?: boolean; // post the module's card; default false
}
interface Coc7CheckResult {
actorId: string; tokenId?: string;
kind: 'skill' | 'characteristic' | 'attribute' | 'weapon';
name: string; // skill name, STR … EDU, Idea, Know, Luck, Sanity
threshold: number; // the value rolled against, before flatThresholdModifier
difficulty: Coc7DifficultyWire; // effective difficulty
thresholds: { regular: number; hard: number; extreme: number; fumbleFrom: number }; // upper bounds, 0 = level absent; fumble from 96 or 100
dice: { tens: { value: number; kept: boolean }[]; unit: number; bonusDice: number; penaltyDice: number }; // tens 0…90 (0 = 00)
total: number; // 1–100 after flatDiceModifier
successLevel: number; // −99 fumble, 0 failure, 1 regular, 2 hard, 3 extreme, 4 critical
outcome: 'fumble' | 'failure' | 'regular' | 'hard' | 'extreme' | 'critical';
isSuccess: boolean | null; // successLevel >= difficulty; null when the difficulty is unknown
isCritical: boolean; isFumble: boolean;
luckRequired: { regular: number; hard: number; extreme: number }; // Luck that would raise the roll to each level
canPush: boolean; // a failed, pushable roll: call again for the pushed roll
markedForDevelopment: boolean; // the skill got its development tick
messageId?: string;
}coc7/roll-skill — Coc7ActorTargetParams & Coc7CheckOptions & { skill: string } → Coc7CheckResult. skill is a skill item id, a name (Spot Hidden, Fighting (Brawl), case-insensitive) or a CoC ID (i.skill.dodge). Threshold = item.system.value. canPush needs the skill's push property, a failure and no fumble. The development tick (system.flags.developement) is set on a rolled success (no bonus or penalty dice) when xpEnabled, the difficulty is known, the roll is not blind and the skill allows XP.
coc7/roll-characteristic — … & { characteristic: 'str' | 'con' | 'siz' | 'dex' | 'app' | 'int' | 'pow' | 'edu' | 'idea' | 'know' } → Coc7CheckResult. idea rolls INT (name Idea), know rolls EDU (Know); otherwise name is the upper-case key. No development tick.
coc7/roll-attribute — … & { attribute: 'luck' | 'sanity' } → Coc7CheckResult. A plain Luck or SAN roll without any loss (the loss flow is coc7/sanity-check). Sanity is never pushable.
{ "type": "coc7/roll-skill", "params": { "actorId": "abc123", "skill": "Spot Hidden", "bonusDice": 1, "showInChat": true } }coc7/roll-attack — Coc7ActorTargetParams & Coc7CheckOptions & { itemId: string; alternativeSkill?: boolean } → Coc7AttackRollResult. itemId is a weapon item id or name. The check runs against the weapon's main skill, or the alternative skill (burst, automatic fire, thrown) with alternativeSkill: true. No target is resolved and no ammo is consumed.
interface Coc7AttackRollResult extends Coc7CheckResult {
weapon: {
id: string; name: string;
skill: { id: string; name: string; value: number };
ranged: boolean; malfunction?: number; impaling: boolean;
damage: { normal: string; long?: string; extreme?: string }; // damage bonus already applied, e.g. "1d6+1d4"
};
isMalfunction: boolean; // total >= weapon.malfunction
impale: boolean; // extreme or critical success with an impaling weapon
}coc7/roll-damage — Coc7ActorTargetParams & { itemId: string; range?: 'normal' | 'long' | 'extreme'; successLevel?: 'regular' | 'hard' | 'extreme'; targetTokenId?: string; showInChat?: boolean } → Coc7DamageRollResult. The formula is the band's damage plus the damage bonus (addb → the actor's full db, ahdb → half of it as the system's halfDB computes it). successLevel: 'extreme' maximizes the formula; an impaling weapon then adds a normal damage roll on top (the system leaves the damage bonus out of that extra roll). targetTokenId previews the total against the target's armor, a number or a rolled formula; nothing is applied. showInChat posts the Foundry roll with the flavor <weapon> damage (<band>).
interface Coc7DamageRollResult {
actorId: string; tokenId?: string;
weapon: { id: string; name: string };
range: 'normal' | 'long' | 'extreme';
formula: string;
damageBonus: string; // the term appended: "+1d4", "+1D2", "-1" or ""
impale: boolean; maximized: boolean;
dice: { formula: string; results: number[]; total: number }[];
total: number;
versusTarget?: { tokenId: string; name: string; armor: number; armorFormula?: string; wouldInflict: number };
messageId?: string;
}coc7/apply-damage — Coc7ActorTargetParams & { damage?: number; heal?: number; ignoreArmor?: boolean; armor?: string | number } → Coc7ApplyDamageResult. Exactly one of damage (≥ 1, before armor) or heal (≥ 1, capped at the maximum). Damage goes through the system's actor.dealDamage (armor subtracted unless ignoreArmor; armor overrides the sheet's value; Major Wound at damage ≥ half the maximum, with Prone; Unconscious / Dying at 0 HP; Dead at damage ≥ the maximum; under the pulp rule a CON check card instead). Healing goes through setHp. Vehicles have no hit points here.
interface Coc7Conditions { criticalWounds: boolean; unconscious: boolean; dying: boolean; dead: boolean; prone: boolean; tempoInsane: boolean; indefInsane: boolean }
interface Coc7ApplyDamageResult {
actorId: string; tokenId?: string;
applied: number; // damage that went through armor
armorAbsorbed: number;
healed: number;
hpBefore: number; hpAfter: number; hpMax: number;
majorWound: boolean; unconscious: boolean; dying: boolean; dead: boolean;
conditions: Coc7Conditions;
}coc7/sanity-check — Coc7ActorTargetParams & { lossPassed: string | number; lossFailed: string | number; reason?: string; difficulty?: Coc7DifficultyWire; bonusDice?: number; penaltyDice?: number; apply?: boolean; showInChat?: boolean } → Coc7SanityCheckResult. The system's SAN check card flow without the card: the SAN roll (against attribs.san.value), then the loss — lossPassed on a success, lossFailed on a failure, the maximum of lossFailed on a fumble — capped by what the reason (a creature name) can still inflict (maxLossToSanReason), halved for a Mythos hardened investigator. apply (default true) writes what the system's setSan would, without its spend-Luck dialog: SAN, dailyLoss, the reason's running total, Temporary Insanity at 5+ in one go, Indefinite Insanity when dailyLoss reaches a dailyLimit above 0. Immune during a Bout of Madness (immune: 'bout') or when the reason is exhausted ('reason').
interface Coc7SanityCheckResult {
actorId: string; tokenId?: string;
roll: Coc7CheckResult; // kind 'attribute', name 'Sanity'
immune: 'none' | 'bout' | 'reason';
lossFormula: string; lossRolled: number;
lossApplied: number; // after the per-reason cap and Mythos hardening
limitedByReason: boolean; mythosHardened: boolean;
applied: boolean; // false with apply: false (sanAfter / dailyLoss are then projections)
sanBefore: number; sanAfter: number; sanMax: number;
dailyLoss: number; dailyLimit: number;
temporaryInsanity: boolean; // lost 5+ at once: an INT roll decides the bout of madness
indefiniteInsanity: boolean; permanentInsanity: boolean;
luckOfferSkipped: boolean; // the pulp rule "Luck halves SAN loss" is on and was not offered
messageId?: string;
}coc7/bout-of-madness — Coc7ActorTargetParams & { realTime?: boolean; duration?: number } → Coc7BoutOfMadnessResult. realTime (default true) draws from the world's real-time bout table (rounds), false from the summary table (hours); an unset table ('none') or one that does not resolve answers Bout of Madness table is not configured. Calls the system's actor.enterBoutOfMadness, which rolls the table, adds the drawn phobia or mania as a status item and sets Temporary Insanity for a real-time bout.
interface Coc7BoutOfMadnessResult {
actorId: string; tokenId?: string;
name: string; description: string; // the table result, enriched HTML flattened to text
phobia: boolean; mania: boolean;
realTime: boolean; duration: number;
tempoInsane: boolean;
}coc7/luck — Coc7ActorTargetParams & { action: 'spend' | 'set' | 'add'; amount: number } → { actorId, tokenId?, action, before, after }. spend goes through spendLuck (refused with Not enough Luck: has <n>, needs <amount>), set and add through setLuck (capped at 99). amount is an integer ≥ 0 (≥ 1 for spend / add).
coc7/set-condition — Coc7ActorTargetParams & { condition: 'criticalWounds' | 'unconscious' | 'dying' | 'dead' | 'prone' | 'tempoInsane' | 'indefInsane'; active: boolean; realTime?: boolean; duration?: number } → { actorId, tokenId?, conditions: Coc7Conditions, statuses: { id, name, phobia, mania }[] }. active: true calls conditionsSet (with realTime / duration, which only matter for tempoInsane), false calls conditionsUnset; the system creates or deletes the status effect when status icons are enabled and sets the sheet flag otherwise. statuses lists the actor's phobia and mania items.
coc7/get-actor-summary — Coc7ActorTargetParams → Coc7ActorSummary (read-only).
interface Coc7ActorSummary {
id: string; name: string; type: string;
characteristics: { key: 'str' | 'con' | 'siz' | 'dex' | 'app' | 'int' | 'pow' | 'edu'; label: string; value: number; half: number; fifth: number }[];
attributes?: { // absent for vehicles
hp: { value: number; max: number }; mp: { value: number; max: number };
sanity: { value: number; max: number; dailyLoss: number; dailyLimit: number; mythosHardened: boolean };
luck: number; mov: number; db: string; build: number; armor: string;
};
conditions: Coc7Conditions;
statuses: { id: string; name: string; phobia: boolean; mania: boolean }[];
skills: { id: string; name: string; value: number; half: number; fifth: number; base: string; specialization?: string; push: boolean; fighting: boolean; firearm: boolean; special: boolean; rare: boolean; development: boolean }[]; // sorted by name
weapons: { id: string; name: string; skill?: { id; name; value }; alternativeSkill?: { id; name; value }; ranged: boolean; damage: { normal: string; long?: string; extreme?: string }; range: { normal: string; long?: string; extreme?: string }; usesPerRound: string; bullets?: number; ammo?: number; malfunction?: number; impaling: boolean; properties: string[] }[];
spells: { id: string; name: string; costs: { hitPoints: string; magicPoints: string; sanity: string; power: string; others: string } }[];
infos: { occupation: string; age: string; archetype?: string; organization?: string };
creditRating?: number; // the Credit Rating skill value when present
creature?: { sanLoss: { passed: string; failed: string }; attacksPerRound: string }; // npc / creature
vehicle?: { hp?: number; mov?: number; build?: number; armor?: number; crew: string; armed: boolean };
}Containers answer Actor type is not supported: container.
Opposed and combined rolls, melee defense resolution, maneuvers, multiple-target or automatic fire, reload and ammunition, pushing as a system state (the agent re-rolls; canPush says when), the development phase, chases, the spend-Luck offer of the pulp SAN rule, Idea-roll clue handling, the system's own chat cards.
On error, response includes success: false and error message:
{
"id": "123",
"success": false,
"error": "Actor not found: invalid-id"
}Common errors:
- Actor/Item/Scene/Combat not found
- Invalid parameters
- Combat not started (for turn navigation)
- Permission denied