Skip to content

Write many role fields in one request

POST
/open/v1/role/{roleId}/document
curl --request POST \
--url https://api.harperharbor.com/open/v1/role/example/document \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "idempotencyKey": "example", "fields": { "roleName": "example", "language": "zh-Hant", "roleDesc": "example", "roleReadme": "example", "roleTag": [ "example" ], "userName": "example", "roleType": "example", "roleSex": "man", "roleAvatar": "https://example.com", "roleBackground": "https://example.com", "roleBackgroundLandscape": "https://example.com", "roleShareImage": "example", "roleDetailDesc": "example", "roleWelcome": "example", "talkExample": [ { "roleType": "ai", "content": "example" } ], "roleOutputContract": "example", "customInstructions": "example", "nickname": "example", "cardMeta": { "creator": "example", "characterVersion": "example", "source": [ "example" ], "creatorNotesMultilingual": { "additionalProperty": "example" }, "creationDate": 1, "modificationDate": 1 }, "responseDefaults": { "agency": "protect", "style": "default", "perspective": "card", "length": "auto", "lengthTarget": "200", "pace": "natural", "customStyle": "example", "agencyNote": "example", "styleNote": "example", "perspectiveNote": "example", "lengthNote": "example", "paceNote": "example" } }, "fieldPatches": { "roleDesc": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] }, "roleDetailDesc": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] }, "roleWelcome": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] }, "roleOutputContract": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] }, "customInstructions": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] } } }'

Applies a partial document to an owned private role in a single write. Every key under fields is optional; absent keys are untouched, present keys overwrite (an empty string clears the field where allowed). fieldPatches carries anchored text edits (operations) applied on top of the current value of the corresponding field and is the way to edit long text without resending it. At least one key under fields must be present (nothing to update otherwise); if nothing is effectively changed (e.g. only a blank roleName), the service answers invalid_arguments. Rules: roleName blank is ignored; roleAvatar/roleBackground blank is ignored, otherwise must be an http(s) URL (invalid_asset_url); roleSex accepts man|women|other and aliases (male, female, woman, 男, 女, 其他; empty clears; else invalid_role_sex); talkExample[].roleType accepts ai|user (assistant maps to ai) and content must be non-blank (invalid_talk_example); roleTag replaces the whole tag list (each non-blank string becomes a platform tag {icon:"", text, type:2}); customInstructions is writable here but is never returned by v1 read routes. Character limits depend on the card language (see GET /role/validate → tokenBudget.limits): roleDesc 500 (English 2500), roleReadme 20000 (English 40000), roleDetailDesc 10000 (English 50000), roleWelcome 8000 (English 10000), customInstructions 500 for zh-Hant/zh-Hans, 1500 otherwise, roleOutputContract 2000. Violations return <field>_too_long codes (role_desc_too_long, role_readme_too_long, role_detail_too_long, role_welcome_too_long, custom_instructions_too_long, role_output_contract_too_long), which the error mapper surfaces as HTTP 500 with retryable: true. The role must be private (public_role_requires_clone). Alternates/prologue greetings are not part of this document — use PATCH /role/{roleId}/welcome.

roleId
required
string

Role id. Only the path parameter is honoured; a roleId inside the body is ignored.

Media typeapplication/json
object
idempotencyKey

Accepted; not used for deduplication.

string
fields
required

Partial document. Absent keys are untouched.

object
roleName

Name; blank ignored.

string
language

Card language. Only zh-Hant and zh-Hans can be swapped, and only on a card whose language is one of them: the original text moves to the new script, which the play page converts from. Other values, or any value on a card in another language, return invalid_arguments.

string
Allowed values: zh-Hant zh-Hans
roleDesc

Intro; limit 500 chars (English 2500).

string
roleReadme

Detail introduction in Markdown for the card’s public page (the summary stays in roleDesc). Presentation only: never sent to the model during chat. Empty string clears it. Limit 20000 chars (English 40000); over the limit returns role_readme_too_long.

string
roleTag

Replacement tag list; blank strings dropped.

Array<string>
userName

Player name used by the card.

string
roleType

Card type; stored as-is (not validated). Understood values: companion, story, game, generator.

string
roleSex

Stored value; aliases male, female, woman, 男, 女, 其他 are mapped; empty clears.

string
Allowed values: man women other ""
roleAvatar

Http(s) URL; blank ignored. HarperHarbor: compatibility alias for the portrait background of the agent configuration; no independent avatar is stored. If roleBackground is also supplied, it takes precedence, including an empty string. An avatar-only write updates the portrait.

string format: uri
roleBackground

Http(s) URL; blank ignored.

string format: uri
roleBackgroundLandscape

Optional landscape (16:9) background: http(s) URL; blank ignored. roleBackground (portrait) stays the baseline.

string format: uri
roleShareImage

Optional share image (1.91:1, 1200×630) for link previews (og:image): http(s) URL; empty string clears it.

string
roleDetailDesc

Private definition; limit 10000 chars (English 50000).

string
roleWelcome

Opening message; limit 8000 chars (English 10000).

string
talkExample

Replacement example dialogue.

Array<object>
object
roleType
required

Speaker; assistant is accepted and stored as ai.

string
Allowed values: ai user
content
required

Line text; trimmed; must be non-blank.

string
roleOutputContract

Output format contract; trimmed; limit 2000 chars.

string
<= 2000 characters
customInstructions

Custom instructions that replace the corresponding default content-scope segment in prompt construction, not the entire system prompt. Existing authorization, validation and moderation rules still apply. An empty value clears the override.

string
nickname

What {{char}} expands to in prompts when it should differ from roleName. Empty string means roleName is used. Absent leaves it untouched; empty string clears it (nickname_too_long above 70 characters).

string
<= 70 characters
cardMeta

Replaces the whole object; send {} to clear. Absent leaves it untouched.

object
creator

Original author credit.

string
characterVersion

Card version string as written by the author.

string
source

Source URLs or identifiers.

Array<string>
creatorNotesMultilingual

Language code → creator notes in that language.

object
key
additional properties
string
creationDate

Unix seconds.

integer
modificationDate

Unix seconds.

integer
responseDefaults
One of:

Author defaults for response preferences. Same keys and values as the player’s overrides, every value a string (lengthTarget as a decimal string). They apply only where the player has not chosen otherwise: when the player picks a different option for an axis, the author’s note, custom style and length target for that axis no longer apply; picking the author’s own option keeps them. Legacy values (plain, dialogue, descriptive, brief, balanced, detailed) are rejected. lengthTarget is required exactly when length is target, customStyle exactly when style is custom.

object
agency
string
Allowed values: protect coauthor
style
string
Allowed values: default vivid guided card custom
perspective
string
Allowed values: card first_character second_user third_limited third_omniscient
length
string
Allowed values: auto recommended target
lengthTarget
string
Allowed values: 200 300 400 500 600 800 1000 1200 1500 2000 2500 3000 4000 5000 7000 10000
pace
string
Allowed values: natural linger advance
customStyle
string
>= 1 characters <= 1000 characters
agencyNote
string
>= 1 characters <= 200 characters
styleNote
string
>= 1 characters <= 200 characters
perspectiveNote
string
>= 1 characters <= 200 characters
lengthNote
string
>= 1 characters <= 200 characters
paceNote
string
>= 1 characters <= 200 characters
fieldPatches

Anchored edits applied after fields, each on the field of the same name.

object
roleDesc

Anchored edit applied to the current value of a text field.

object
baseSha256

Optional hex SHA-256 of the text the patch was computed against; the patch is rejected (invalid_field_patch) when it does not match the stored text.

string
operations
required

Operations applied in order.

Array<object>

One anchored text operation. Anchors (oldText, beforeText, afterText) must be found exactly once unless replaceAll is set; otherwise invalid_field_patch (anchor_not_found / anchor_not_unique / invalid_operation).

object
op
required

Operation kind. replaceText: replace oldText with newText; deleteText: remove oldText; insertText: insert text before beforeText or after afterText (exactly one anchor); appendText/prependText: add text at the end/start. oldText/text must be non-empty. Anchors must occur exactly once unless replaceAll is set (replace/delete only).

string
Allowed values: replaceText deleteText insertText appendText prependText
oldText

Text to find (replace operations).

string
newText

Replacement text.

string
text

Text to insert (insert operations).

string
beforeText

Anchor: insert before this text.

string
afterText

Anchor: insert after this text.

string
replaceAll

Replace every occurrence instead of requiring a unique anchor.

boolean
roleDetailDesc

Anchored edit applied to the current value of a text field.

object
baseSha256

Optional hex SHA-256 of the text the patch was computed against; the patch is rejected (invalid_field_patch) when it does not match the stored text.

string
operations
required

Operations applied in order.

Array<object>

One anchored text operation. Anchors (oldText, beforeText, afterText) must be found exactly once unless replaceAll is set; otherwise invalid_field_patch (anchor_not_found / anchor_not_unique / invalid_operation).

object
op
required

Operation kind. replaceText: replace oldText with newText; deleteText: remove oldText; insertText: insert text before beforeText or after afterText (exactly one anchor); appendText/prependText: add text at the end/start. oldText/text must be non-empty. Anchors must occur exactly once unless replaceAll is set (replace/delete only).

string
Allowed values: replaceText deleteText insertText appendText prependText
oldText

Text to find (replace operations).

string
newText

Replacement text.

string
text

Text to insert (insert operations).

string
beforeText

Anchor: insert before this text.

string
afterText

Anchor: insert after this text.

string
replaceAll

Replace every occurrence instead of requiring a unique anchor.

boolean
roleWelcome

Anchored edit applied to the current value of a text field.

object
baseSha256

Optional hex SHA-256 of the text the patch was computed against; the patch is rejected (invalid_field_patch) when it does not match the stored text.

string
operations
required

Operations applied in order.

Array<object>

One anchored text operation. Anchors (oldText, beforeText, afterText) must be found exactly once unless replaceAll is set; otherwise invalid_field_patch (anchor_not_found / anchor_not_unique / invalid_operation).

object
op
required

Operation kind. replaceText: replace oldText with newText; deleteText: remove oldText; insertText: insert text before beforeText or after afterText (exactly one anchor); appendText/prependText: add text at the end/start. oldText/text must be non-empty. Anchors must occur exactly once unless replaceAll is set (replace/delete only).

string
Allowed values: replaceText deleteText insertText appendText prependText
oldText

Text to find (replace operations).

string
newText

Replacement text.

string
text

Text to insert (insert operations).

string
beforeText

Anchor: insert before this text.

string
afterText

Anchor: insert after this text.

string
replaceAll

Replace every occurrence instead of requiring a unique anchor.

boolean
roleOutputContract

Anchored edit applied to the current value of a text field.

object
baseSha256

Optional hex SHA-256 of the text the patch was computed against; the patch is rejected (invalid_field_patch) when it does not match the stored text.

string
operations
required

Operations applied in order.

Array<object>

One anchored text operation. Anchors (oldText, beforeText, afterText) must be found exactly once unless replaceAll is set; otherwise invalid_field_patch (anchor_not_found / anchor_not_unique / invalid_operation).

object
op
required

Operation kind. replaceText: replace oldText with newText; deleteText: remove oldText; insertText: insert text before beforeText or after afterText (exactly one anchor); appendText/prependText: add text at the end/start. oldText/text must be non-empty. Anchors must occur exactly once unless replaceAll is set (replace/delete only).

string
Allowed values: replaceText deleteText insertText appendText prependText
oldText

Text to find (replace operations).

string
newText

Replacement text.

string
text

Text to insert (insert operations).

string
beforeText

Anchor: insert before this text.

string
afterText

Anchor: insert after this text.

string
replaceAll

Replace every occurrence instead of requiring a unique anchor.

boolean
customInstructions

Custom instructions that replace the corresponding default content-scope segment in prompt construction, not the entire system prompt. Existing authorization, validation and moderation rules still apply. An empty value clears the override.

object
baseSha256

Optional hex SHA-256 of the text the patch was computed against; the patch is rejected (invalid_field_patch) when it does not match the stored text.

string
operations
required

Operations applied in order.

Array<object>

One anchored text operation. Anchors (oldText, beforeText, afterText) must be found exactly once unless replaceAll is set; otherwise invalid_field_patch (anchor_not_found / anchor_not_unique / invalid_operation).

object
op
required

Operation kind. replaceText: replace oldText with newText; deleteText: remove oldText; insertText: insert text before beforeText or after afterText (exactly one anchor); appendText/prependText: add text at the end/start. oldText/text must be non-empty. Anchors must occur exactly once unless replaceAll is set (replace/delete only).

string
Allowed values: replaceText deleteText insertText appendText prependText
oldText

Text to find (replace operations).

string
newText

Replacement text.

string
text

Text to insert (insert operations).

string
beforeText

Anchor: insert before this text.

string
afterText

Anchor: insert after this text.

string
replaceAll

Replace every occurrence instead of requiring a unique anchor.

boolean

Role summary plus the list of patched fields.

Media typeapplication/json
object
role
required

Summary returned by every role write.

object
roleId
required

Role id.

string
roleVisibility
required

Current visibility. HarperHarbor defaults to unlisted (usable by ID without community listing).

string
Allowed values: private public waitReview unlisted
reviewStatus
required

Moderation status.

string
Allowed values: not_submitted pending passed rejected
accountPermission
required

Always owner on these routes.

string
Allowed values: owner
patch

Present when a deep patch was applied to a single field.

object
patchedOperations
required

Number of operations applied.

integer
charsBefore
required

Rune count before.

integer
charsAfter
required

Rune count after.

integer
sha256Before
required

Hex SHA-256 of the text before.

string
sha256After
required

Hex SHA-256 of the text after.

string
textPatches

Per-field patch summaries keyed by field name; present when fieldPatches were used.

object
key
additional properties
object
patchedOperations
required

Number of operations applied.

integer
charsBefore
required

Rune count before.

integer
charsAfter
required

Rune count after.

integer
sha256Before
required

Hex SHA-256 of the text before.

string
sha256After
required

Hex SHA-256 of the text after.

string
fieldChars

Character counts before/after for the edited field (e.g. roleDetailDesc, roleWelcome).

object
key
additional properties
object
before
required

Character (rune) count before the write.

integer
after
required

Character (rune) count after the write.

integer
nextRecommendedTools
required

Suggested next MCP tool names; informational.

Array<string>
document
required
object
documentVersion
required

Document format version. This names the format, not the service that produced it. Some providers still return a vendor-prefixed legacy value for this format; compare the suffix or treat the string as opaque.

string
Allowed values: rolePatch.v1
patchedFields
required

Field names that were written.

Array<string>
textPatches

Summaries of applied fieldPatches.

object
key
additional properties
object
patchedOperations
required

Number of operations applied.

integer
charsBefore
required

Rune count before.

integer
charsAfter
required

Rune count after.

integer
sha256Before
required

Hex SHA-256 of the text before.

string
sha256After
required

Hex SHA-256 of the text after.

string
nextRecommendedTool
required

Always validate_role.

string
Example
{
"role": {
"roleVisibility": "private",
"reviewStatus": "not_submitted",
"accountPermission": "owner"
},
"document": {
"documentVersion": "rolePatch.v1"
}
}

invalid JSON body, nothing to update (body {"error": "..."}), or invalid_arguments (nothing effectively patched; {"error","retryable":false}).

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
}

permission_denied.

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
}

record not found, public_role_requires_clone, *_too_long, invalid_asset_url, invalid_role_sex, invalid_talk_example, invalid_field_patch (anchor not found/not unique/invalid op), or another unclassified error; retryable: true.

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
}