{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://nowpresenting.net/spec/event-blueprint.schema.json",
  "title": "Pulse Event Blueprint",
  "description": "A complete, reusable description of a Pulse event. POST it to the import endpoint to create a draft event. Cross-references use array INDICES (a segment's speaker, a link's attachment), never ids. Version 2.",
  "type": "object",
  "required": ["pulseTemplate", "title"],
  "additionalProperties": false,
  "properties": {
    "pulseTemplate": { "type": "integer", "const": 2, "description": "Blueprint schema version. Use 2." },
    "title": { "type": "string", "minLength": 1, "description": "The event name." },
    "speaker": { "type": "string", "description": "A single headline speaker name (optional; prefer the speakers[] array)." },
    "passcode": { "type": "string", "description": "Optional room passcode (≥4 chars)." },
    "scheduledStart": { "type": "string", "format": "date-time", "description": "ISO 8601. When set, the event auto-starts at this time." },
    "scheduledEnd": { "type": "string", "format": "date-time" },
    "showAgendaPreStart": { "type": "boolean", "description": "Show the agenda in the pre-start lobby (default true)." },
    "reactionSetId": { "type": "string", "enum": ["default", "minimal", "energetic", "professional", "classroom", "celebration", "focus", "sports", "hearts", "custom"], "description": "The event's default reaction set, or 'custom' to use customReactions." },
    "customReactions": { "type": "array", "items": { "type": "string" }, "minItems": 3, "maxItems": 8, "description": "The organizer's own reaction emoji (3–8, drawn from the curated set) — used when reactionSetId is 'custom'." },
    "attendeeLayout": { "type": "array", "items": { "type": "string", "enum": ["engage", "slides", "chat", "qa", "info"] }, "description": "Order of the attendee tabs (order only; an omitted tab still appears, appended in default order)." },
    "controlSize": { "type": "string", "enum": ["XS", "S", "M", "L"], "description": "Attendee one-tap control size." },
    "deckDownloadable": { "type": "boolean" },
    "requireSurveyForDownload": { "type": "boolean" },
    "slidesInRoomOnly": { "type": "boolean", "description": "Gate slide viewing to attendees on the venue network." },
    "themeId": { "type": "string", "description": "Per-event meeting theme id (omit to inherit the workspace theme)." },
    "accentColor": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
    "accentColor2": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
    "qrFgColor": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$", "description": "Per-event join-QR color." },
    "qrLogo": { "type": "boolean" },
    "stageQr": {
      "type": "object", "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean" },
        "corner": { "type": "string", "enum": ["tl", "tr", "bl", "br", "hidden"] }
      }
    },
    "video": {
      "type": "object", "additionalProperties": false,
      "description": "Attendee-device YouTube video for the three event phases. Each value is a YouTube link (watch/youtu.be/shorts/embed) or a bare 11-char id; omit a slot to skip it. 'live' plays only when the moderator triggers it during the event. Each slot defaults to muted autoplay; set its *Autoplay flag to false to instead show the video and let the viewer press play (with sound).",
      "properties": {
        "lobby": { "type": "string", "description": "Plays on the pre-start lobby screen." },
        "live": { "type": "string", "description": "Plays on attendee devices when the moderator starts it." },
        "ended": { "type": "string", "description": "Plays on the ended screen, below the survey." },
        "lobbyAutoplay": { "type": "boolean", "description": "true (default) = muted autoplay; false = viewer presses play (sound allowed)." },
        "liveAutoplay": { "type": "boolean", "description": "true (default) = muted autoplay; false = viewer presses play." },
        "endedAutoplay": { "type": "boolean", "description": "true (default) = muted autoplay; false = viewer presses play." }
      }
    },
    "settings": { "type": "object", "description": "Feature toggles (reactions, handRaise, pacing, questions, chat, polls, icebreakers, slides, showTimer, guestAccess, moderateQuestions, anonymousFeedback, shareActivity). Omit to inherit workspace defaults." },
    "speakers": {
      "type": "array", "description": "Referenced by index from agenda segments and speaker-attached links.",
      "items": {
        "type": "object", "additionalProperties": false, "required": ["name"],
        "properties": {
          "name": { "type": "string" },
          "title": { "type": "string" },
          "bio": { "type": "string" },
          "links": {
            "type": "array", "maxItems": 6,
            "items": {
              "type": "object", "additionalProperties": false,
              "properties": {
                "kind": { "type": "string", "enum": ["linkedin", "website", "x", "github", "email", "custom"] },
                "value": { "type": "string", "description": "An https:// URL, or a bare email address when kind=email." },
                "label": { "type": "string" }
              }
            }
          }
        }
      }
    },
    "agenda": {
      "type": "array", "maxItems": 50, "description": "Ordered run-of-show. Advancing the deck into a segment's slide range auto-selects it.",
      "items": {
        "type": "object", "additionalProperties": false,
        "properties": {
          "type": { "type": "string", "enum": ["intro", "speaker", "intermission", "closing", "custom"] },
          "title": { "type": "string" },
          "speakerIndex": { "type": "integer", "minimum": 0, "description": "Index into speakers[]." },
          "slideFrom": { "type": "integer", "minimum": 0, "description": "0-based inclusive." },
          "slideTo": { "type": "integer", "minimum": 0 },
          "plannedMinutes": { "type": "integer", "minimum": 1, "maximum": 1440 },
          "reactionSetId": { "type": "string", "description": "Override the event's reaction set for this segment (reactions swap live)." }
        }
      }
    },
    "resources": {
      "type": "array", "description": "Supporting links. Attach to the timeline so they appear automatically.",
      "items": {
        "type": "object", "additionalProperties": false, "required": ["title"],
        "properties": {
          "title": { "type": "string" },
          "url": { "type": "string", "description": "Absolute https:// URL. Optional when a body is given (a text-only card)." },
          "section": { "type": "string" },
          "attachKind": { "type": "string", "enum": ["manual", "event", "speaker", "segment", "slide"], "description": "manual = moderator toggles live; event = always; speaker/segment = visible while that speaker/segment is active; slide = opens once the presenter passes slideIndex and stays." },
          "attachIndex": { "type": "integer", "minimum": 0, "description": "Required when attachKind is speaker or segment: index into speakers[] or agenda[]." },
          "slideIndex": { "type": "integer", "minimum": 0, "description": "Required when attachKind is slide: the 0-based slide this resource opens after." },
          "body": { "type": "string", "description": "Optional text body (≤2000 chars) — renders as a definition/note card; may replace the url." }
        }
      }
    },
    "polls": {
      "type": "array",
      "items": {
        "type": "object", "additionalProperties": false, "required": ["prompt", "options"],
        "properties": {
          "kind": { "type": "string", "enum": ["poll", "quiz"] },
          "prompt": { "type": "string" },
          "multiSelect": { "type": "boolean" },
          "resultsVisibility": { "type": "string", "enum": ["live", "afterClose", "staffOnly"] },
          "options": {
            "type": "array", "minItems": 2,
            "items": { "type": "object", "additionalProperties": false, "properties": { "text": { "type": "string" }, "isCorrect": { "type": "boolean" } }, "required": ["text"] }
          }
        }
      }
    },
    "questions": { "type": "array", "items": { "type": "string" }, "description": "Prepared Q&A questions (seeded, moderator publishes them live)." },
    "survey": {
      "type": "object", "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean" },
        "questions": {
          "type": "array",
          "items": { "type": "object", "additionalProperties": false, "properties": { "prompt": { "type": "string" }, "type": { "type": "string", "enum": ["Rating", "Choice", "Multi", "Text"] }, "options": { "type": "array", "items": { "type": "string" } }, "scale": { "type": "integer" } }, "required": ["prompt"] }
        }
      }
    }
  }
}
