Skip to content

Create an agent configuration

POST
/open/v1/role
curl --request POST \
--url https://api.harperharbor.com/open/v1/role \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "roleName": "example", "language": "example", "cardType": "story", "idempotencyKey": "example", "origin": "example", "visibility": "private" }'

Creates an agent configuration owned by the bearer account. HarperHarbor defaults to roleVisibility=unlisted: anyone with the ID can use its public presentation and authenticated playback across applications, without community approval. Full instructions and Lorebooks remain author-only. Communities independently control review, listing and discovery. Explicit private configurations remain owner-only; deleted or provider-taken-down configurations are unavailable. Fill content with the document and welcome endpoints. Tenant records storage and application attribution, not a playback boundary. Creates an agent configuration owned by the bearer account. Set visibility to private for author-only access or unlisted for link access; omitted visibility defaults to unlisted. Authorized clients retain their scoped access. Creation does not publish or submit for review. The CLI sends unlisted with --create.

Media typeapplication/json
object
roleName
required

Card name; trimmed; must be non-blank.

string
language

Card language. Recognized values and aliases are normalized to en, zh-Hant, zh-Hans, ja, ko, all (e.g. zh-TW, zh-CN, jp, kr); any other non-empty string is stored as given and treated as a non-English locale for limits. Defaults to the account language, then zh-Hans. Drives per-field character limits.

string
cardType

Stored as-is as the card’s roleType (not validated). Values understood by validation/quality heuristics: companion, story, game, generator.

string
default: story
idempotencyKey

Accepted for API symmetry; not used for deduplication on this route.

string
origin

Self-declared creation source. Values on the provider’s whitelist are stored as creationMethod; anything else falls back to open_api.

string
visibility
string
default: unlisted
Allowed values: private unlisted

Success

Media typeapplication/json

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>
Example
{
"roleVisibility": "private",
"reviewStatus": "not_submitted",
"accountPermission": "owner"
}

invalid JSON body, roleName is required (body-level checks; body is {"error": "..."}), or invalid_arguments from the service ({"error": "invalid_arguments", "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
}

Unclassified service/database error; {"error": "<string>", "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
}