Skip to content
Alex edited this page Jul 15, 2026 · 16 revisions

WebSocket API

The module connects to your server as a WebSocket client. Your server sends commands, Foundry executes them and returns results.

Connection

  • Foundry connects to: ws://your-server:port/ws
  • Protocol: JSON messages
  • Auto-reconnect: Configurable interval and max attempts

Message Format

Command (Server → Foundry)

interface Command {
  id: string;      // Unique identifier for correlation
  type: CommandType;
  params: object;
}

Response (Foundry → Server)

interface CommandResponse {
  id: string;      // Same as command ID
  success: boolean;
  data?: object;   // Result on success
  error?: string;  // Error message on failure
}

Commands Reference

Dice Rolling

roll-dice

Execute arbitrary dice formula.

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;
}

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] }] } }

dnd5e/roll-skill

Roll a D&D 5e skill check.

Naming (v8.6.0+): This is the canonical, system-namespaced command (<system>/<command>). The legacy roll-skill name still works as a deprecated alias that routes to the same handler, so existing integrations are unaffected — new integrations should prefer dnd5e/roll-skill.

Params:

interface RollSkillParams {
  actorId: string;
  skill: SkillKey;       // See skill keys table
  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" } }

dnd5e/roll-save

Roll a D&D 5e saving throw.

Naming (v8.7.0+): Canonical, system-namespaced name. The legacy roll-save still works as a deprecated alias routing to the same handler.

Params:

interface RollSaveParams {
  actorId: string;
  ability: AbilityKey;   // "str", "dex", "con", "int", "wis", "cha"
  showInChat?: boolean;
}

dnd5e/roll-ability

Roll a D&D 5e ability check.

Naming (v8.7.0+): Canonical, system-namespaced name. The legacy roll-ability still works as a deprecated alias routing to the same handler.

Params:

interface RollAbilityParams {
  actorId: string;
  ability: AbilityKey;
  showInChat?: boolean;
}

dnd5e/roll-attack

Roll an attack with a weapon/spell.

Naming (v8.7.0+): Canonical, system-namespaced name. The legacy roll-attack still 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;
}

dnd5e/roll-damage

Roll damage for a weapon/spell.

Naming (v8.7.0+): Canonical, system-namespaced name. The legacy roll-damage still works as a deprecated alias routing to the same handler.

Params:

interface RollDamageParams {
  actorId: string;
  itemId: string;
  critical?: boolean;    // Double dice
  showInChat?: boolean;
}

World Info

dnd5e/filter-compendium-actors

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 } } }

dnd5e/filter-compendium-items

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-world-info

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 }
    ]
  }
}

Actor Management

get-actors

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-actor

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": {} }
    ]
  }
}

filter-actors

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-actors for 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-actor

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
  }
}

create-actor-from-compendium

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

Update actor properties.

Params:

interface UpdateActorParams {
  actorId: string;
  name?: string;
  img?: string;
  folder?: string;
  system?: object;        // Partial update of system data
}

Result: ActorResult

Example:

// Command - update HP
{
  "id": "1",
  "type": "update-actor",
  "params": {
    "actorId": "abc123",
    "system": {
      "attributes": { "hp": { "value": 25 } }
    }
  }
}

delete-actor

Delete actor from the world.

Params:

interface DeleteActorParams {
  actorId: string;
}

Result: DeleteResult


Chat Messages

send-chat-message

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"
  }
}

get-chat-messages

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-chat-message

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-chat-message

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 } }

clear-chat

Delete all messages from the chat log.

Params:

// No parameters required

Result:

interface ClearChatResult {
  deletedCount: number;
}

Example:

// Command
{ "id": "1", "type": "clear-chat", "params": {} }

// Response
{ "id": "1", "success": true, "data": { "deletedCount": 142 } }

export-chat

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" } }

Journal Management

get-journals

Get a list of all journal entries in the world.

Params:

// No parameters required
interface GetJournalsParams {}

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-journal

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-journal

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

Update journal properties.

Params:

interface UpdateJournalParams {
  journalId: string;
  name?: string;
  folder?: string;
}

delete-journal

Delete journal entry.

Params:

interface DeleteJournalParams {
  journalId: string;
}

Result:

interface DeleteResult {
  deleted: boolean;
}

create-journal-page

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-journal-page

Update page content.

Params:

interface UpdateJournalPageParams {
  journalId: string;
  pageId: string;
  name?: string;
  content?: string;
  src?: string;                  // Source URL for image/video/pdf pages
}

delete-journal-page

Remove page from journal.

Params:

interface DeleteJournalPageParams {
  journalId: string;
  pageId: string;
}

show-journal

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"]
  }
}

Combat Tracker

create-combat

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-combatant

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-combatant

Remove from combat.

Params:

interface RemoveCombatantParams {
  combatantId: string;
  combatId?: string;
}

start-combat

Begin combat (sets round to 1).

Params:

interface CombatIdParams {
  combatId?: string;
}

end-combat

End and delete combat. Shows confirmation dialog in Foundry VTT v13.

Params: CombatIdParams


delete-combat

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" } }

next-turn / previous-turn

Navigate turn order.

Params: CombatIdParams


set-turn

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-combat-state

Get current combat state.

Params: CombatIdParams

Result: CombatResult


get-combat-turn-context

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) ..."
  }
}

Initiative

roll-initiative

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;
  }>;
}

set-initiative

Manually set initiative value.

Params:

interface SetInitiativeParams {
  combatantId: string;
  initiative: number;
  combatId?: string;
}

roll-all-initiative

Roll initiative for all combatants.

Params:

interface RollAllInitiativeParams {
  combatId?: string;
  formula?: string;
  npcsOnly?: boolean;   // Roll only for NPCs
}

Combatant Updates

update-combatant

Update combatant properties.

Params:

interface UpdateCombatantParams {
  combatantId: string;
  combatId?: string;
  initiative?: number;
  defeated?: boolean;
  hidden?: boolean;
}

set-combatant-defeated

Mark combatant as defeated.

Params:

interface SetCombatantDefeatedParams {
  combatantId: string;
  defeated: boolean;
  combatId?: string;
}

toggle-combatant-visibility

Toggle hidden status.

Params:

interface ToggleCombatantVisibilityParams {
  combatantId: string;
  combatId?: string;
}

Token Management

create-token

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;
}

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
}

delete-token

Remove token from scene.

Params:

interface DeleteTokenParams {
  tokenId: string;
  sceneId?: string;
}

move-token

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;
  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
}

update-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;
}

get-scene-tokens

Get all tokens from scene.

Params:

interface GetSceneTokensParams {
  sceneId?: string;      // Defaults to active scene
}

Result:

interface SceneTokensResult {
  sceneId: string;
  sceneName: string;
  tokens: TokenResult[];
}

Scene Management

get-scene

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-scenes-list

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" }
    ]
  }
}

activate-scene

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-scene

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
  }
}

Item Management

get-items

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-item

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" } }

get-actor-items

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": {}
      }
    ]
  }
}

dnd5e/use-item

Use/activate an item (cast spell, use consumable, attack with weapon).

Naming (v8.7.0+): Canonical, system-namespaced name. The legacy use-item still 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;
}

interface ActivityInfo {
  id: string;
  name: string;
  type: string;
}

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"
  }
}

dnd5e/activate-item

Naming (v8.7.0+): Canonical, system-namespaced name. The legacy activate-item still 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
}

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;        // Always true on success
  targetsSet: number;        // Number of targets set before activation (0 if targetTokenIds not provided)
  rolls: RollResult[];       // Dice rolls returned by Foundry's native item.use() / activity.use()
  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.
}

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
}

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, or timed out (5s)
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 May appear (native Foundry behavior)
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 registers a Hooks.once('midi-qol.RollComplete') listener before calling use(), then awaits the workflow result with a 5-second timeout. If Midi-QOL is not installed, no hooks are registered and workflow will be absent -- zero overhead.

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"
  }
}

add-item-to-actor

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-item-from-compendium

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-actor-item

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 }
  }
}

delete-actor-item

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 } }

Effect Management

get-actor-effects

Get all active effects on an actor.

Params:

interface GetActorEffectsParams {
  actorId: string;
  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-actor-status

Toggle a D&D 5e status condition on/off.

Params:

interface ToggleActorStatusParams {
  actorId: string;
  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 } }

add-actor-effect

Create a custom ActiveEffect on an actor.

Params:

interface AddActorEffectParams {
  actorId: string;
  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-actor-effect

Remove an effect from an actor.

Params:

interface RemoveActorEffectParams {
  actorId: string;
  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-actor-effect

Update an existing effect's properties.

Params:

interface UpdateActorEffectParams {
  actorId: string;
  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" } }

Compendium Access

get-compendiums

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-compendium

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-compendium-index

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
}

search-compendium

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-compendium-document

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-from-compendium

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-compendiums

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 in the module)
}

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" }
  ]
}

search-compendium-pages

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
}

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-uuid

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.


Roll Tables

list-roll-tables

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-roll-table

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-table

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-table

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-roll-table

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

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-roll-table

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 } }

Door Management

set-door-state

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 } }

Pathfinder 2e (PF2e)

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.

Rolls

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" } }

Conditions

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 conditions value sets 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;
}

Strikes (weapon attacks)

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; mapIncrease selects the multiple-attack-penalty step (0 = none, 1 = −5, 2 = −10). Returns RollResult with degree-of-success flags.
  • pf2e/roll-strike-damage — damage roll; critical: true rolls critical damage.

Item use

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 heightened rank.
  • pf2e/post-item — post any item's card to chat (returns the chat message id).

Compendium search

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"] } }

Error Handling

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

Clone this wiki locally