Render an opening through the card's display rules
const url = 'https://api.harperharbor.com/open/v1/role/render?roleId=example';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.harperharbor.com/open/v1/role/render?roleId=example' \ --header 'Authorization: Bearer <token>'Applies the author’s display rules to one opening of a card owned by the bearer, using the same engine the play page runs, and returns the text the player’s renderer receives before Markdown expansion. {{char}} and {{user}} are expanded first: {{user}} becomes the card’s player name, or a language default when that name is a placeholder such as 你 or user. Each rule reports applied, unmatched, disabled, empty or rolled_back (with the engine’s reason: bad_regex, empty_match, replacement_alone, volume or timeout). {{random}} always takes its first option, so two renders of the same card are identical. report.unsupported lists author-API identifiers the card’s chat page does not provide, with a hint: on a classic-page card that is the sandbox sdk, [data-chat] nodes and --chat-* variables (switch the card to the sandbox page); on any page it is MMD’s platform state variables. capture says what still needs a browser. The play page remains authoritative for Markdown, HTML card components, layout and contrast.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Role id.
Which opening to render: 0 (default) is roleWelcome; N is welcomeAlternates[N-1]. Out of range is invalid_param.
Responses
Section titled “Responses”Render report.
object
object
Main opening plus alternates.
object
What {{user}} was expanded to.
What {{char}} was expanded to.
The chosen opening with {{char}} and {{user}} expanded, before any rule.
After the display rules: what the renderer receives. Markdown is not expanded.
Null when the card has no display rules.
object
Total rules.
Enabled rules.
One entry per rule, in application order.
object
Only with rolled_back.
Static scan of rendered; nothing here needs a browser.
object
Count per HTML tag name.
object
hc-* HTML card component classes found.
Inline on*= event handlers.
At most 50 distinct http(s) URLs.
An author-API identifier the card’s chat page does not provide.
object
rendered, welcome, or rule:<id>.
What to do: switch the card to the sandbox page, or drop the call because no chat page provides it.
Whether any enabled rule can match across lines; such rules disable the streaming render cache.
Human-readable notes: rolled-back rules and unsupported identifiers.
The provider takes no screenshots; recommended says what to check in a browser.
object
Example
{ "rules": [ { "status": "applied", "reason": "bad_regex" } ]}roleId is required, or invalid_param when opening is not an index into the openings.
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}role_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 when the rules could not be evaluated within the render deadline; 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}