Skip to main content
This page is for anyone authoring or programmatically reading Wanderer’s Guide content: homebrew creators, tool builders, and contributors. If you only want to call the public API, the API reference is enough; this is the layer underneath.

The data model

WG content lives in Postgres, with one table per primary content type: A second layer, the frontend cache, mirrors a subset of these tables in the browser so the character sheet doesn’t hit the database for every lookup. The cache is populated at app start from find-* calls and refreshed when the active character’s content_sources.enabled list changes. Weapon hands is a top-level field holding the printed value, such as 1, 2, or 1+. usage is separate: leave it empty when the source lists Hands without a Usage line. Magical equipment can have a real Usage line, which should be preserved. Weapon reload is stored in meta_data.reload; the thrown-weapon placeholder is —. Spellhearts may store explicit printed casting values in optional meta_data.spellheart_casting. Its attack and dc fields are finite integers; at least one is required. Missing fields are unknown and are never inferred from the other value. Spellheart cantrips can use a higher corresponding value from the character’s current spellcasting sources. Other spellheart spells use the explicit item values. This metadata does not change staff or wand casting, spell frequency, or saved item snapshots. Older saved Spellhearts without explicit casting values can use the printed values from their matching, enabled official catalog item. Saved overrides take precedence; the saved description, operations, and item settings remain unchanged. The confirmed historical Wand of the Ash Puppet spell reference can be read for spell indexing without replacing its saved description or initializing charges. This does not add an activation or switch the saved item to a different printing. Wand and staff casts use one current spellcasting source whose tradition lists the spell, or whose normal spell list includes that spell. Multiple eligible sources use the highest spell DC, then attack modifier. Attack and DC stay paired with that source. This calculation does not grant casting permission or change saved items, staff preparation, charges, or spell slots. Characters without a matching source keep the existing manual fallback; Trick Magic Item checks are not automated. Spellheart spell lists read the item’s casting activations. Plain You cast and You [cast](link_action_19611) expose the same spells and printed ranks. Passive spell references, prerequisites, literal code examples and other linked actions do not grant additional cast spells. Comma-separated casting alternatives retain each printed rank. Headerless legacy descriptions retain first-link compatibility. Automatic Bonus Progression’s level-17 Ability Apex increases the selected attribute modifier by 1 or raises it to +4, whichever is higher. This is a full increase even above +4, and preserves any existing half-boost. Ordinary attribute boosts retain their usual half-boost behavior above +4. Fixed attribute boosts inside a selected custom ancestry option constrain free boosts in the same ancestry. Switching options ignores saved free boosts that now conflict. Alternate ancestry boosts replace nested attribute adjustments while retaining size and other non-attribute effects. The legacy Heal Companion feat grants the current Heal Companion focus spell through the Ranger casting source, with Wisdom, trained spell proficiency, and the Spells tab. It is deprecated and hidden from new feat choices. Its original feat ID and grants remain valid for saved characters that already selected it. Class archetype features can be embedded in feature_adjustments instead of stored as separate ability-block rows. The sheet and exports resolve these records using the calculated class feature IDs, including replacement features and either dual class. Conditional choices within selected feats remain visible alongside ordinary grants. This includes dedication skill choices and feat choices in Free Archetype slots. Standalone list expressions such as {{FEAT_NAMES}} display their entries. Use INCLUDES when testing list membership inside an arithmetic expression. Characters with multiple casting sources can choose Cast staff using in the staff’s spell list. Adding charges uses a slot from the selected prepared source; spontaneous casting consumes a slot from the selected spontaneous source. Deleting a character note page applies pending text edits before removing the page. The remaining editors reload their own contents, and deleting the last page leaves one blank Notes page. Editing or removing an imported creature ability affects only the selected row, even when legacy rows share an ID. Character calculation failures stay in background diagnostics. The sheet and builder remain editable, and retain the last successful calculation during the current session. A transient failure gets one automatic retry. Persistent failures wait for changed inputs; raw edits remain buffered locally, and incomplete derived stats never save. Predefined ability, spell, and language choices in the operation editor have an X to remove that choice. Other choices retain their existing IDs and values, including when the removed choice was first in the list. Clearing the only choice leaves an empty picker ready for a replacement. Hazards use the creature table for source ownership and search, but they are not living entities. Creature queries default to type='creature'; hazard queries use type='hazard'. A hazard’s stat block lives in structured details fields for stealth, disable, defenses, activation, routine, and reset. Official hazard corrections can be submitted from the hazard drawer for moderator review as content updates. They use a hazard-specific editor and remain separate from the creature editor. Encounters offer separate Add Creature and Add Hazard selections. A hazard combatant stores type='HAZARD' and a hazard stat-block snapshot alongside optional hazard_state.hp_current and hazard_state.disabled values. Each instance has its own _id, so damage or disabling one instance never edits the catalog or another instance. HP controls appear only when HP is listed. Disabling is manual; reaching zero HP or a Broken Threshold does not automatically resolve a hazard’s special rules. Complex hazards roll their explicitly listed Stealth modifier for initiative; simple hazards do not roll initiative. Existing assigned initiative is skipped unless selected again. Simple hazards contribute one fifth of creature XP at the same level, while complex hazards contribute full XP. Neither counts toward party size or party level. Hazards do not use creature adjustments, condition controls, or character calculations. The character sheet’s Extras tab is unchanged. Hazard prose links each named spell, action, energy trait, and source creature reference. Creature references use the existing read-only stat-block view and drawer history; returning to the hazard preserves the encounter instance. Creature rows can carry optional meta_data.stat_block annotations for source-specific stat lines, including listed senses and skills, trait labels, conditional defense and HP notes, and innate spell frequencies. immunities_note and resistances_note display conditional effects in their stat-block lists without treating them as unconditional mechanics. These annotations only affect display. Embedded attack items can use meta_data.display_traits for trait text that has no matching trait record and meta_data.inventory_label when an inventory label differs from the attack name. The underlying creature operations and abilities still hold the actual mechanics.

Reviewing content submissions

Content update requests show the content type, applicable hidden setting, repeatability for ability blocks, and classification switches for traits. Updates compare the original and submitted settings. Expand Full content data to inspect the complete read-only records, including operations and metadata omitted from the rules preview. The original and updated rules previews remain available alongside these review details. Reviewing a request does not edit or approve it; moderation still happens in Discord.

Loading and saving safely

A content package becomes available only after every requested table and source lookup succeeds. A successful empty table is valid; a failed request remains an error. The sheet and builder offer Retry and do not calculate or save against a partial package. Browser caches follow the signed-in account and resolved source set, and resets discard late requests and hydration from the previous cache generation. Content cache cleanup preserves unsynced character drafts. The character list requires the same signed-in account that requested it. If its authentication session becomes unavailable or changes, the list shows a load error with Retry instead of treating an anonymous response as an empty account. Optional browser-cache work cannot block authoritative content loading. Homebrew invalidation clears the current in-memory generation immediately and queues persistent deletion in order with other storage writes. Reading storage has a 2.5-second deadline; a late read cannot publish rows after fresh downloads start. A recent snapshot gets a separate 750-millisecond source-version check. Confirmed changes reject the snapshot; a slow or unavailable check uses the account-scoped snapshot for this visit. A late verdict never replaces content during calculations. Unverified snapshots keep their original timestamp when persisted, preserving the 24-hour age limit. If browser storage itself remains stalled, fresh content still loads but persistence waits for that storage operation to finish. Catalog artwork loads only when displayed. Calculation workers reuse the package posted for their current job. Catalog selections remain scoped to its enabled sources, and the reader is released after success or failure. A missing required table fails the calculation. Explicit grants can reference content from another book; when that reference is absent from the package, the existing scoped lookup still runs without storing fallback rows in the worker cache. A failed lookup stops calculation rather than dropping the grant. Such uncached dependencies still require a connection. Explicit main-thread calculations retain the normal content store behavior. Trait-name filters also receive a separately cached INFO+PAGE trait catalog, preserving the endpoint’s first-by-ID choice when names occur in more than one book. This optional metadata starts loading with the PAGE package and gets at most 750 milliseconds of extra wait after the required tables finish. Failure or a late result cannot block an unrelated character or mutate a posted package; an actual missing lookup uses the existing scoped request. Spell panels use the same complete page package, so an early render cannot cache an empty catalog while a character is being restored. The builder waits for the current character’s authoritative save context before exposing editable controls. A cached route row cannot make the form ready early. A failed load leaves the character route available for retry. Save status distinguishes server confirmation from a locally retained edit; reconnect and retry use one serialized save queue. Each browser page keeps its own recovery copy, so concurrent tabs cannot silently overwrite another tab’s local draft. Open character pages check for remote edits every five seconds while visible and on reconnect or focus. Incoming edits advance the saved baseline before changing the editor, so polling cannot echo the same character back through autosave. A read started before a newer save, account change or route change cannot replace the current state. Failed reads leave the loaded sheet available. Remote edits use the same merge and conflict handling as save recovery. Incoming conditions and selections invalidate older calculations. If the reconciled character needs a different source set, the page loads that exact package before calculating or saving. This includes unsynced source choices restored from a draft; repeatedly loading the server’s older sources would otherwise create a reload loop. Concurrent character edits merge at nested fields and stable entry IDs. Independent changes survive. When both writers change the same value, saving pauses quietly. Background reads continue, and ordinary edits recheck the latest competing version. Saving resumes automatically once the versions merge safely. While a conflict remains, the editor retains its local input and original base instead of treating unrelated remote fields as local changes. Both survive navigation and reload when draft storage is available; the competing server version is not overwritten. Recovery never displays toasts, reload prompts, or conflict-choice buttons. Session expiry, revoked write access, rejected saves, and unavailable draft storage also remain quiet. Pending input is retained in the editor; available local storage preserves its account-scoped draft. Calculation completion applies every guarded state update; it does not debounce several updates into only the last one. Health normalization reads the latest HP when applying the result, so a slow calculation cannot replace newer damage or healing with an older clamped value. Input debouncing remains in place before running operations. HP initialization clears its reset flag even if the numeric HP already equals the maximum, so a later condition recalculation cannot heal newly received damage. Explicit condition edits apply their HP consequences in the same saved update on the sheet, companions, and campaign encounter controls. Increasing Drained loses current HP equal to the increase times the character’s level (minimum level 1), as well as reducing maximum HP. Decreasing or removing it does not restore current HP. Loading an existing Drained condition, recalculating, retrying a save, or receiving a remote update never deducts HP again. This HP loss is not damage and does not add Dying. Removing Dying increases Wounded once. Stabilizing at zero HP leaves the character Unconscious; subsequently healing that stable character does not increase Wounded again. Normalization and loading do not create these combat events. Drained with its HP and level, and Dying/Wounded with their HP transition, are coupled edits. A concurrent change to these values pauses saving even if two independent actions happened to produce the same HP number. The local recovery copy preserves the complete local transition for review. Identical accepted snapshots from a lost save acknowledgement reconcile without replaying the action. Condition dialogs apply their edit to the current entity when selected, including remote HP received while the dialog was open. Companion and GM controls preserve that current state through the same update, including queued unsynced GM damage.

Public vs. homebrew

find-* endpoints implicitly filter to official, published sources unless you pass content_sources (or query by a specific id). This is the rule baked into fetchData(). Without it, a search would surface every random homebrew row in the database. Every entity that lives in a content source carries a generated uuid (a hash of name + type + level/rank + content_source_id) so duplicate entries within the same source are caught at insert time.

Character JSON exports

Version 4 JSON exports retain the saved character in character and its calculated data in content. content.companions contains each companion’s calculated data in the same format, including attributes, proficiencies, maximum HP, AC, spells, slots, named languages and senses, defenses, speeds, abilities, and traits. Each entry has index, id, name, and content. Its index points to character.companions.list[index], including when two companions share a catalog ID. Companions use the character’s enabled sources and current calculated owner bindings. The saved character and companion records stay unchanged; import still reads character.

Operations

Speed assignments establish a base speed; when several sources grant one, the highest base applies. Speed adjustments add to or subtract from that base, regardless of which source runs first. For example, a Human’s 25-foot base speed and a custom +5 adjustment produce 30 feet in both the builder and character sheet. Removing a grant recalculates the remaining base and adjustments. Mode keys retain numbers: Cursebound 2 and Cursebound 3 toggle independently, and ACTIVE_MODES conditions distinguish them. Existing shared keys from numbered modes retain the effects that were active before the update until those modes are toggled individually. Companion bindValue operations keep their saved source store and variable visible when reopened, including custom destination values created only in the companion. Companion conditions can read bindings from the already calculated parent character. Binding to the character’s FEAT_NAMES lets companion conditionals inspect those feats. Known spells use the saved ranks for their own casting source. A spell learned from two sources appears once per rank in each source; distinct heightened versions remain available within that source. The operations column on each content row is what makes WG content actually do something to a character. See Operations for the full catalog of types and their JSON shapes. When you POST a content row to a create-* endpoint, you’re sending operations in raw JSON. The API treats them as opaque (the OpenAPI schema uses additionalProperties: true); validation happens at character-build time on the frontend.

Eidolon attacks and equipment

Open a companion’s drawer and choose Configure attacks to reach its Inventory, where you can add and configure its natural attacks. Give those attacks the Unarmed Attack category so the sheet treats them as unarmed strikes. For a summoner or a character with an eidolon, Inventory offers Invest on magic weapons. Invest and equip one weapon to share its potency and striking runes with the eidolon’s unarmed attacks. A weapon with its own Invested trait keeps its ordinary investment; use Share runes to choose it as the eidolon’s source. Changing the shared weapon preserves those ordinary investments. A weapon inside a container is stowed and does not confer its runes. Invested Handwraps of Mighty Blows (including Dragon Handwraps) provide the fallback when no selected invested weapon is held. The optional inventory.eidolon_weapon_id records the shared weapon’s inventory-entry ID. Divesting or deleting that entry clears the selection. These bonuses are calculated from the current inventory without rewriting the companion’s saved attack. This automation covers fundamental weapon potency and striking runes. Property rune compatibility and other equipment benefits still require manual configuration.

Linking content from prose

Content references are Markdown links with a drawer target:
The target uses the actual numeric content id, not its uuid. Ability-block links use the appropriate subtype, such as action, feat, or class-feature, even though those records share the ability_block table. Generate links with convertToHardcodedLink(type, name, displayText?). Do not type or guess target IDs. Resolve the intended official printing first, using source-scoped content lookups rather than homebrew or playtest name matches. When names collide, verify the target’s type, source, level or rank, and rules. For example, these calls produce the separate references in a magical weapon’s opening phrase. Rune records use the item link type:
The helper reads the current cache and falls back to plain text if a target is missing. That fallback keeps prose readable, but it is not a successful indexing check. Load the intended records and assert that every required reference resolves. New records without allocated IDs should retain explicit unresolved bindings in private staging. Do not publish placeholder IDs or borrow another printing’s ID. Link every relevant occurrence, not only the first mention. This includes descriptions, heightened entries, and prose in requirements, targets, triggers, costs, frequency, access, and special clauses. A source website’s existing links are a starting point, not a complete WG indexing checklist. Disambiguate by context. A named spell reference is not a same-named trait, and an ordinary action verb is not necessarily a named rules action. Do not run a global word replacement over all fields. Condition links are added after Markdown parsing, leaving authored links and code untouched. Persistent damage also auto-links across emphasis and linked damage-type lists. The condition and traits stay separately clickable without nested links or changed wording. The condition blacklist also applies to persistent damage.

Preserve formatting and field meaning

  • Items, traits, and class-feature references use lowercase prose labels. Spell references use lowercase italic labels. Feats and named actions use Title Case. Preserve the source’s emphasis for magic items and self-references.
  • Keep existing valid links, code spans, and code blocks intact. Do not re-link words inside an authored link or convert literal examples into game references.
  • Preserve the book’s divider between item stat lines and description text. Do not add dividers absent from the source or leave empty trailing dividers.
  • Preserve Markdown hard line breaks. Consecutive stat lines need two trailing spaces when a line break, rather than a new paragraph, is intended. A bare newline may join those lines in the renderer.
  • Add action-symbol markup only for an explicit printed action cost, not each prose mention of Interact, Strike, or another action. For example, <abbr cost="ONE-ACTION" class="action-symbol">1</abbr> represents a printed one-action cost; the named action remains a separate content link.
  • Keep activation labels, requirements, and frequency distinct. Do not move a requirement into frequency merely because another website labels it differently.
  • Do not add new headings, bullets, citation notes, or explanatory wording to printed rules merely to describe a repair. Use clean punctuation in new prose.
  • Prerequisite arrays, trait ID arrays, operations, and selection filters are structured engine inputs, not Markdown fields. Do not insert prose links into them during a formatting pass.
Linking fire damage does not grant the Fire trait to the item. Likewise, linking a spell does not grant that spell mechanically. Staff and Spellheart casting must follow the printed activation and the existing item-casting rules, not every spell mentioned in passive description text.

Source verification and book completion

Compare each record’s complete rules and stat fields with the correct final printing, not just its name. Preserve verified book, page, and Archives of Nethys URL metadata under meta_data.source.book, meta_data.source.page, and meta_data.source.url. Store the exact entry URL, not a search page or an unrelated similarly named entry. Citation metadata is separate from player-facing rules text; a formatting-only edit preserves it. When sources disagree, record the exact discrepancy privately and compare the official rules or errata and other curated implementations. Do not silently invent omitted values, copy generated tables without checking their rules, or assume every Foundry record reflects the latest printing. A book inventory is a finite checklist, not a correctness certificate. Separate printed entries from WG scaffolding, generated grade variants, navigation pages, and unsupported rule systems. Existing source-owned records and dependencies in other books still need verification. A matching name or a successful schema parse does not prove the content is complete or mechanically connected.

Verification checklist

  1. Reconcile every applicable printed entry, its fields, reprints, and dependencies. Keep source discrepancies and unsupported mechanics explicit.
  2. Check every prose field for all relevant reference occurrences and verify each target’s actual identity. Preserve operations and structured prerequisites when the change is formatting-only.
  3. Validate complete proposed rows with the app’s schemas. See Validating content for the existing commands.
  4. Test the actual character or encounter controller for mechanical changes, including removal, reload, source choices, stacking, and unchanged saved input. Do not model temporary activation effects as unconditional passive bonuses.
  5. Inspect the actual drawer and casting view on desktop and mobile. Check headers as well as the description: valid Markdown in storage is not enough if the consuming field renders plain text. Count repeated links, open their targets, return through drawer history, and check condition links and line breaks.
  6. Before a write, check pending curator submissions and protect the exact current values from concurrent changes. Afterward, read back the complete saved row, validate it, and verify the intended rendered result. Preserve existing saved characters and playtest identities.
  7. Report imported content, privately staged content, verified behavior, and unresolved work separately. Keep private audit reports and unpublished records out of Git. Do not call an incomplete audit a completed book.

See also

Homebrew editor and ancestry behavior

Reopening a cached content editor or creature preview initializes its saved fields again. Editing or deleting a creature’s base ability targets that entry’s position, including older creatures whose base abilities share the placeholder ID -1. An ancestry can grant another ancestry trait with the same name as its base ancestry. Both trait IDs remain available when filtering ancestry feats and heritages. Copied class features recognize feats carrying a class trait even when the homebrew class uses a different trait, so the class feat picker retains its archetype and dedication tabs. Numeric conditions use a variable’s calculated value, including typed bonuses from active modes. This lets a mode raise a counter such as CURSEBOUND and have dependent conditions match the value shown by inline expressions. Companion calculations refresh when their owner’s calculated variables change.