Update metadata, add/update/delete entries and bind a Lorebook in one request
const url = 'https://api.harperharbor.com/open/v1/worldbook/example/document';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"metadata":{"name":"example","description":"example","iconUrl":"example","tags":["example"],"visibility":"private","textPatches":{"description":{"baseSha256":"example","operations":[{"op":"replaceText","oldText":"example","newText":"example","text":"example","beforeText":"example","afterText":"example","replaceAll":false}]},"content":{"baseSha256":"example","operations":[{"op":"replaceText","oldText":"example","newText":"example","text":"example","beforeText":"example","afterText":"example","replaceAll":false}]}}},"entries":[{"op":"create","entryId":"example","name":"example","content":"example","category":"rule","keywords":["example"],"secondaryKeywords":["example"],"isEnabled":true,"isConstant":true,"triggerRegion":"both","matchOptions":{"caseSensitive":false,"matchWholeWords":false,"selectiveLogic":0,"selective":true,"scanDepth":1,"order":1,"groupId":"example","groupOrder":1,"extensions":{}},"contentDeepPatch":{"baseSha256":"example","operations":[{"op":"replaceText","oldText":"example","newText":"example","text":"example","beforeText":"example","afterText":"example","replaceAll":false}]},"textPatches":{"description":{"baseSha256":"example","operations":[{"op":"replaceText","oldText":"example","newText":"example","text":"example","beforeText":"example","afterText":"example","replaceAll":false}]},"content":{"baseSha256":"example","operations":[{"op":"replaceText","oldText":"example","newText":"example","text":"example","beforeText":"example","afterText":"example","replaceAll":false}]}}}],"binding":{"roleId":"example","targetType":"character","targetId":"example"}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.harperharbor.com/open/v1/worldbook/example/document \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "metadata": { "name": "example", "description": "example", "iconUrl": "example", "tags": [ "example" ], "visibility": "private", "textPatches": { "description": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] }, "content": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] } } }, "entries": [ { "op": "create", "entryId": "example", "name": "example", "content": "example", "category": "rule", "keywords": [ "example" ], "secondaryKeywords": [ "example" ], "isEnabled": true, "isConstant": true, "triggerRegion": "both", "matchOptions": { "caseSensitive": false, "matchWholeWords": false, "selectiveLogic": 0, "selective": true, "scanDepth": 1, "order": 1, "groupId": "example", "groupOrder": 1, "extensions": {} }, "contentDeepPatch": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] }, "textPatches": { "description": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] }, "content": { "baseSha256": "example", "operations": [ { "op": "replaceText", "oldText": "example", "newText": "example", "text": "example", "beforeText": "example", "afterText": "example", "replaceAll": false } ] } } } ], "binding": { "roleId": "example", "targetType": "character", "targetId": "example" } }'Applies metadata, entry operations and binding in one transaction; an invalid operation rolls back the entire request. At most 200 entry operations are accepted per request. The caller must own the Lorebook and any agent configuration being bound. Metadata replaces name, description, iconUrl and tags; omitted visibility is preserved. The response returns createdEntryIds in create-operation order. Read the book-specific entryContentMax from the detail endpoint; the widest bound language applies. There is no idempotency deduplication: after an unknown outcome, read the entries before repeating creates.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Lorebook id. Only the path parameter is honoured.
Request Bodyrequired
Section titled “Request Bodyrequired”object
Full replacement of the Lorebook’s editable metadata. Fields not sent are blanked (description, iconUrl, tags) or reset to private (visibility), except when textPatches.description is present, in which case missing fields are back-filled from the current record.
object
Required, non-blank (name required).
Unknown/omitted = private.
object
Metadata description deep patch.
object
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.
Operations applied in order.
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
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).
Text to find (replace operations).
Replacement text.
Text to insert (insert operations).
Anchor: insert before this text.
Anchor: insert after this text.
Replace every occurrence instead of requiring a unique anchor.
Entry content deep patch (update op; same as contentDeepPatch).
object
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.
Operations applied in order.
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
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).
Text to find (replace operations).
Replacement text.
Text to insert (insert operations).
Anchor: insert before this text.
Anchor: insert after this text.
Replace every occurrence instead of requiring a unique anchor.
One entry operation. create: name and content required (content non-blank and within the book’s entry limit: 4000 chars for Chinese, 6000 for Japanese or Korean, 12000 for English, following the language of the cards the book is bound to, the widest when there are several; an unbound book uses its own language; entryContentMax on GET /open/v1/worldbook/detail gives the value); category defaults to custom; triggerRegion defaults to both; isConstant defaults to false; matchOptions omitted = tavern default on tavern books, none on native books; keywords shorter than the minimum length are silently dropped (single characters are allowed only on tavern-format books). update: entryId required; empty-string name/content/category/triggerRegion mean ‘keep’; keywords/secondaryKeywords null/omitted = keep, [] = clear; isEnabled/isConstant/matchOptions omitted = keep; contentDeepPatch (or textPatches.content) edits the content in place. delete: entryId required. Unknown op fails with invalid_worldbook_document_operation.
object
Case-insensitive.
Target entry for update/delete.
At most the book’s entry limit, entryContentMax on the Lorebook detail (4000 chars for Chinese, 6000 for Japanese or Korean, 12000 for English).
Update only.
Tavern-format keyword matching options. null on native entries.
object
How secondaryKeywords gate activation: 0 = AND ANY (any secondary keyword present), 1 = NOT ALL, 2 = NOT ANY, 3 = AND ALL. Out-of-range values are normalized to 0.
When false, ignore secondaryKeywords. If omitted, secondaryKeywords use selectiveLogic.
HarperHarbor: keyword scan window in messages, including current input. Zero or omitted uses the current input and latest assistant reply.
Author ordering for new admissions. Existing HarperHarbor context anchors keep their original positions.
Optional logical entry identifier, scoped to one book. HarperHarbor semantic recall admits eligible imported fragments together.
Fragment order within groupId.
Preserved author metadata. Unknown fields are not executed as matching or placement instructions.
object
Anchored edit applied to the current value of a text field.
object
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.
Operations applied in order.
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
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).
Text to find (replace operations).
Replacement text.
Text to insert (insert operations).
Anchor: insert before this text.
Anchor: insert after this text.
Replace every occurrence instead of requiring a unique anchor.
object
Metadata description deep patch.
object
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.
Operations applied in order.
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
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).
Text to find (replace operations).
Replacement text.
Text to insert (insert operations).
Anchor: insert before this text.
Anchor: insert after this text.
Replace every occurrence instead of requiring a unique anchor.
Entry content deep patch (update op; same as contentDeepPatch).
object
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.
Operations applied in order.
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
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).
Text to find (replace operations).
Replacement text.
Text to insert (insert operations).
Anchor: insert before this text.
Anchor: insert after this text.
Replace every occurrence instead of requiring a unique anchor.
Bind this Lorebook to a role. Send roleId (targetType defaults to character), or an explicit targetType/targetId pair. A role holds one active Lorebook; the previous binding is replaced.
object
Role id (used as targetId when targetType is character).
Responses
Section titled “Responses”Summary of what was applied.
object
Example
{ "documentVersion": "worldbookPatch.v1"}worldbook_limit_exceeded reports field, reason, index, max, actual and unit for an operation-count limit. Other validation errors use invalid_arguments with field, index and reason; length errors also include max, actual and unit. Entry indices are zero-based; document-level fields use -1. Fix the indicated field or split the batch before resending.
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}not_found
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}temporarily_unavailable
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}