Script

Add block

Append a new main-section block, or insert at a specific position. Only permitted when project is inactive. This endpoint does not create outro blocks; use PUT /script for controlled outro updates.

post/v1/communities/{communityId}/projects/{projectId}/script/blocks

Request

  • Base URL: https://api.subo.ai
  • URL: https://api.subo.ai/v1/communities/{communityId}/projects/{projectId}/script/blocks
  • Auth: API key in header X-API-Key

Path parameters

communityIdstring required

Community ID (Discord guild snowflake, serialized as string)

projectIdstring required

Project ID

Request body

idinteger nullable
namestring nullable

Variable token name for this block. Used in pipe variables ([q_name]), precondition expressions (NOT((q_name="value"))), and calculated formulas. Auto-assigned as q0, q1, … (by position) when omitted. Must be unique within the script.

section'main' | 'intro' | 'outro' nullable

Block section. intro blocks are shown before the survey begins (editable and deletable). outro blocks are returned only when GET /script?expand=outro is used and are read-only in script write endpoints.

type'single_punch' | 'multi_punch' | 'open_text' | 'open_numeric' | 'rating' | 'opinion_scale' | 'nps' | 'ranking' | 'content_block' | 'action_block' | 'calculated_block' required
answer_style'full_text' | 'emoji_only' | 'select_menu' | 'buttons' nullable

How answer options are displayed. full_text: labelled buttons; emoji_only: keycap emoji buttons; select_menu: dropdown. Applies to single_punch and multi_punch blocks, to ranking (which takes full_text or emoji_only only), and to the scale family (rating / opinion_scale / nps), which accept only buttons (default) or select_menu — a scale's point rendering is set with scale.icon, not here. A select menu is the better widget from six labeled points up, where a button row wraps. Sending full_text or emoji_only on a scale returns 400 invalid_request. Null inherits the project default. For Discord-delivered projects, full_text and select_menu reject options that exceed the Discord character limit (40 for buttons, 50 for select menus) or carry more than one emoji that needs rendering (a button/select label has a single emoji slot) — the request returns 400 invalid_request with a violations array. Use emoji_only (its answer list renders every emoji), shorten the option, or remove the extra emoji.

colorstring nullable

Discord embed accent color as a hex string without the leading # (e.g. e1287e). Applies to content_block and action_block embeds when the project is Discord-delivered. Requires Premium tier. Ignored on read if community is on the Basic tier; blocks authored via the API will still persist the value, but it will not render until the community upgrades.

image_urlstring uri nullable

Image displayed above the question prompt. Requires Premium tier to persist via the editor; readable by all tiers.

thumbnail_urlstring uri nullable

Thumbnail image displayed top-right of the Discord embed. Available on all block types. Requires Premium tier to persist via the editor; readable by all tiers.

promptstring required
mininteger nullable

Min selections (multi_punch) or min value (open_numeric)

maxinteger nullable

Max selections (multi_punch) or max value (open_numeric)

requiredboolean nullable

Whether a response is required to advance

preconditionstring nullable

Raw skip-logic hide expression: when it evaluates to true the block is hidden; when false (or null) it is shown. Prefer the structured show_when / hide_when fields below — they compile to this and avoid its quoting/operator/casing pitfalls. Reference a prior block by its name (matched case-sensitively against the stored, title-cased name, e.g. a block named NotifyPref is referenced as Notifypref) or by position as q1, q2, … qN; named references are recommended because positions shift when blocks are added or removed. Operators: = (or ==), !=, <, >, >=, <=, in (membership/substring), AND, OR, NOT. String literals use double quotes. To show only when a condition holds, wrap it: NOT((Notifypref = "Other")). Invalid expressions (unknown variable, syntax error) are rejected with 400 invalid_request at write time rather than silently failing open. See the Script tag description for the full reference.

action_kind'give_xp' | 'give_achievement' | 'give_role' nullable

Required for action_block. Null for all other block types.

correct_answer_idsinteger[] nullable

Canonical correct-answer field. Array of real option.id values that are correct. Single-element today (MVP single-select grading); array type future-proofs multi-select. Survives option reorder because ids are stable. Only meaningful when scoring_enabled is true on the project.

correct_answer_indexinteger nullable

Deprecated — use correct_answer_ids instead. 0-based position into the current options array of the first correct answer. Still accepted on input (translated to the option's id at write time) and emitted on output for back-compat, but breaks when options are reordered. Only meaningful when scoring_enabled is true.

when_correctstring nullable

Message shown to the respondent immediately after they answer correctly. Only meaningful when correct_answer_ids is set. Supports template variables: [UserName], [answer] (their response), [score], [max_score], [score_correct], [max_score_correct], [correct_answers], [max_correct_answers], [score_<bucket>], [max_score_<bucket>], [score_correct_<bucket>], [max_score_correct_<bucket>], and answer-pipe tokens [BlockLabel]. score_correct counts only points earned on correctly-answered graded questions; correct_answers / max_correct_answers count graded questions right vs total.

when_incorrectstring nullable

Message shown to the respondent immediately after they answer incorrectly. Only meaningful when correct_answer_ids is set. Supports the same template variables as when_correct.

randomize_optionsboolean nullable

Shuffle the option order per respondent, so the list a participant sees is not the order you authored. Valid on single_punch, multi_punch and ranking; sending it on rating / opinion_scale / nps returns 400 invalid_request — a scale is ordered by definition and its points are never shuffled.

It matters most on ranking, where a fixed order is not a cosmetic default but a measurement bias: whichever item sits first gets tapped first, and that bias lands in the average rank the results report.

The shuffle is seeded per respondent per session, so revisiting a question does not reorder it mid-answer. Options carrying anchor_position: "last" are appended after the shuffle in their stored order. Defaults to false. Discord poll embeds are out of scope — one embed renders for every viewer, so there is no per-respondent order to vary.

text_analysisboolean nullable

open_text only: send this block's answers for AI summary. null (or absent on read) follows the project's settings.text_analysis. Without an explicit value, a block whose question asks for personal data (name, email, phone, address...) is not summarized. Whatever this says, respondents are sent as pseudonyms, personal data in answers (emails, phone numbers, ids...) is removed before sending, and a block whose answers are mostly personal data is not summarized at all. Sending true/false on another block type returns 400 invalid_request. On update: omit to leave it, send null to follow the project again.

rank_top_ninteger nullable

Ranking only: how many items the respondent is asked to put in order ("rank your top 3"). Omit or null to have every item ranked. It has its own field rather than riding max, which means max SELECTIONS on a multi_punch.

A ranking submits complete or not at all — every item, or exactly the top N — so average ranks stay comparable across respondents. Each ranked item is stored as one response row whose value is the rank position (1 = first) and whose option_id is the item; unranked items in top-N mode have no row.

Ranking has one interaction model (tap in order) on both Discord and web, so its answer_style is only about how the items are drawn: full_text (default) or emoji_only, which puts the item names in a list above the buttons and is the way to fit long names on Discord. select_menu returns 400 invalid_request — a dropdown can't express an order. The item list is capped at 20 — a full grid of Discord buttons — and more than about 7 items is ranked unreliably, which is what rank_top_n is for.

calculated_formulastring nullable

Formula for calculated_block type. Evaluated in script order; earlier calculated blocks can be referenced by subsequent blocks via [BlockLabel]. Three expression types are supported:

  1. argmax([score_a], [score_b], ...) — returns the display name of the bucket with the highest value.

  2. if <cond> then <val> else <fallback> — keywords are case-insensitive. <cond> supports >=, <=, !=, ==, >, < (numeric). A bare numeric condition is truthy if non-zero.

  3. Arithmetic: any expression using [variable] tokens, digits, +, -, *, /, (, ). Unknown variables resolve to 0.

Available variables: [score], [max_score], [score_correct], [max_score_correct], [correct_answers], [max_correct_answers], [score_<bucket>], [max_score_<bucket>], [score_correct_<bucket>], [max_score_correct_<bucket>], answer-pipe tokens [BlockLabel], and previously evaluated [CalcFieldName] tokens. score_correct / max_score_correct accumulate only from correctly-answered graded questions; correct_answers / max_correct_answers are question counts (right vs total gradeable).

Examples: argmax([score_gryffindor], [score_slytherin], [score_ravenclaw]) if [score] >= 15 then Excellent else Good [score] / [max_score] * 100 if [correct_answers] == [max_correct_answers] then Perfect! else [correct_answers] out of [max_correct_answers] [score_correct] / [max_score_correct] * 100

positioninteger nullable

0-based position in the script. Omit to append.

Response

Updated script with new block

Changes

No recorded changes to this operation across all 1 revision of this API.