Set the opening message, alternate openings and prologue options
const url = 'https://api.harperharbor.com/open/v1/role/example/welcome';const options = { method: 'PATCH', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"roleWelcome":"example","alternates":["example"],"prologue":["example"],"idempotencyKey":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PATCH \ --url https://api.harperharbor.com/open/v1/role/example/welcome \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "roleWelcome": "example", "alternates": [ "example" ], "prologue": [ "example" ], "idempotencyKey": "example" }'Writes the opening (roleWelcome, required, non-blank) of an owned private role. alternates (alternate AI openings the player can pick before the first turn) and prologue (player-side first lines offered as choices) are full replacements distinguished by presence: key absent = unchanged, empty array = cleared. Neither list has a fixed item-count limit; blank entries are omitted. Every entry — like roleWelcome itself — is bounded by the welcome limit of the card language (8000 chars; 10000 for English cards; role_welcome_too_long). The role must be private (public_role_requires_clone). Limit and visibility violations are surfaced as HTTP 500 by the error mapper.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Role id.
Request Bodyrequired
Section titled “Request Bodyrequired”object
Opening message; non-blank; limit 8000 chars (English 10000).
Alternate AI openings; full replacement when present; [] clears. Blank entries dropped.
Player-side first-line options; full replacement when present; [] clears.
Accepted; not used for deduplication.
Examplegenerated
{ "roleWelcome": "example", "alternates": [ "example" ], "prologue": [ "example" ], "idempotencyKey": "example"}Responses
Section titled “Responses”Role summary; fieldChars.roleWelcome reports before/after character counts.
Summary returned by every role write.
object
Role id.
Current visibility. HarperHarbor defaults to unlisted (usable by ID without community listing).
Moderation status.
Always owner on these routes.
Present when a deep patch was applied to a single field.
object
Number of operations applied.
Rune count before.
Rune count after.
Hex SHA-256 of the text before.
Hex SHA-256 of the text after.
Per-field patch summaries keyed by field name; present when fieldPatches were used.
object
object
Number of operations applied.
Rune count before.
Rune count after.
Hex SHA-256 of the text before.
Hex SHA-256 of the text after.
Character counts before/after for the edited field (e.g. roleDetailDesc, roleWelcome).
object
object
Character (rune) count before the write.
Character (rune) count after the write.
Suggested next MCP tool names; informational.
Example
{ "roleVisibility": "private", "reviewStatus": "not_submitted", "accountPermission": "owner"}invalid JSON body, roleWelcome is required.
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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.
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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.
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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, role_welcome_too_long, or another unclassified error; retryable: true.
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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}