Skip to content

Replace the display rules of an owned role

PUT
/open/v1/role/{roleId}/author-asset
curl --request PUT \
--url https://api.harperharbor.com/open/v1/role/example/author-asset \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "rules": [ { "id": "example", "name": "example", "find": "example", "replace": "example", "enabled": true } ], "mountTrigger": "example", "mountLayer": "under", "pageMode": "classic", "version": 0, "sharedState": { "polls": { "additionalProperty": { "options": [ "example" ], "share": true } }, "counters": { "additionalProperty": { "goal": 1, "maxStep": 1, "perMember": 1, "period": "none" } } } }'

Full replacement of the author asset (there is no partial update). Optimistic locking: version must equal the version last read (0 when no asset exists yet); a mismatch returns 409 version_conflict. mountLayer is required and must be under, over or cover; pageMode may be omitted (treated as classic), immersive or sandbox. Every rule needs a non-blank find; each replace is limited to 131072 bytes and the sum of id+name+find+replace across all rules to 33554432 bytes (validate_reject, with message and detail {reason: emptyFind|ruleReplace|rulesTotal|sharedState, index, name, max, actual, unit: bytes}). Saving sets status to passed immediately and increments version. Rules act on the rendering layer only and never enter the prompt. Exception: sharedState is kept when omitted or null, and {} clears it; an invalid declaration is rejected with reason: sharedState.

roleId
required
string

Role id owned by the caller.

Media typeapplication/json
object
rules

Full rule list (replaces the stored list); null/omitted stores an empty list.

Array<object>
object
id
required
string
name
required
string
find
required
string
replace
required
string
enabled
required
boolean
mountTrigger

Trigger text/pattern that tells the client when to mount the persistent block (free text, stored as is).

string
<= 500 characters
mountLayer
required

Container the persistent block is mounted in (validate_reject when unknown).

string
Allowed values: under over cover
pageMode

immersive asks clients for a full-bleed page without the standard chrome. sandbox hands the whole chat area to the author’s page, rendered in an isolated frame (the new-style chat page); clients that cannot host it must tell the player instead of applying the rules the classic way. Empty/omitted = classic; unknown values are rejected.

string
default: classic
Allowed values: classic immersive sandbox
version

Version last read; omitted/0 for the first save (409 if an asset already exists).

integer format: int64
0
sharedState

Omitted or null keeps the stored declaration; {} clears it.

object
polls

Up to 16 polls by key. options lists 2–12 option IDs; share: true also allows the poll on share links.

object
<= 16 properties
key
additional properties
object
options
required
Array<string>
>= 2 items <= 12 items unique items
share
boolean
counters

Up to 16 counters by key. maxStep is 1–1000 per write, perMember caps each player per period, goal is optional, and period is none, day or week (UTC+8).

object
<= 16 properties
key
additional properties
object
goal
integer
<= 1000000000000
maxStep
required
integer
>= 1 <= 1000
perMember
required
integer
>= 1 <= 1000000000
period
string
Allowed values: none day week

Saved asset (new version, status: passed).

Media typeapplication/json
object
rules
required
Array<object>
object
id
required
string
name
required
string
find
required
string
replace
required
string
enabled
required
boolean
mountTrigger

Absent in the empty shape.

string
mountLayer

Absent in the empty shape.

string
Allowed values: under over cover ""
pageMode
required
string
Allowed values: classic immersive sandbox
status
required

none when no asset exists. Saves currently store passed directly.

string
Allowed values: none pending passed rejected
version
required

Optimistic-lock version; 0 when no asset exists.

integer format: int64
rejectReason

Author-facing rejection reason (empty unless rejected). Absent in the empty shape.

string
variants

Map from each Han character appearing in any rule’s find to a character class containing its Simplified/Traditional variants, for the client to expand before compiling the pattern. Absent in the empty shape.

object
key
additional properties
string
sharedState

Shared state the card declares: polls and counters players can write. Keys and option IDs are 1–64 letters, digits, ., _ or -, starting with a letter or digit; {} declares nothing. An invalid declaration is rejected with validate_reject (reason: sharedState).

object
polls

Up to 16 polls by key. options lists 2–12 option IDs; share: true also allows the poll on share links.

object
<= 16 properties
key
additional properties
object
options
required
Array<string>
>= 2 items <= 12 items unique items
share
boolean
counters

Up to 16 counters by key. maxStep is 1–1000 per write, perMember caps each player per period, goal is optional, and period is none, day or week (UTC+8).

object
<= 16 properties
key
additional properties
object
goal
integer
<= 1000000000000
maxStep
required
integer
>= 1 <= 1000
perMember
required
integer
>= 1 <= 1000000000
period
string
Allowed values: none day week
Example
{
"mountLayer": "under",
"pageMode": "classic",
"status": "none",
"sharedState": {
"counters": {
"additionalProperty": {
"period": "none"
}
}
}
}

invalid_param (bad JSON) or validate_reject (unknown mountLayer/pageMode, empty find, rule or total size over limit). Body {"error": "<code>"}; for empty find and size limits also message (English sentence) and detail {reason, index (0-based, -1 for the whole set), name, max, actual, unit: "bytes"}.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

unauthorized.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

forbidden — not the role owner.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

role_not_found.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

version_conflict — version does not match the stored version (also when an asset already exists and version is 0).

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

generic_error.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}