{
  "version": "riddle-proof.profile-result.v1",
  "profile_name": "riddle-site-v525-docs-code-copy-clipboard-denial",
  "runner": "riddle",
  "status": "passed",
  "baseline_policy": "invariant_only",
  "route": {
    "requested": "https://riddledc.com/docs/preview/",
    "observed": "/docs/preview/",
    "expected_path": "/docs/preview/",
    "matched": true,
    "http_status": 200
  },
  "artifacts": {
    "screenshots": [
      "riddle-site-v525-docs-code-copy-clipboard-denial-desktop",
      "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-before",
      "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-after",
      "riddle-site-v525-docs-code-copy-clipboard-denial-phone",
      "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-before",
      "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-after",
      "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini",
      "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-before",
      "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-after",
      "riddle-site-v525-docs-code-copy-clipboard-denial-ipad",
      "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-before",
      "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-after"
    ],
    "console": "console.json",
    "proof_json": "proof.json",
    "dom_summary": "dom-summary.json",
    "riddle_artifacts": [
      {
        "name": "proof.json",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/proof.json.json",
        "source": "artifacts"
      },
      {
        "name": "console.json",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/console.json.json",
        "source": "artifacts"
      },
      {
        "name": "dom-summary.json",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/dom-summary.json.json",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-before.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-before.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-after.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-after.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-desktop.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-desktop.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-before.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-before.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-after.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-after.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-phone.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-phone.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-before.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-before.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-after.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-after.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-before.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-before.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-after.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-after.png",
        "source": "artifacts"
      },
      {
        "name": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad.png",
        "url": "https://cdn.riddledc.com/scripts/job_59dc499b/riddle-site-v525-docs-code-copy-clipboard-denial-ipad.png",
        "source": "artifacts"
      }
    ]
  },
  "checks": [
    {
      "type": "setup_actions_succeeded",
      "label": "setup actions succeeded",
      "status": "passed",
      "evidence": {
        "action_count": 8,
        "viewports": [
          {
            "name": "desktop",
            "expected_action_count": 8,
            "ok": true,
            "result_count": 8
          },
          {
            "name": "phone",
            "expected_action_count": 8,
            "ok": true,
            "result_count": 8
          },
          {
            "name": "ipad-mini",
            "expected_action_count": 8,
            "ok": true,
            "result_count": 8
          },
          {
            "name": "ipad",
            "expected_action_count": 8,
            "ok": true,
            "result_count": 8
          }
        ],
        "setup_summary": {
          "viewport_count": 4,
          "action_count": 8,
          "viewports": [
            {
              "name": "desktop",
              "expected_action_count": 8,
              "ok": true,
              "result_count": 8,
              "observed_path": "/docs/preview/",
              "final_url": "https://riddledc.com/docs/preview/",
              "action_counts": {
                "wait_for_selector": 2,
                "wait_for_text": 2,
                "clear_console": 1,
                "screenshot": 2,
                "click": 1
              },
              "frame_action_count": 0,
              "frame_urls": [],
              "setup_screenshots": [
                "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-before",
                "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-after"
              ],
              "clicked_total": 1,
              "clicked_truncated": false,
              "click_count_action_total": 0,
              "click_count_value_total": 0,
              "clicked": [
                {
                  "ordinal": 5,
                  "selector": ".copy-button",
                  "frame_selector": null,
                  "text": "Copy"
                }
              ],
              "text_samples": [
                {
                  "ordinal": 2,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle Docs Proof MCP Pricing Blog Playground Sign Up Log In ← Back to Docs Preview Tools Deploy any app to an ephemeral URL... (517 chars)"
                },
                {
                  "ordinal": 6,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle Docs Proof MCP Pricing Blog Playground Sign Up Log In ← Back to Docs Preview Tools Deploy any app to an ephemeral URL... (517 chars)"
                }
              ],
              "failed": [],
              "optional_failed": []
            },
            {
              "name": "phone",
              "expected_action_count": 8,
              "ok": true,
              "result_count": 8,
              "observed_path": "/docs/preview/",
              "final_url": "https://riddledc.com/docs/preview/",
              "action_counts": {
                "wait_for_selector": 2,
                "wait_for_text": 2,
                "clear_console": 1,
                "screenshot": 2,
                "click": 1
              },
              "frame_action_count": 0,
              "frame_urls": [],
              "setup_screenshots": [
                "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-before",
                "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-after"
              ],
              "clicked_total": 1,
              "clicked_truncated": false,
              "click_count_action_total": 0,
              "click_count_value_total": 0,
              "clicked": [
                {
                  "ordinal": 5,
                  "selector": ".copy-button",
                  "frame_selector": null,
                  "text": "Copy"
                }
              ],
              "text_samples": [
                {
                  "ordinal": 2,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Pl... (517 chars)"
                },
                {
                  "ordinal": 6,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Pl... (517 chars)"
                }
              ],
              "failed": [],
              "optional_failed": []
            },
            {
              "name": "ipad-mini",
              "expected_action_count": 8,
              "ok": true,
              "result_count": 8,
              "observed_path": "/docs/preview/",
              "final_url": "https://riddledc.com/docs/preview/",
              "action_counts": {
                "wait_for_selector": 2,
                "wait_for_text": 2,
                "clear_console": 1,
                "screenshot": 2,
                "click": 1
              },
              "frame_action_count": 0,
              "frame_urls": [],
              "setup_screenshots": [
                "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-before",
                "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-after"
              ],
              "clicked_total": 1,
              "clicked_truncated": false,
              "click_count_action_total": 0,
              "click_count_value_total": 0,
              "clicked": [
                {
                  "ordinal": 5,
                  "selector": ".copy-button",
                  "frame_selector": null,
                  "text": "Copy"
                }
              ],
              "text_samples": [
                {
                  "ordinal": 2,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Pl... (517 chars)"
                },
                {
                  "ordinal": 6,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Pl... (517 chars)"
                }
              ],
              "failed": [],
              "optional_failed": []
            },
            {
              "name": "ipad",
              "expected_action_count": 8,
              "ok": true,
              "result_count": 8,
              "observed_path": "/docs/preview/",
              "final_url": "https://riddledc.com/docs/preview/",
              "action_counts": {
                "wait_for_selector": 2,
                "wait_for_text": 2,
                "clear_console": 1,
                "screenshot": 2,
                "click": 1
              },
              "frame_action_count": 0,
              "frame_urls": [],
              "setup_screenshots": [
                "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-before",
                "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-after"
              ],
              "clicked_total": 1,
              "clicked_truncated": false,
              "click_count_action_total": 0,
              "click_count_value_total": 0,
              "clicked": [
                {
                  "ordinal": 5,
                  "selector": ".copy-button",
                  "frame_selector": null,
                  "text": "Copy"
                }
              ],
              "text_samples": [
                {
                  "ordinal": 2,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Pl... (517 chars)"
                },
                {
                  "ordinal": 6,
                  "action": "wait_for_text",
                  "frame_selector": null,
                  "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Pl... (517 chars)"
                }
              ],
              "failed": [],
              "optional_failed": []
            }
          ]
        },
        "failed": []
      }
    },
    {
      "type": "route_loaded",
      "label": "route_loaded",
      "status": "passed",
      "evidence": {
        "expected_path": "/docs/preview/",
        "observed_paths": [
          "/docs/preview/",
          "/docs/preview/",
          "/docs/preview/",
          "/docs/preview/"
        ],
        "http_statuses": [
          200,
          200,
          200,
          200
        ]
      }
    },
    {
      "type": "selector_visible",
      "label": "selector_visible",
      "status": "passed",
      "evidence": {
        "selector": ".guide-page.preview-tools-page",
        "visible_counts": [
          1,
          1,
          1,
          1
        ]
      }
    },
    {
      "type": "selector_visible",
      "label": "selector_visible",
      "status": "passed",
      "evidence": {
        "selector": ".code-block-wrapper",
        "visible_counts": [
          10,
          10,
          10,
          10
        ]
      }
    },
    {
      "type": "selector_count_at_least",
      "label": "selector_count_at_least",
      "status": "passed",
      "evidence": {
        "selector": ".copy-button",
        "min_count": 1,
        "counts": [
          10,
          10,
          10,
          10
        ]
      }
    },
    {
      "type": "text_visible",
      "label": "text_visible",
      "status": "passed",
      "evidence": {
        "text": "Preview",
        "matches": [
          true,
          true,
          true,
          true
        ]
      }
    },
    {
      "type": "text_visible",
      "label": "text_visible",
      "status": "passed",
      "evidence": {
        "text": "riddle_preview",
        "matches": [
          true,
          true,
          true,
          true
        ]
      }
    },
    {
      "type": "text_absent",
      "label": "text_absent",
      "status": "passed",
      "evidence": {
        "text": "Application error",
        "matches": [
          false,
          false,
          false,
          false
        ]
      }
    },
    {
      "type": "no_horizontal_overflow",
      "label": "no_horizontal_overflow",
      "status": "passed",
      "evidence": {
        "max_overflow_px": 1,
        "overflow_px": [
          0,
          0,
          0,
          0
        ],
        "bounds_overflow_px": [
          0,
          0,
          0,
          0
        ],
        "overflow_offender_counts": [
          0,
          0,
          0,
          0
        ],
        "viewports": [
          "desktop",
          "phone",
          "ipad-mini",
          "ipad"
        ]
      }
    },
    {
      "type": "no_fatal_console_errors",
      "label": "no_fatal_console_errors",
      "status": "passed",
      "evidence": {
        "console_fatal_count": 0,
        "page_error_count": 0,
        "total_console_fatal_count": 0,
        "total_page_error_count": 0,
        "allowed_console_fatal_count": 0,
        "explicitly_allowed_console_fatal_count": 0,
        "allowed_expected_network_mock_console_count": 0,
        "allowed_expected_network_mock_console_events": [],
        "allowed_page_error_count": 0,
        "allowed_console_texts": [],
        "allowed_console_patterns": [],
        "allowed_page_error_texts": [],
        "allowed_page_error_patterns": []
      }
    },
    {
      "type": "no_console_warnings",
      "label": "no_console_warnings",
      "status": "passed",
      "evidence": {
        "console_warning_count": 0,
        "total_console_warning_count": 1,
        "allowed_console_warning_count": 1,
        "allowed_console_texts": [],
        "allowed_console_patterns": [
          "preloaded using link preload but not used within a few seconds"
        ],
        "unallowed_console_warning_samples": [],
        "allowed_console_warning_samples": [
          "The resource https://riddledc.com/_next/static/css/2d6eac7c22a4fd3d.css was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally. https://riddledc.com/docs/preview/"
        ]
      }
    }
  ],
  "summary": "riddle-site-v525-docs-code-copy-clipboard-denial passed 11 check(s) across 4 viewport(s) (desktop, phone, ipad-mini, ipad).",
  "captured_at": "2026-05-18T00:37:53.129Z",
  "evidence": {
    "version": "riddle-proof.profile-evidence.v1",
    "profile_name": "riddle-site-v525-docs-code-copy-clipboard-denial",
    "target_url": "https://riddledc.com/docs/preview/",
    "baseline_policy": "invariant_only",
    "captured_at": "2026-05-18T00:37:53.129Z",
    "viewports": [
      {
        "name": "desktop",
        "width": 1280,
        "height": 900,
        "url": "https://riddledc.com/docs/preview/",
        "route": {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        },
        "title": "Preview Tools Guide - Deploy & Screenshot Any App | Riddle",
        "body_text_length": 12583,
        "body_text_sample": "Skip to main content Riddle Docs Proof MCP Pricing Blog Playground Sign Up Log In ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle_preview server_preview build_preview Speed ~5s ~30–60s ~1–3min Server process No (static only) Yes Yes Docker image — Stock (pull) Custom (build) System packages — — ✓ Image caching — — ✓ (30 min default) Security audit — — ✓ Playwright scripts — ✓ ✓ Env vars — ✓ ✓ Preview URL ✓ (24hr) — — riddle_preview — Static Hosting Deploy built static files (HTML, CSS, JS) to an ephemeral URL at preview.riddledc.com. The preview expires after 24 hours. Best for SPAs, Vite builds, Next.js static exports, or any pre-built frontend. How It Works Tar your build output directory Upload to Riddle Get a live URL in ~5 seconds Screenshot it with any Riddle tool Basic Example Copied! const response = await fetch(\"https://api.riddledc.com/v1/preview\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ directory: \"/path/to/build/output\", framework: \"spa\" // \"spa\" (default) or \"static\" }) }); const { preview_url, id } = await response.json(); // preview_url: \"https://preview.riddledc.com/s/pv_a1b2c3d4/\" // id: \"pv_a1b2c3d4\" (use to delete early) Parameters Parameter Type Description directory string Absolute path to build output (must contain index.html) framework string \"spa\" (default) — all routes serve index.html. \"static\" — file-based routing. 💡 When to use riddle_preview Your app is already built and just needs hosting. You have a dist/ or out/ folder with static files. You want a shareable URL that works for 24 hours. Delete Early Previews auto-expire after 24 hours, but you can clean up immediately: Copy await fetch(\"https://api.riddledc.com/v1/preview/pv_a1b2c3d4\", { method: \"DELETE\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}` } }); riddle_server_preview — Server Apps Run a server-side application inside an isolated Docker container and screenshot it with Playwright. Uses stock images from Docker Hub — no Dockerfile needed. Perfect for Next.js, Express, Django, FastAPI, Rails, and any app that needs a running server process. How It Works Tar your project directory and upload it Pull a stock Docker image (e.g. node:20-slim) Start your server inside the container Wait for the readiness check to pass Take a Playwright screenshot Return the screenshot and logs Polling responses include a current phase, phase_updated_at, and sometimes phase_details. They also include progress with step, total, percent, label, and terminal fields for simple status displays. This makes stuck jobs easier to read: you can tell whether Riddle is pulling an image, extracting the upload, starting the server, waiting for readiness, running browser proof, collecting artifacts, or finalizing outputs. Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ server_preview: { directory: \"/path/to/my-express-app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, path: \"/dashboard\", env: { NODE_ENV: \"production\" }, viewport: { width: 1920, height: 1080 } } }) }); With Authentication & Custom Scripts Copy { server_preview: { directory: \"/path/to/app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, // Inject localStorage tokens before page loads localStorage: { \"auth_token\": \"eyJhbGciOiJIUzI1NiJ9...\", \"user_id\": \"12345\" }, // Sensitive env vars — stored securely, deleted after use sensitive_env: { DATABASE_URL: \"postgres://...\", API_SECRET: \"sk-...\" }, // Wait for React hydration before screenshotting wait_for_selector: \"#app[data-hydrated]\", // Run a Playwright script after server is ready script: ` await page.click('button.load-data'); await page.waitForSelector('.data-table'); await saveScreenshot('with-data'); `, // Emulate dark mode color_scheme: \"dark\", // Readiness check config readiness_path: \"/health\", readiness_timeout: 30, timeout: 120 } } Key Parameters Parameter Type Description directory string Path to your project image string Docker image to run (node:20-slim, python:3.12-slim, etc.) command string Command to start your server port number Port your server listens on path string URL path to screenshot (default: /) env object Non-sensitive environment variables sensitive_env object Secrets — stored securely, deleted after use localStorage object Key-value pairs injected before page load script string Playwright script to run after server is ready wait_for_selector string CSS selector to wait for before screenshotting color_scheme string \"dark\" or \"light\" — applied before navigation viewport object { width, height } — default 1920×1080 readiness_path string Health check endpoint to poll readiness_timeout number Seconds to wait for the server to answer before browser proof starts navigation_timeout number Seconds Playwright should wait for the target page after readiness passes timeout number Max execution time in seconds (default: 120) Common Docker Images Node.js node:20-slim Express, Fastify, Next.js, Nuxt Python python:3.12-slim Django, FastAPI, Flask Ruby ruby:3.3-slim Rails, Sinatra riddle_build_preview — Custom Builds Build a Docker image from your own Dockerfile, run the server, and screenshot it. Use this when you need custom system packages, compiled languages, multi-stage builds, or anything beyond a stock runtime image. The word \"build\" means Docker image build. It does not mean \"run my frontend build command\". How It Works Tar your project (must include a Dockerfile) Build the Docker image on the worker Start the container and wait for readiness Take a Playwright screenshot Cache the built image for fast re-runs Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ build_preview: { directory: \"/path/to/project\", // Must contain Dockerfile command: \"npm start\", port: 3000, build_args: { NODE_ENV: \"production\" }, keep_image_minutes: 30, // Cache the built image timeout: 180 // Includes build time } }) }); With Security Audit Enable audit: true to run a security scan on your code and dependencies. Returns a detailed report of findings, risk flags, and dependency inventory. Copy { build_preview: { directory: \"/path/to/project\", command: \"python server.py\", port: 8000, audit: true, // Security scan timeout: 180 } } // Response includes: // { // audit: { // security_findings: [...], // deps_extracted: { runtime: { pip: [...], npm: [...] } }, // code_summary: { files: [...], total_lines: 1234 }, // risk_flags: [{ level: \"info\", reason: \"...\" }] // } // } Additional Parameters Supports all riddle_server_preview parameters plus: Parameter Type Description build_args object Docker --build-arg key-value pairs keep_image_minutes number Cache built image on worker (default: 30, max: 120, 0 = delete immediately) audit boolean Run security audit on code + dependencies exclude string[] Glob patterns to exclude from tarball (default: [\".git\", \"*.log\"]) 💡 Image Caching Set keep_image_minutes: 30 to avoid rebuilding on every run. The built image stays on the worker, so subsequent runs skip the Docker build step entirely. Set to 0 if you always want a fresh build. ⚠️ Timeout includes build time The default timeout is 180 seconds. Complex multi-stage builds may need more — set timeout: 600 (max). Cached images skip the ",
        "scroll_width": 1280,
        "client_width": 1280,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          ".guide-page.preview-tools-page": {
            "count": 1,
            "visible_count": 1
          },
          ".code-block-wrapper": {
            "count": 10,
            "visible_count": 10
          },
          ".copy-button": {
            "count": 10,
            "visible_count": 10
          }
        },
        "frames": {},
        "text_sequences": {},
        "text_matches": {
          "text:Preview": true,
          "text:riddle_preview": true,
          "text:Application error": false
        },
        "http_statuses": {},
        "link_statuses": {},
        "setup_action_results": [
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 0,
            "selector": ".code-block-wrapper",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 1,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 2,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle Docs Proof MCP Pricing Blog Playground Sign Up Log In ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled la... (12580 chars)",
            "target_index": 0,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "clear_console",
            "ordinal": 3,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "cleared_console_event_count": 0,
            "cleared_page_error_count": 0
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 4,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-before",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-before"
          },
          {
            "ok": true,
            "action": "click",
            "ordinal": 5,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "count": 10,
            "target_index": 0,
            "text": "Copy"
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 6,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle Docs Proof MCP Pricing Blog Playground Sign Up Log In ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled la... (12583 chars)",
            "target_index": 0,
            "timeout_ms": 5000
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 7,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-after",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-desktop-docs-preview-code-copy-after"
          }
        ],
        "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-desktop"
      },
      {
        "name": "phone",
        "width": 390,
        "height": 844,
        "url": "https://riddledc.com/docs/preview/",
        "route": {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        },
        "title": "Preview Tools Guide - Deploy & Screenshot Any App | Riddle",
        "body_text_length": 12529,
        "body_text_sample": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle_preview server_preview build_preview Speed ~5s ~30–60s ~1–3min Server process No (static only) Yes Yes Docker image — Stock (pull) Custom (build) System packages — — ✓ Image caching — — ✓ (30 min default) Security audit — — ✓ Playwright scripts — ✓ ✓ Env vars — ✓ ✓ Preview URL ✓ (24hr) — — riddle_preview — Static Hosting Deploy built static files (HTML, CSS, JS) to an ephemeral URL at preview.riddledc.com. The preview expires after 24 hours. Best for SPAs, Vite builds, Next.js static exports, or any pre-built frontend. How It Works Tar your build output directory Upload to Riddle Get a live URL in ~5 seconds Screenshot it with any Riddle tool Basic Example Copied! const response = await fetch(\"https://api.riddledc.com/v1/preview\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ directory: \"/path/to/build/output\", framework: \"spa\" // \"spa\" (default) or \"static\" }) }); const { preview_url, id } = await response.json(); // preview_url: \"https://preview.riddledc.com/s/pv_a1b2c3d4/\" // id: \"pv_a1b2c3d4\" (use to delete early) Parameters Parameter Type Description directory string Absolute path to build output (must contain index.html) framework string \"spa\" (default) — all routes serve index.html. \"static\" — file-based routing. 💡 When to use riddle_preview Your app is already built and just needs hosting. You have a dist/ or out/ folder with static files. You want a shareable URL that works for 24 hours. Delete Early Previews auto-expire after 24 hours, but you can clean up immediately: Copy await fetch(\"https://api.riddledc.com/v1/preview/pv_a1b2c3d4\", { method: \"DELETE\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}` } }); riddle_server_preview — Server Apps Run a server-side application inside an isolated Docker container and screenshot it with Playwright. Uses stock images from Docker Hub — no Dockerfile needed. Perfect for Next.js, Express, Django, FastAPI, Rails, and any app that needs a running server process. How It Works Tar your project directory and upload it Pull a stock Docker image (e.g. node:20-slim) Start your server inside the container Wait for the readiness check to pass Take a Playwright screenshot Return the screenshot and logs Polling responses include a current phase, phase_updated_at, and sometimes phase_details. They also include progress with step, total, percent, label, and terminal fields for simple status displays. This makes stuck jobs easier to read: you can tell whether Riddle is pulling an image, extracting the upload, starting the server, waiting for readiness, running browser proof, collecting artifacts, or finalizing outputs. Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ server_preview: { directory: \"/path/to/my-express-app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, path: \"/dashboard\", env: { NODE_ENV: \"production\" }, viewport: { width: 1920, height: 1080 } } }) }); With Authentication & Custom Scripts Copy { server_preview: { directory: \"/path/to/app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, // Inject localStorage tokens before page loads localStorage: { \"auth_token\": \"eyJhbGciOiJIUzI1NiJ9...\", \"user_id\": \"12345\" }, // Sensitive env vars — stored securely, deleted after use sensitive_env: { DATABASE_URL: \"postgres://...\", API_SECRET: \"sk-...\" }, // Wait for React hydration before screenshotting wait_for_selector: \"#app[data-hydrated]\", // Run a Playwright script after server is ready script: ` await page.click('button.load-data'); await page.waitForSelector('.data-table'); await saveScreenshot('with-data'); `, // Emulate dark mode color_scheme: \"dark\", // Readiness check config readiness_path: \"/health\", readiness_timeout: 30, timeout: 120 } } Key Parameters Parameter Type Description directory string Path to your project image string Docker image to run (node:20-slim, python:3.12-slim, etc.) command string Command to start your server port number Port your server listens on path string URL path to screenshot (default: /) env object Non-sensitive environment variables sensitive_env object Secrets — stored securely, deleted after use localStorage object Key-value pairs injected before page load script string Playwright script to run after server is ready wait_for_selector string CSS selector to wait for before screenshotting color_scheme string \"dark\" or \"light\" — applied before navigation viewport object { width, height } — default 1920×1080 readiness_path string Health check endpoint to poll readiness_timeout number Seconds to wait for the server to answer before browser proof starts navigation_timeout number Seconds Playwright should wait for the target page after readiness passes timeout number Max execution time in seconds (default: 120) Common Docker Images Node.js node:20-slim Express, Fastify, Next.js, Nuxt Python python:3.12-slim Django, FastAPI, Flask Ruby ruby:3.3-slim Rails, Sinatra riddle_build_preview — Custom Builds Build a Docker image from your own Dockerfile, run the server, and screenshot it. Use this when you need custom system packages, compiled languages, multi-stage builds, or anything beyond a stock runtime image. The word \"build\" means Docker image build. It does not mean \"run my frontend build command\". How It Works Tar your project (must include a Dockerfile) Build the Docker image on the worker Start the container and wait for readiness Take a Playwright screenshot Cache the built image for fast re-runs Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ build_preview: { directory: \"/path/to/project\", // Must contain Dockerfile command: \"npm start\", port: 3000, build_args: { NODE_ENV: \"production\" }, keep_image_minutes: 30, // Cache the built image timeout: 180 // Includes build time } }) }); With Security Audit Enable audit: true to run a security scan on your code and dependencies. Returns a detailed report of findings, risk flags, and dependency inventory. Copy { build_preview: { directory: \"/path/to/project\", command: \"python server.py\", port: 8000, audit: true, // Security scan timeout: 180 } } // Response includes: // { // audit: { // security_findings: [...], // deps_extracted: { runtime: { pip: [...], npm: [...] } }, // code_summary: { files: [...], total_lines: 1234 }, // risk_flags: [{ level: \"info\", reason: \"...\" }] // } // } Additional Parameters Supports all riddle_server_preview parameters plus: Parameter Type Description build_args object Docker --build-arg key-value pairs keep_image_minutes number Cache built image on worker (default: 30, max: 120, 0 = delete immediately) audit boolean Run security audit on code + dependencies exclude string[] Glob patterns to exclude from tarball (default: [\".git\", \"*.log\"]) 💡 Image Caching Set keep_image_minutes: 30 to avoid rebuilding on every run. The built image stays on the worker, so subsequent runs skip the Docker build step entirely. Set to 0 if you always want a fresh build. ⚠️ Timeout includes build time The default timeout is 180 seconds. Complex multi-stage builds may need more — set timeout: 600 (max). Cached images skip the build step, so subsequent runs are much faster. Status",
        "scroll_width": 390,
        "client_width": 390,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          ".guide-page.preview-tools-page": {
            "count": 1,
            "visible_count": 1
          },
          ".code-block-wrapper": {
            "count": 10,
            "visible_count": 10
          },
          ".copy-button": {
            "count": 10,
            "visible_count": 10
          }
        },
        "frames": {},
        "text_sequences": {},
        "text_matches": {
          "text:Preview": true,
          "text:riddle_preview": true,
          "text:Application error": false
        },
        "http_statuses": {},
        "link_statuses": {},
        "setup_action_results": [
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 0,
            "selector": ".code-block-wrapper",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 1,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 2,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle... (12526 chars)",
            "target_index": 0,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "clear_console",
            "ordinal": 3,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "cleared_console_event_count": 1,
            "cleared_page_error_count": 0
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 4,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-before",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-before"
          },
          {
            "ok": true,
            "action": "click",
            "ordinal": 5,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "count": 10,
            "target_index": 0,
            "text": "Copy"
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 6,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle... (12529 chars)",
            "target_index": 0,
            "timeout_ms": 5000
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 7,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-after",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-phone-docs-preview-code-copy-after"
          }
        ],
        "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-phone"
      },
      {
        "name": "ipad-mini",
        "width": 768,
        "height": 1024,
        "url": "https://riddledc.com/docs/preview/",
        "route": {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        },
        "title": "Preview Tools Guide - Deploy & Screenshot Any App | Riddle",
        "body_text_length": 12529,
        "body_text_sample": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle_preview server_preview build_preview Speed ~5s ~30–60s ~1–3min Server process No (static only) Yes Yes Docker image — Stock (pull) Custom (build) System packages — — ✓ Image caching — — ✓ (30 min default) Security audit — — ✓ Playwright scripts — ✓ ✓ Env vars — ✓ ✓ Preview URL ✓ (24hr) — — riddle_preview — Static Hosting Deploy built static files (HTML, CSS, JS) to an ephemeral URL at preview.riddledc.com. The preview expires after 24 hours. Best for SPAs, Vite builds, Next.js static exports, or any pre-built frontend. How It Works Tar your build output directory Upload to Riddle Get a live URL in ~5 seconds Screenshot it with any Riddle tool Basic Example Copied! const response = await fetch(\"https://api.riddledc.com/v1/preview\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ directory: \"/path/to/build/output\", framework: \"spa\" // \"spa\" (default) or \"static\" }) }); const { preview_url, id } = await response.json(); // preview_url: \"https://preview.riddledc.com/s/pv_a1b2c3d4/\" // id: \"pv_a1b2c3d4\" (use to delete early) Parameters Parameter Type Description directory string Absolute path to build output (must contain index.html) framework string \"spa\" (default) — all routes serve index.html. \"static\" — file-based routing. 💡 When to use riddle_preview Your app is already built and just needs hosting. You have a dist/ or out/ folder with static files. You want a shareable URL that works for 24 hours. Delete Early Previews auto-expire after 24 hours, but you can clean up immediately: Copy await fetch(\"https://api.riddledc.com/v1/preview/pv_a1b2c3d4\", { method: \"DELETE\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}` } }); riddle_server_preview — Server Apps Run a server-side application inside an isolated Docker container and screenshot it with Playwright. Uses stock images from Docker Hub — no Dockerfile needed. Perfect for Next.js, Express, Django, FastAPI, Rails, and any app that needs a running server process. How It Works Tar your project directory and upload it Pull a stock Docker image (e.g. node:20-slim) Start your server inside the container Wait for the readiness check to pass Take a Playwright screenshot Return the screenshot and logs Polling responses include a current phase, phase_updated_at, and sometimes phase_details. They also include progress with step, total, percent, label, and terminal fields for simple status displays. This makes stuck jobs easier to read: you can tell whether Riddle is pulling an image, extracting the upload, starting the server, waiting for readiness, running browser proof, collecting artifacts, or finalizing outputs. Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ server_preview: { directory: \"/path/to/my-express-app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, path: \"/dashboard\", env: { NODE_ENV: \"production\" }, viewport: { width: 1920, height: 1080 } } }) }); With Authentication & Custom Scripts Copy { server_preview: { directory: \"/path/to/app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, // Inject localStorage tokens before page loads localStorage: { \"auth_token\": \"eyJhbGciOiJIUzI1NiJ9...\", \"user_id\": \"12345\" }, // Sensitive env vars — stored securely, deleted after use sensitive_env: { DATABASE_URL: \"postgres://...\", API_SECRET: \"sk-...\" }, // Wait for React hydration before screenshotting wait_for_selector: \"#app[data-hydrated]\", // Run a Playwright script after server is ready script: ` await page.click('button.load-data'); await page.waitForSelector('.data-table'); await saveScreenshot('with-data'); `, // Emulate dark mode color_scheme: \"dark\", // Readiness check config readiness_path: \"/health\", readiness_timeout: 30, timeout: 120 } } Key Parameters Parameter Type Description directory string Path to your project image string Docker image to run (node:20-slim, python:3.12-slim, etc.) command string Command to start your server port number Port your server listens on path string URL path to screenshot (default: /) env object Non-sensitive environment variables sensitive_env object Secrets — stored securely, deleted after use localStorage object Key-value pairs injected before page load script string Playwright script to run after server is ready wait_for_selector string CSS selector to wait for before screenshotting color_scheme string \"dark\" or \"light\" — applied before navigation viewport object { width, height } — default 1920×1080 readiness_path string Health check endpoint to poll readiness_timeout number Seconds to wait for the server to answer before browser proof starts navigation_timeout number Seconds Playwright should wait for the target page after readiness passes timeout number Max execution time in seconds (default: 120) Common Docker Images Node.js node:20-slim Express, Fastify, Next.js, Nuxt Python python:3.12-slim Django, FastAPI, Flask Ruby ruby:3.3-slim Rails, Sinatra riddle_build_preview — Custom Builds Build a Docker image from your own Dockerfile, run the server, and screenshot it. Use this when you need custom system packages, compiled languages, multi-stage builds, or anything beyond a stock runtime image. The word \"build\" means Docker image build. It does not mean \"run my frontend build command\". How It Works Tar your project (must include a Dockerfile) Build the Docker image on the worker Start the container and wait for readiness Take a Playwright screenshot Cache the built image for fast re-runs Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ build_preview: { directory: \"/path/to/project\", // Must contain Dockerfile command: \"npm start\", port: 3000, build_args: { NODE_ENV: \"production\" }, keep_image_minutes: 30, // Cache the built image timeout: 180 // Includes build time } }) }); With Security Audit Enable audit: true to run a security scan on your code and dependencies. Returns a detailed report of findings, risk flags, and dependency inventory. Copy { build_preview: { directory: \"/path/to/project\", command: \"python server.py\", port: 8000, audit: true, // Security scan timeout: 180 } } // Response includes: // { // audit: { // security_findings: [...], // deps_extracted: { runtime: { pip: [...], npm: [...] } }, // code_summary: { files: [...], total_lines: 1234 }, // risk_flags: [{ level: \"info\", reason: \"...\" }] // } // } Additional Parameters Supports all riddle_server_preview parameters plus: Parameter Type Description build_args object Docker --build-arg key-value pairs keep_image_minutes number Cache built image on worker (default: 30, max: 120, 0 = delete immediately) audit boolean Run security audit on code + dependencies exclude string[] Glob patterns to exclude from tarball (default: [\".git\", \"*.log\"]) 💡 Image Caching Set keep_image_minutes: 30 to avoid rebuilding on every run. The built image stays on the worker, so subsequent runs skip the Docker build step entirely. Set to 0 if you always want a fresh build. ⚠️ Timeout includes build time The default timeout is 180 seconds. Complex multi-stage builds may need more — set timeout: 600 (max). Cached images skip the build step, so subsequent runs are much faster. Status",
        "scroll_width": 768,
        "client_width": 768,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          ".guide-page.preview-tools-page": {
            "count": 1,
            "visible_count": 1
          },
          ".code-block-wrapper": {
            "count": 10,
            "visible_count": 10
          },
          ".copy-button": {
            "count": 10,
            "visible_count": 10
          }
        },
        "frames": {},
        "text_sequences": {},
        "text_matches": {
          "text:Preview": true,
          "text:riddle_preview": true,
          "text:Application error": false
        },
        "http_statuses": {},
        "link_statuses": {},
        "setup_action_results": [
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 0,
            "selector": ".code-block-wrapper",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 1,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 2,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle... (12526 chars)",
            "target_index": 0,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "clear_console",
            "ordinal": 3,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "cleared_console_event_count": 1,
            "cleared_page_error_count": 0
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 4,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-before",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-before"
          },
          {
            "ok": true,
            "action": "click",
            "ordinal": 5,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "count": 10,
            "target_index": 0,
            "text": "Copy"
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 6,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle... (12529 chars)",
            "target_index": 0,
            "timeout_ms": 5000
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 7,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-after",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini-docs-preview-code-copy-after"
          }
        ],
        "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-mini"
      },
      {
        "name": "ipad",
        "width": 820,
        "height": 1180,
        "url": "https://riddledc.com/docs/preview/",
        "route": {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        },
        "title": "Preview Tools Guide - Deploy & Screenshot Any App | Riddle",
        "body_text_length": 12529,
        "body_text_sample": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle_preview server_preview build_preview Speed ~5s ~30–60s ~1–3min Server process No (static only) Yes Yes Docker image — Stock (pull) Custom (build) System packages — — ✓ Image caching — — ✓ (30 min default) Security audit — — ✓ Playwright scripts — ✓ ✓ Env vars — ✓ ✓ Preview URL ✓ (24hr) — — riddle_preview — Static Hosting Deploy built static files (HTML, CSS, JS) to an ephemeral URL at preview.riddledc.com. The preview expires after 24 hours. Best for SPAs, Vite builds, Next.js static exports, or any pre-built frontend. How It Works Tar your build output directory Upload to Riddle Get a live URL in ~5 seconds Screenshot it with any Riddle tool Basic Example Copied! const response = await fetch(\"https://api.riddledc.com/v1/preview\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ directory: \"/path/to/build/output\", framework: \"spa\" // \"spa\" (default) or \"static\" }) }); const { preview_url, id } = await response.json(); // preview_url: \"https://preview.riddledc.com/s/pv_a1b2c3d4/\" // id: \"pv_a1b2c3d4\" (use to delete early) Parameters Parameter Type Description directory string Absolute path to build output (must contain index.html) framework string \"spa\" (default) — all routes serve index.html. \"static\" — file-based routing. 💡 When to use riddle_preview Your app is already built and just needs hosting. You have a dist/ or out/ folder with static files. You want a shareable URL that works for 24 hours. Delete Early Previews auto-expire after 24 hours, but you can clean up immediately: Copy await fetch(\"https://api.riddledc.com/v1/preview/pv_a1b2c3d4\", { method: \"DELETE\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}` } }); riddle_server_preview — Server Apps Run a server-side application inside an isolated Docker container and screenshot it with Playwright. Uses stock images from Docker Hub — no Dockerfile needed. Perfect for Next.js, Express, Django, FastAPI, Rails, and any app that needs a running server process. How It Works Tar your project directory and upload it Pull a stock Docker image (e.g. node:20-slim) Start your server inside the container Wait for the readiness check to pass Take a Playwright screenshot Return the screenshot and logs Polling responses include a current phase, phase_updated_at, and sometimes phase_details. They also include progress with step, total, percent, label, and terminal fields for simple status displays. This makes stuck jobs easier to read: you can tell whether Riddle is pulling an image, extracting the upload, starting the server, waiting for readiness, running browser proof, collecting artifacts, or finalizing outputs. Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ server_preview: { directory: \"/path/to/my-express-app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, path: \"/dashboard\", env: { NODE_ENV: \"production\" }, viewport: { width: 1920, height: 1080 } } }) }); With Authentication & Custom Scripts Copy { server_preview: { directory: \"/path/to/app\", image: \"node:20-slim\", command: \"npm start\", port: 3000, // Inject localStorage tokens before page loads localStorage: { \"auth_token\": \"eyJhbGciOiJIUzI1NiJ9...\", \"user_id\": \"12345\" }, // Sensitive env vars — stored securely, deleted after use sensitive_env: { DATABASE_URL: \"postgres://...\", API_SECRET: \"sk-...\" }, // Wait for React hydration before screenshotting wait_for_selector: \"#app[data-hydrated]\", // Run a Playwright script after server is ready script: ` await page.click('button.load-data'); await page.waitForSelector('.data-table'); await saveScreenshot('with-data'); `, // Emulate dark mode color_scheme: \"dark\", // Readiness check config readiness_path: \"/health\", readiness_timeout: 30, timeout: 120 } } Key Parameters Parameter Type Description directory string Path to your project image string Docker image to run (node:20-slim, python:3.12-slim, etc.) command string Command to start your server port number Port your server listens on path string URL path to screenshot (default: /) env object Non-sensitive environment variables sensitive_env object Secrets — stored securely, deleted after use localStorage object Key-value pairs injected before page load script string Playwright script to run after server is ready wait_for_selector string CSS selector to wait for before screenshotting color_scheme string \"dark\" or \"light\" — applied before navigation viewport object { width, height } — default 1920×1080 readiness_path string Health check endpoint to poll readiness_timeout number Seconds to wait for the server to answer before browser proof starts navigation_timeout number Seconds Playwright should wait for the target page after readiness passes timeout number Max execution time in seconds (default: 120) Common Docker Images Node.js node:20-slim Express, Fastify, Next.js, Nuxt Python python:3.12-slim Django, FastAPI, Flask Ruby ruby:3.3-slim Rails, Sinatra riddle_build_preview — Custom Builds Build a Docker image from your own Dockerfile, run the server, and screenshot it. Use this when you need custom system packages, compiled languages, multi-stage builds, or anything beyond a stock runtime image. The word \"build\" means Docker image build. It does not mean \"run my frontend build command\". How It Works Tar your project (must include a Dockerfile) Build the Docker image on the worker Start the container and wait for readiness Take a Playwright screenshot Cache the built image for fast re-runs Basic Example Copy const response = await fetch(\"https://api.riddledc.com/v1/run\", { method: \"POST\", headers: { \"Authorization\": `Bearer ${RIDDLE_API_KEY}`, \"Content-Type\": \"application/json\" }, body: JSON.stringify({ build_preview: { directory: \"/path/to/project\", // Must contain Dockerfile command: \"npm start\", port: 3000, build_args: { NODE_ENV: \"production\" }, keep_image_minutes: 30, // Cache the built image timeout: 180 // Includes build time } }) }); With Security Audit Enable audit: true to run a security scan on your code and dependencies. Returns a detailed report of findings, risk flags, and dependency inventory. Copy { build_preview: { directory: \"/path/to/project\", command: \"python server.py\", port: 8000, audit: true, // Security scan timeout: 180 } } // Response includes: // { // audit: { // security_findings: [...], // deps_extracted: { runtime: { pip: [...], npm: [...] } }, // code_summary: { files: [...], total_lines: 1234 }, // risk_flags: [{ level: \"info\", reason: \"...\" }] // } // } Additional Parameters Supports all riddle_server_preview parameters plus: Parameter Type Description build_args object Docker --build-arg key-value pairs keep_image_minutes number Cache built image on worker (default: 30, max: 120, 0 = delete immediately) audit boolean Run security audit on code + dependencies exclude string[] Glob patterns to exclude from tarball (default: [\".git\", \"*.log\"]) 💡 Image Caching Set keep_image_minutes: 30 to avoid rebuilding on every run. The built image stays on the worker, so subsequent runs skip the Docker build step entirely. Set to 0 if you always want a fresh build. ⚠️ Timeout includes build time The default timeout is 180 seconds. Complex multi-stage builds may need more — set timeout: 600 (max). Cached images skip the build step, so subsequent runs are much faster. Status",
        "scroll_width": 820,
        "client_width": 820,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          ".guide-page.preview-tools-page": {
            "count": 1,
            "visible_count": 1
          },
          ".code-block-wrapper": {
            "count": 10,
            "visible_count": 10
          },
          ".copy-button": {
            "count": 10,
            "visible_count": 10
          }
        },
        "frames": {},
        "text_sequences": {},
        "text_matches": {
          "text:Preview": true,
          "text:riddle_preview": true,
          "text:Application error": false
        },
        "http_statuses": {},
        "link_statuses": {},
        "setup_action_results": [
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 0,
            "selector": ".code-block-wrapper",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_selector",
            "ordinal": 1,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 2,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle... (12526 chars)",
            "target_index": 0,
            "timeout_ms": 30000
          },
          {
            "ok": true,
            "action": "clear_console",
            "ordinal": 3,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "cleared_console_event_count": 1,
            "cleared_page_error_count": 0
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 4,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-before",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-before"
          },
          {
            "ok": true,
            "action": "click",
            "ordinal": 5,
            "selector": ".copy-button",
            "frame_selector": null,
            "optional": false,
            "count": 10,
            "target_index": 0,
            "text": "Copy"
          },
          {
            "ok": true,
            "action": "wait_for_text",
            "ordinal": 6,
            "selector": "body",
            "frame_selector": null,
            "optional": false,
            "text": "Skip to main content Riddle ← Back to Docs Preview Tools Deploy any app to an ephemeral URL, screenshot it, and compare changes — all via API. Plain text for agents: /docs/preview/markdown.md Which Tool Should I Use? What are you deploying? Static files HTML, CSS, JS — already built ↓ riddle_preview ~5 seconds Server app Node, Python, Ruby — stock runtime ↓ riddle_server_preview ~30–60 seconds Custom build Dockerfile, system deps, compiled langs ↓ riddle_build_preview ~1–3 minutes Feature riddle... (12529 chars)",
            "target_index": 0,
            "timeout_ms": 5000
          },
          {
            "ok": true,
            "action": "screenshot",
            "ordinal": 7,
            "selector": null,
            "frame_selector": null,
            "optional": false,
            "label": "docs-preview-code-copy-after",
            "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad-docs-preview-code-copy-after"
          }
        ],
        "screenshot_label": "riddle-site-v525-docs-code-copy-clipboard-denial-ipad"
      }
    ],
    "console": {
      "events": [
        {
          "type": "warning",
          "text": "The resource https://riddledc.com/_next/static/css/2d6eac7c22a4fd3d.css was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally.",
          "location": {
            "url": "https://riddledc.com/docs/preview/",
            "lineNumber": 0,
            "columnNumber": 0
          }
        }
      ],
      "fatal_count": 0
    },
    "page_errors": [],
    "dialogs": [],
    "network_mocks": [],
    "dom_summary": {
      "expected_viewport_count": 4,
      "viewport_count": 4,
      "partial": false,
      "routes": [
        {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        },
        {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        },
        {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        },
        {
          "requested": "https://riddledc.com/docs/preview/",
          "observed": "/docs/preview/",
          "expected_path": "/docs/preview/",
          "matched": true,
          "http_status": 200
        }
      ],
      "titles": [
        "Preview Tools Guide - Deploy & Screenshot Any App | Riddle",
        "Preview Tools Guide - Deploy & Screenshot Any App | Riddle",
        "Preview Tools Guide - Deploy & Screenshot Any App | Riddle",
        "Preview Tools Guide - Deploy & Screenshot Any App | Riddle"
      ],
      "overflow_px": [
        0,
        0,
        0,
        0
      ],
      "bounds_overflow_px": [
        0,
        0,
        0,
        0
      ],
      "overflow_offender_counts": [
        0,
        0,
        0,
        0
      ],
      "frames": [
        {
          "viewport": "desktop",
          "selectors": []
        },
        {
          "viewport": "phone",
          "selectors": []
        },
        {
          "viewport": "ipad-mini",
          "selectors": []
        },
        {
          "viewport": "ipad",
          "selectors": []
        }
      ],
      "http_status": [],
      "link_status": [],
      "route_inventory": [],
      "network_mock_count": 0,
      "network_mock_hit_count": 0,
      "dialog_count": 0,
      "dialog_accept_count": 0,
      "dialog_dismiss_count": 0
    }
  },
  "riddle": {
    "job_id": "job_59dc499b",
    "status": "completed",
    "terminal": true
  }
}
