# How to create for the Rafflex marketplace

This is the entry point of the public creator documentation. It is written for an AI assistant that advises a creator on, or builds, marketplace assets. When you are setting a creator up for the first time, follow the setup runbook listed first in llms.txt instead, and come back here for the rules.

## What Rafflex is

Rafflex is a raffle platform: businesses run competition websites on it, selling tickets for prize draws and instant wins. The community marketplace extends those sites with creator built assets. A creator builds an asset once and submits it for review; once staff approve it, it is listed on the marketplace, where any Rafflex site owner can install it (free or paid, at the creator's choice).

- When the creator brings an idea, decide which asset type it is first (next section), because the type is fixed once the product exists.
- When these documents do not cover a behaviour, say so and ask the creator rather than guessing, because everything an asset can and cannot do is written here and in the rules the platform enforces.

## What you can build

There are two asset types:

- A **game** runs on the order complete page after a buyer purchases raffle tickets. It is a client side reveal animation over the buyer's plays, whose outcomes the platform has already decided: scratch cards, prize wheels, mystery boxes, slot reels, and similar.
- A **block** is a section of a raffle site's pages: a hero, countdown, winner wall, product grid, FAQ, and similar. It renders anywhere on the site, is driven by the site's competitions, categories, winners, and settings, themes itself from the site's own colours, and exposes the options a site owner can change (a heading, a colour, how many to show) straight from the template.

The dividing line is the plays array: games exist to reveal it, and blocks must not rely on it (it is usually empty outside an order).

- When the idea is about the moment of finding out whether tickets won, build a game.
- When the idea is about presenting the site's content, build a block.
- When an idea needs both (say a winner wall plus a spin to win moment), build two assets, because one template cannot be both types.

## How assets work

- Every asset is one Twig template: markup, styles, and JavaScript in a single file, rendered inside a strict shared sandbox on a customer's raffle site.
- The platform injects data as context (competitions pre-sorted four ways, categories with their competitions, winners, the buyer's plays, site settings, the product's uploaded files, and the options the site owner set), and the template renders from it.
- Games are client side animations of pre-decided outcomes: the platform decides every win before the template runs, and the game reveals the results in the plays array. Never compute or randomise a win in a template, because the outcome the buyer sees must be the one the platform recorded.
- A template's own code always stays inline. A script src, or an import in a module script, is allowed only for an approved library (a third party build staff have vetted, such as three.js) referenced through the files map, for example `import * as THREE from "{{ files['three'] }}";`. Any other script source, bare import, import map, or dynamic import() is rejected.

## The creator workflow

The reference topics follow the order a first product takes. A product starts in the studio, a private scratchpad, and becomes a product only once something works. A connected AI works on the product's private draft instead; nothing reaches buyers until staff approve a version.

- Before you start: learn how assets work, the sandbox rules, and the data contract (this document).
- Connect your AI: connect the assistant to the creator's marketplace account through the remote MCP server, so it pushes and checks drafts directly. Connecting is optional; without it, the creator copies prompts and templates between the studio and the chat.
- Build with AI: connected, write the template to the draft and fix what the check reports. Not connected, the creator copies the studio's prompt (Build starts from scratch, Iterate refines the current template, Fix carries the last error), pastes your template into the editor, and runs it in the preview.
- Add media: every image, sound, model, and approved library joins the product's media library and is referenced by its tag through the files map.
- Test every scenario: render every play scenario and play count, then run the template check that submission runs.
- List and submit: the product is created from the working template (its media moves with it), the creator sets the listing and price, the Ready to submit checklist passes, and the draft is submitted for review. Approval publishes it, immediately or at a scheduled go live time.
- After launch: ship new versions through the same review, keep install notes and documentation current, answer questions, and fix reported bugs.

The creator decides when to submit and sets the price. Never submit until the creator says so, because a submission is emailed to them and enters the staff review queue.

## Sandbox rules (hard constraints)

The Twig sandbox rejects anything outside this whitelist:

- Allowed tags: if, for, set. No other tag exists (no block, include, extends, macro, verbatim, or apply).
- Allowed filters: upper, lower, date, number_format, default, length, first, last, join, slice, round, trim, escape, json. No other filter exists.
- No Twig functions at all (no range(), cycle(), random(), dump(), max(), or min()).
- No range operator (..).
- At most 3 {% for %} loops in the whole template.
- Iteration caps: at most 1000 items per collection and 25000 total loop iterations, within a 2000 ms render budget.

Scripts are checked in the source and again in the rendered output of every scenario:

- Your own JavaScript stays inline. A script src, or a static import in a <script type="module">, must be exactly a files reference such as {{ files['three'] }} whose tag points at an approved library. Any other src fails with "Scripts may load only an approved library through files, for example src="{{ files['three'] }}". Put your own code inline."
- Bare imports (import * as THREE from "three"), relative or absolute URLs, and libraries that are not approved fail with "Import approved libraries by their files URL, for example import * as THREE from "{{ files['three'] }}". Bare imports and other URLs are not allowed."
- Import maps fail with "Import maps are not allowed. Import the approved library by its files URL instead."
- Dynamic import() fails with "Dynamic script imports are not allowed."

## Submission rules (hard constraints)

- Templates are first party only, with no allowlist and no exceptions: no external domain may appear anywhere, in any attribute, style, or script. That means no CDN scripts, no Google Fonts, no hotlinked images, no absolute http(s) URLs, and no protocol-relative //domain URLs. Reference only relative platform paths and uploaded media through the files map by tag ({{ files['background'] }}). Every layer enforces this: the static checks, a scan of the rendered output, the preview's security policy, human review, and periodic fleet re-scans.
- No <form> elements, formaction attributes, or <link> tags. Game entry flows through the platform, inputs bound to Alpine state cover every interactive need, and styles ship inline.
- No network or exfiltration APIs, even aliased: fetch, XMLHttpRequest, WebSocket, EventSource, sendBeacon, WebRTC, and the entire navigator object are banned as bare identifiers, along with eval, Function, constructor or __proto__ or Reflect access, import.meta, CSS @import, data:text/html, and hex or unicode escape sequences.
- Everything stays inline in the single template: styles in a <style> block, JavaScript in a <script> block. The one exception is an approved library: a script src, or an import in a <script type="module">, may load one, and only through its files reference, such as {{ files['three'] }}. Your own code never moves into a separate file.
- The template must render a graceful, well-designed empty state when plays is empty and when any list it uses has no entries.

## Documents in this series

Every document is public, plain markdown, and self contained. Read the one that matches the task instead of guessing a rule:

- [Connect your AI](https://marketplace.rafflex.io/docs/how-to-create/connect.md): The remote MCP server: connecting each AI client, signing in, starter prompts, watch mode, uploads per client, what a connected AI can and cannot do, and disconnecting.
- [Architect a game](https://marketplace.rafflex.io/docs/how-to-create/games.md): The complete specification for building a marketplace game: data contract, plays array, injection pattern, and definition of done.
- [Architect a block](https://marketplace.rafflex.io/docs/how-to-create/blocks.md): The complete specification for building a marketplace page block: data contract, theming from settings, and definition of done.
- [Use images and sound](https://marketplace.rafflex.io/docs/how-to-create/media.md): Uploading images, sound, and 3D models to the product media library, adding approved libraries, and wiring them into a template through the files map.
- [Test and debug](https://marketplace.rafflex.io/docs/how-to-create/testing.md): The preview scenarios that exercise every template state, testing with a connected AI, and the platform errors mapped to fixes.
- [List and submit](https://marketplace.rafflex.io/docs/how-to-create/submit.md): Creating the product, the listing fields, pricing and creator earnings, the Ready to submit checklist, the reviewer pitfalls, and what review checks.
- [After launch](https://marketplace.rafflex.io/docs/how-to-create/after-launch.md): New versions, release channels, how installs update, install notes, bug reports, questions, what a connected AI reports, and announcements.
- [Template data contract](https://marketplace.rafflex.io/docs/template-context.md): every variable a template receives, with generated samples.