Policy
The policy is one JSON document that tells Plots and Memory how to behave. It also decides how many model calls your events cause, and so what they cost. This page lists every setting an account may change, its default, and what it does to your bill.
What the policy is
The policy has three sections: shared for rules every module obeys, memory for Memory, and plots for Plots. Every member has a default. A member you leave out takes its default, and a policy without a section runs that section on defaults. You never need to send a policy to start.
- An account has one policy on the hosted Director: the last one you stored. All your API keys, sign-in tokens, games, and projects share it.
- The policy is read when an event is processed or a decision is made, so a new policy applies from the next event or decision.
- Storing, reading, and changing the policy needs no module switch, costs nothing, and does not count toward the request limit.
Store and read the policy
PUT /v2/policy stores the policy. GET /v2/policy reads the policy Director runs.
curl -X PUT https://director.eposyne.com/v2/policy \
-H "Authorization: Bearer $EPOSYNE_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @policy.jsonSend the whole document. It replaces the stored one. The body is at most 65,536 bytes. Director checks every section first and stores nothing unless all of it is valid. A valid policy answers 200 with the policy state:
{
"source": "pushed",
"hash": "9b2c0f5c1c4e0d3a7f1e6a8b5d2c4f60a1b3c5d7e9f0a2b4c6d8e0f1a3b5c7d9",
"stored_at": "2026-10-13T18:00:00.000Z",
"policy": {
"memory": {"weighing": "model"}
}
}curl https://director.eposyne.com/v2/policy \
-H "Authorization: Bearer $EPOSYNE_API_KEY"sourceispushedwhen you stored a policy, anddefaultwhen you did not. Withdefault,policyis{}and every default applies.policyis the document as you stored it. Defaults are not filled in.hashis the SHA-256 of the document in canonical form: keys sorted, no extra whitespace, UTF-8. Compare it with the hash of your own file to see whether Director holds the same policy.stored_atis the time you stored it, andnullfordefault.
When the policy is refused
An invalid policy answers 422 with "code": "policy_invalid" and one entry in errors for each problem. The policy you had before stays in force.
{
"detail": "invalid policy",
"code": "policy_invalid",
"errors": [
{"path": "extra", "message": "unknown section"},
{"path": "memory.weighing", "message": "Input should be 'rules' or 'model'"},
{"path": "plots.max_active_arcs", "message": "must be an integer from 0 to 1000"}
]
}The rules are strict. A section or member that the policy does not define is refused. A value must have exactly the right type: "3" is not an integer, and 2.5 is not an integer either. A list of ids may not repeat an id. A body that is not JSON, or is over 65,536 bytes, is refused with the same code and the path document. Match on code and read path. The wording of message can change.
The defaults
This is the policy Director runs when you store none. Every member shown is a setting you may change.
{
"shared": {
"invention": {"allowed": false, "violation": "record_only", "max_regenerations": 2},
"judgement_model": null
},
"memory": {
"world": true,
"npc": true,
"weighing": "rules",
"perception_location_fallback": true,
"spreading": {
"enabled": false,
"meetings": ["movement", "reported"],
"group_field": "group",
"group_interval_game_hours": 24,
"group_exchange_limit": 20
},
"referee": true,
"lies_believable": true,
"persistent": {"characters": [], "kinds": []}
},
"plots": {
"director_may_start_arcs": true,
"sources": ["authored", "catalogue", "free"],
"permitted_templates": null,
"max_active_arcs": 3,
"max_emergent_share": null,
"excluded_locations": [],
"excluded_moments": [],
"eligible_npcs": null,
"fill_scope": "empty_fields",
"completion_field": null,
"may_override_guards": false,
"relevance": {
"role_in_current_beat": "full",
"role_in_arc": "summary",
"current_beat_at_location": "summary",
"other_arcs": "omit"
},
"steering": "subtle"
}
}How the policy multiplies model calls
Every module judgement is reserved and charged from your usage credits like a decision. See Billing and limits. The policy decides how many judgements happen. Count them like this.
Plots, for every event
- One fitting call for every player an event names (its
player_id, or each of itsplayers), while Plots is on. Plots asks whether the event belongs to a running arc, completes its current beat, or could start a new story. No policy setting turns this call off. Only switching Plots off in Architect, or not sending events that name a player, does. - If the answer is a new story and the framework allows it: one gate call, then if the gate allows it one choosing call that picks an arc and binds its roles, then one fill-in call when the first beat has empty fields.
- If the answer is that the current beat is complete: one call for each fork that needs a judgement (a branch whose condition is plain text or empty), and one fill-in call for the next beat when it has empty fields.
- A fill-in that invents characters, places, or items against the invention rule is made again, as the table below says.
Memory, for every event
- With
weighing: "rules"(the default), Memory makes no model call when it processes events. Spreading in rules mode keeps the gist of a memory and makes no call either. - With
weighing: "model", Memory makes one weighing call for each memory it forms: one for every character who perceives an event (the target, the actor, the witnesses), and one for the listener of a retelling. An event seen by 4 characters costs 4 calls. - With
weighing: "model"and spreading on, Memory makes one rumour call for each memory a character passes on. At a meeting each of the two characters passes on at most 3 memories, so a meeting costs at most 6 calls. A group exchange costs at mostgroup_exchange_limitcalls per group and interval.
In a decision
- A decision at a stream position makes one referee call when the player's line touches something the deciding character believes that is uncertain or contradicts the world record. This happens whatever
weighingsays, as long asreferee,world, andnpcare on. - With Memory on and
weighing: "model", every decision gets one invention check call on its reply, with or without a stream position. - A reply that invents a character, place, or item is, under
violation: "block", written again, up tomax_regenerationstimes. Each regeneration is a full decision call, billed like the decision itself, plus a new check whenweighingismodel.
The settings
shared
| Setting | Default | What it does | Effect on model calls and cost |
|---|---|---|---|
invention.allowed | false | Whether Plots may add characters, places, or items that appear in no arc, in the world record, or in memory. An allowed invention becomes a note in the world record. Core, the part that writes decisions, never invents, whatever this says. | true stops the regenerations of a Plots fill-in that invents. false with block makes Plots fill-ins repeat. |
invention.violation | "record_only" | What happens to a reply that invents when invention is not allowed. record_only delivers the reply and Memory records only that the character said it. block withholds the reply and writes it again. | record_only adds no calls. block adds up to max_regenerations decision-sized calls for each reply that invents. |
invention.max_regenerations | 2 (1 to 5) | Under block, how many times a reply is written again before it is delivered under record_only. | The most extra decision-sized calls for one decision. At 5, one decision can cost six times its normal price in the worst case. |
judgement_model | null | The model for judgements, as {"provider": "...", "model": "..."}, a pair from your model list. null uses the decision's model inside a decision, and your account's default model for events. | Sets the price of every judgement call. Binding a small, cheap model here cuts the cost of every judgement at once. If Director cannot use the model you name, judgements use the default model instead. |
memory
| Setting | Default | What it does | Effect on model calls and cost |
|---|---|---|---|
world | true | The world part: what really happened, kept as the world record. The referee judges a player's claim against it. | Off disables the referee. |
npc | true | The NPC part: what each character perceived, believes, and feels. Off, and Memory gives Core nothing. | Off removes the weighing, rumour, and referee calls. |
weighing | "rules" | How much an event matters to a character: rules computes it, model asks a model. | model is the setting that multiplies calls: one per memory formed, one per rumour passed on, and one invention check per decision. See above. |
perception_location_fallback | true | With no witnesses, the characters known to be at the event's location perceived it. | On means more characters perceive each event, so with model weighing more weighing calls. Off limits perception to the target, the actor, and the witnesses you name. |
spreading.enabled | false | Whether Director spreads memories between characters on its own. Retellings your game reports with told are always processed. | Off adds no rumour calls. On adds them under model weighing. |
spreading.meetings | ["movement", "reported"] | How Director recognizes a meeting: movement from character location events, reported from the characters-met event, group from a shared group field on a game-time interval. | Each source you add adds meetings, and each meeting costs up to 6 rumour calls under model weighing. |
spreading.group_field | "group" | The Character field that names a character's group. An empty value takes part in no group exchange. | No direct effect. |
spreading.group_interval_game_hours | 24 (1 to 8760) | How often a group exchanges memories, in game hours. | A shorter interval means more exchanges, so more rumour calls under model weighing. |
spreading.group_exchange_limit | 20 (0 to 10000) | The most memories exchanged per group per interval. | The cap on rumour calls for a group in one interval. 0 stops group exchange. |
referee | true | Judge a player's claim against the world record when it touches an uncertain or contradicting belief. | On costs at most one call per decision at a player line. Off removes it. |
lies_believable | true | Whether the referee may let a convincing lie be believed. | No effect on cost. |
persistent.characters, persistent.kinds | [], [] | Characters, and kinds of memory (event, knowledge, impression), whose memories are kept for the whole world across timelines. See Memory across timelines. | No effect on cost. |
plots
| Setting | Default | What it does | Effect on model calls and cost |
|---|---|---|---|
director_may_start_arcs | true | Whether Director may start an arc when an event fits no running arc. Off, Plots still tracks and advances arcs that are running. | Off removes the gate, choosing, and fill-in calls for new arcs. The fitting call per event stays. |
sources | ["authored", "catalogue", "free"] | Where a new arc may come from: your library, the template catalogue, or invented by the model. An empty list lets Director start no arc. | An empty list removes the calls for new arcs. Removing free stops invented arcs. |
permitted_templates | null | The catalogue templates a new arc may use, by reference id. null permits the whole catalogue, an empty list none. | No direct effect. |
max_active_arcs | 3 (0 to 1000) | The most arcs a player may have active at once. At the limit, Director starts no new arc. 0 starts none. | A refusal here happens before the gate, so a full player costs no gate, choosing, or fill-in calls. |
max_emergent_share | null | The largest share of a player's active arcs that may be emergent, from 0 to 1. null leaves it open. | A refusal here saves the gate call. |
excluded_locations | [] | Location ids at which no new arc starts. | A refusal here saves the gate call. |
excluded_moments | [] | Event keys during which no new arc starts. A standard event matches under either spelling of its key, loreweaver. or eposyne.. | A refusal here saves the gate call. |
eligible_npcs | null | The characters Plots may bind to an open role. null allows every character, an empty list none. A character you cast in a beat is never affected. | No direct effect. |
fill_scope | "empty_fields" | How far Plots fills in an arc. empty_fields completes only fields that are empty, and never changes a field you wrote. add_beats is accepted and stored, but in this release Plots still completes empty fields only. | Each fill-in is one call, plus any regeneration. |
completion_field | null | The Beat field that says when a beat is complete, tried before the standard field complete_when. | No direct effect. |
may_override_guards | false | Whether Plots may take a branch whose formal guard is false. Off, a formal guard decides alone. | On offers more branches to the model at a fork. |
relevance.role_in_current_beat, relevance.role_in_arc, relevance.current_beat_at_location, relevance.other_arcs | "full", "summary", "summary", "omit" | How much of each running arc reaches the decision for a character: full, summary, or omit. It is always direction, never knowledge the character holds. | Changes the size of the decision prompt, so the cost of every decision at a stream position. full and summary add text, omit adds none. |
steering | "subtle" | How strongly a reply steers away when Director refuses a new arc: subtle or explicit. | No effect on cost. |
A worked example
A tavern scene: 20 events that name the player, 3 characters besides the player who perceive each of them, 5 decisions at a stream position after the player speaks, one new arc started, one meeting between two characters. Count the judgement calls first with the defaults.
| Source | Calls with the defaults |
|---|---|
| Plots, fitting: 20 events | 20 |
| Plots, new arc: gate, choosing, fill-in | 3 |
Memory, events: weighing is rules | 0 |
| Memory, referee: at most once per decision | 5 at most |
| Total judgements | 28 at most, plus the 5 decisions |
Now the same scene with weighing: "model" and spreading.enabled: true:
| Source | Calls with model weighing |
|---|---|
| Plots, fitting and new arc | 23 |
| Memory, weighing: 20 events, 3 characters perceive each | 60 |
| Memory, rumours: one meeting, at most 6 | 6 at most |
| Memory, referee | 5 at most |
| Memory, invention check: 5 decisions | 5 |
| Total judgements | 99 at most, plus the 5 decisions |
The same scene costs more than three times as many judgements. The 60 weighing calls alone pass the limit of 50 judgements for one events request, so if you sent the 20 events in one request, part of the batch would come back as deferred. If you keep model weighing, bind a small model for the judgements and keep decisions on the model you like. Store this policy:
{
"shared": {
"judgement_model": {"provider": "<provider id>", "model": "<model id>"}
},
"memory": {
"weighing": "model",
"spreading": {"enabled": true, "meetings": ["reported"], "group_exchange_limit": 5}
},
"plots": {
"max_active_arcs": 2,
"relevance": {"other_arcs": "omit"}
}
}Choose the pair from your own GET /v2/models list. With this policy, rumours spread only at meetings your game reports, a group exchanges at most 5 memories per interval, and every judgement runs on the small model. Judgements on the stream cost whatever that model's listed price is. They stay in your usage totals, see Billing and limits.