Skip to content

token.setProperty()

Write a property on a token, resolving the name the same way getProperty() does.

Part of the Action API · Function reference

Available on token, and — with caveats — on target and targets.

Signature

token.setProperty(propertyName: string, value: any): void

Synchronous, returns nothing.

Parameters

Parameter Type Description
propertyName string Name, alias, or id. Case-insensitive, resolved as in getProperty().
value any The new value. Any JSON-serializable value.

Returns

Nothing. There is no success or failure signal — read the value back if you need to be sure.

Which key gets written

The point of setProperty() is that it writes the key the property already uses, rather than inventing a parallel one:

  • If the token already stores the property under any name, alias, id, or differently-cased spelling, that existing key is updated.
  • If the property is defined by the system or campaign but not yet stored on this token, it is created under the canonical name from the definition.
  • If nothing is known about the name at all, it is created exactly as you spelled it.

That last case is how you end up with hitpoints sitting next to hp — if a property matters, define it in campaign settings first.

This is how you write to a token

Direct assignment does not work:

token.name = 'Bob';       // ignored — core fields are protected
token.setProperty('hp', 12);  // this is the way

Assigning to a core field (name, id, position, and the rest) is silently refused. Only setProperty() and the writable metadata bag change anything.

Persistence

A write through setProperty() on the acting token is saved to the server after the action finishes. You do not save anything yourself.

Writes to a target are a different story — see the warning below.

The combat log

Every change is recorded in the combat log as a property change, with the old and new values. A write of the value it already had is skipped, so no-op writes do not clutter the log.

Hit-point changes made by applyDamage() do not pass through here — they are logged as damage instead, so nothing is double-counted.

Examples

Spend a resource

const slots = token.getProperty('spellSlots') ?? 0;
if (slots <= 0) return `${token.name} has no spell slots left.`;
token.setProperty('spellSlots', slots - 1);

Toggle a flag

token.setProperty('concentrating', true);

Track something structured

const conditions = token.getProperty('conditions') ?? [];
token.setProperty('conditions', [...conditions, 'prone']);

Heal without an approval card

// Direct write — no GM approval, no damage modifiers, no card.
// Use applyDamage(-n) if you want the usual healing flow instead.
const hp = token.getProperty('hp') ?? 0;
const max = token.getProperty('hp_max') ?? hp;
token.setProperty('hp', Math.min(hp + 5, max));

Notes and gotchas

  • Writing to a target is possible, and probably a mistake. target blocks direct field assignment but does not block setProperty() — the write lands on the local copy and is not saved, so it disappears the next time that token refreshes. Treat targets as read-only and use applyDamage(), which is the sanctioned way to change someone else's token.
  • No validation. Writing 'twelve' into a numeric property stores the string, and later roll() expressions using that property will quietly drop the term.
  • The Test panel writes for real in memory but does not persist, so a value you set while testing reverts.
  • Nothing is announced. setProperty() sends no chat message; combine it with sendMessage() if the table should know.

See also