{
  "version": "riddle-proof.profile-result.v1",
  "profile_name": "riddle-site-v492-docs-markdown-code-entity",
  "runner": "riddle",
  "status": "passed",
  "baseline_policy": "invariant_only",
  "route": {
    "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
    "observed": "/s/ps_0a64108e/docs/markdown.md",
    "expected_path": "/docs/markdown.md",
    "matched": true,
    "http_status": 200
  },
  "artifacts": {
    "screenshots": [
      "riddle-site-v492-docs-markdown-code-entity-desktop",
      "riddle-site-v492-docs-markdown-code-entity-phone",
      "riddle-site-v492-docs-markdown-code-entity-ipad-mini",
      "riddle-site-v492-docs-markdown-code-entity-ipad"
    ],
    "console": "console.json",
    "proof_json": "proof.json",
    "dom_summary": "dom-summary.json"
  },
  "checks": [
    {
      "type": "route_loaded",
      "label": "route_loaded",
      "status": "passed",
      "evidence": {
        "expected_path": "/docs/markdown.md",
        "observed_paths": [
          "/s/ps_0a64108e/docs/markdown.md",
          "/s/ps_0a64108e/docs/markdown.md",
          "/s/ps_0a64108e/docs/markdown.md",
          "/s/ps_0a64108e/docs/markdown.md"
        ],
        "http_statuses": [
          200,
          200,
          200,
          200
        ]
      }
    },
    {
      "type": "http_status",
      "label": "agent docs markdown has decoded code fences",
      "status": "passed",
      "evidence": {
        "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
        "method": "GET",
        "allowed_statuses": [
          200
        ],
        "require_nonzero_bytes": false,
        "min_bytes": 1000,
        "allowed_content_types": [
          "text/markdown",
          "text/plain",
          "text/html"
        ],
        "viewports": [
          {
            "viewport": "desktop",
            "key": "GET https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
            "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
            "method": "GET",
            "status": 200,
            "status_text": "OK",
            "ok": true,
            "error": null,
            "content_type": "text/markdown",
            "content_length": 17789,
            "bytes": 17789,
            "body_contains": {
              "Script Helpers": true,
              "const data = await page.evaluate(() => window.appState);": true,
              "}).then(r => r.json());": true
            },
            "body_contains_missing": [],
            "body_not_contains": {
              "=&gt;": false,
              "&#123;": false,
              "&#125;": false
            },
            "body_not_contains_found": [],
            "body_not_patterns": null,
            "body_not_patterns_found": [],
            "body_sample": "# API Documentation\n\nOne endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default.\n\nGPT Actions OpenAPI:\n[/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml)\n(\n[JSON](/riddledc-actions-openapi.json)\n,\n[.well-known](/.well-known/riddledc-actions-openapi.yaml)\n)\n\nNew to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode).\n\n## Quick Start\n\n### Your First Screenshot\n\n```\ncurl -X POST \"https://api.riddledc.com/v1/run\" \\\\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\\\n  -H \"Content-Type: application/json\" \\\\\n  -d '{\"url\": \"https://example.com\"}' \\\\\n  -o screenshot.png\n```\n\nGet your API key from [your dashboard](/dashboard). PNG bytes returned directly.\n\n### Create Account\n\n[Sign up](/register)\n\n### Get API Key\n\n[Dashboard](/dashboard)\n\n### Make Request\n\nSee examples below\n\n## Guides\n\n- [Riddle Proof](/docs/riddle-proof): evidence-bac",
            "failures": []
          }
        ],
        "failures": []
      }
    },
    {
      "type": "text_visible",
      "label": "text_visible",
      "status": "passed",
      "evidence": {
        "text": "Script Helpers",
        "matches": [
          true,
          true,
          true,
          true
        ]
      }
    },
    {
      "type": "text_visible",
      "label": "text_visible",
      "status": "passed",
      "evidence": {
        "text": "Async Mode",
        "matches": [
          true,
          true,
          true,
          true
        ]
      }
    },
    {
      "type": "selector_text_absent",
      "label": "selector_text_absent",
      "status": "passed",
      "evidence": {
        "selector": "body",
        "text": "=&gt;",
        "pattern": null,
        "viewports": [
          {
            "viewport": "desktop",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          },
          {
            "viewport": "phone",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          },
          {
            "viewport": "ipad-mini",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          },
          {
            "viewport": "ipad",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          }
        ]
      }
    },
    {
      "type": "selector_text_absent",
      "label": "selector_text_absent",
      "status": "passed",
      "evidence": {
        "selector": "body",
        "text": "&#123;",
        "pattern": null,
        "viewports": [
          {
            "viewport": "desktop",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          },
          {
            "viewport": "phone",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          },
          {
            "viewport": "ipad-mini",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          },
          {
            "viewport": "ipad",
            "selector_count": 1,
            "visible_count": 1,
            "matched_count": 0,
            "matched": false,
            "samples": []
          }
        ]
      }
    },
    {
      "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": 0,
        "allowed_console_warning_count": 0,
        "allowed_console_texts": [],
        "allowed_console_patterns": [],
        "unallowed_console_warning_samples": [],
        "allowed_console_warning_samples": []
      }
    }
  ],
  "summary": "riddle-site-v492-docs-markdown-code-entity passed 8 check(s) across 4 viewport(s) (desktop, phone, ipad-mini, ipad).",
  "captured_at": "2026-05-17T12:50:32.896Z",
  "evidence": {
    "version": "riddle-proof.profile-evidence.v1",
    "profile_name": "riddle-site-v492-docs-markdown-code-entity",
    "target_url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
    "baseline_policy": "invariant_only",
    "captured_at": "2026-05-17T12:50:32.896Z",
    "viewports": [
      {
        "name": "desktop",
        "width": 1280,
        "height": 900,
        "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
        "route": {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        },
        "title": "",
        "body_text_length": 17039,
        "body_text_sample": "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just ",
        "scroll_width": 1280,
        "client_width": 1280,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          "body": {
            "count": 1,
            "visible_count": 1
          }
        },
        "frames": {},
        "text_sequences": {
          "body": {
            "count": 1,
            "visible_count": 1,
            "texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "visible_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ],
            "visible_match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ]
          }
        },
        "text_matches": {
          "text:Script Helpers": true,
          "text:Async Mode": true
        },
        "http_statuses": {
          "GET https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md": {
            "version": "riddle-proof.http-status.v1",
            "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
            "method": "GET",
            "status": 200,
            "ok": true,
            "error": null,
            "request_body_bytes": 0,
            "allowed_statuses": [
              200
            ],
            "require_nonzero_bytes": false,
            "min_bytes": 1000,
            "allowed_content_types": [
              "text/markdown",
              "text/plain",
              "text/html"
            ],
            "redirected": false,
            "final_url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
            "content_type": "text/markdown",
            "content_length": 17789,
            "status_text": "OK",
            "bytes": 17789,
            "body_sample": "# API Documentation\n\nOne endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default.\n\nGPT Actions OpenAPI:\n[/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml)\n(\n[JSON](/riddledc-actions-openapi.json)\n,\n[.well-known](/.well-known/riddledc-actions-openapi.yaml)\n)\n\nNew to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode).\n\n## Quick Start\n\n### Your First Screenshot\n\n```\ncurl -X POST \"https://api.riddledc.com/v1/run\" \\\\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\\\n  -H \"Content-Type: application/json\" \\\\\n  -d '{\"url\": \"https://example.com\"}' \\\\\n  -o screenshot.png\n```\n\nGet your API key from [your dashboard](/dashboard). PNG bytes returned directly.\n\n### Create Account\n\n[Sign up](/register)\n\n### Get API Key\n\n[Dashboard](/dashboard)\n\n### Make Request\n\nSee examples below\n\n## Guides\n\n- [Riddle Proof](/docs/riddle-proof): evidence-bac",
            "body_contains": {
              "Script Helpers": true,
              "const data = await page.evaluate(() => window.appState);": true,
              "}).then(r => r.json());": true
            },
            "body_not_contains": {
              "=&gt;": false,
              "&#123;": false,
              "&#125;": false
            }
          }
        },
        "link_statuses": {},
        "setup_action_results": [],
        "screenshot_label": "riddle-site-v492-docs-markdown-code-entity-desktop"
      },
      {
        "name": "phone",
        "width": 390,
        "height": 844,
        "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
        "route": {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        },
        "title": "",
        "body_text_length": 17039,
        "body_text_sample": "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just ",
        "scroll_width": 390,
        "client_width": 390,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          "body": {
            "count": 1,
            "visible_count": 1
          }
        },
        "frames": {},
        "text_sequences": {
          "body": {
            "count": 1,
            "visible_count": 1,
            "texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "visible_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ],
            "visible_match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ]
          }
        },
        "text_matches": {
          "text:Script Helpers": true,
          "text:Async Mode": true
        },
        "http_statuses": {},
        "link_statuses": {},
        "setup_action_results": [],
        "screenshot_label": "riddle-site-v492-docs-markdown-code-entity-phone"
      },
      {
        "name": "ipad-mini",
        "width": 768,
        "height": 1024,
        "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
        "route": {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        },
        "title": "",
        "body_text_length": 17039,
        "body_text_sample": "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just ",
        "scroll_width": 768,
        "client_width": 768,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          "body": {
            "count": 1,
            "visible_count": 1
          }
        },
        "frames": {},
        "text_sequences": {
          "body": {
            "count": 1,
            "visible_count": 1,
            "texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "visible_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ],
            "visible_match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ]
          }
        },
        "text_matches": {
          "text:Script Helpers": true,
          "text:Async Mode": true
        },
        "http_statuses": {},
        "link_statuses": {},
        "setup_action_results": [],
        "screenshot_label": "riddle-site-v492-docs-markdown-code-entity-ipad-mini"
      },
      {
        "name": "ipad",
        "width": 820,
        "height": 1180,
        "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
        "route": {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        },
        "title": "",
        "body_text_length": 17039,
        "body_text_sample": "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just ",
        "scroll_width": 820,
        "client_width": 820,
        "overflow_px": 0,
        "bounds_overflow_px": 0,
        "overflow_offenders": [],
        "selectors": {
          "body": {
            "count": 1,
            "visible_count": 1
          }
        },
        "frames": {},
        "text_sequences": {
          "body": {
            "count": 1,
            "visible_count": 1,
            "texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "visible_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) ,"
            ],
            "match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ],
            "visible_match_texts": [
              "# API Documentation One endpoint: `/v1/run`. Send Playwright scripts or JSON, get screenshots. Sync by default. GPT Actions OpenAPI: [/riddledc-actions-openapi.yaml](/riddledc-actions-openapi.yaml) ( [JSON](/riddledc-actions-openapi.json) , [.well-known](/.well-known/riddledc-actions-openapi.yaml) ) New to Riddle? Start with [Script Mode](#script-mode) - write Playwright, send it to our API, get results. For simpler jobs, use [URL mode](#url-mode) or [Steps mode](#steps-mode). ## Quick Start ### Your First Screenshot ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' \\\\ -o screenshot.png ``` Get your API key from [your dashboard](/dashboard). PNG bytes returned directly. ### Create Account [Sign up](/register) ### Get API Key [Dashboard](/dashboard) ### Make Request See examples below ## Guides - [Riddle Proof](/docs/riddle-proof): evidence-backed workflows for agent-authored browser changes. - [Preview Modes](/docs/preview): choose static preview, server preview, or build preview. ## Authentication Two options: API keys (recommended) or JWT tokens. Using MCP? See [Riddle MCP modes and setup](/mcp). ### API Keys (Recommended) Create from your [Dashboard](/dashboard) or via API: ``` # Create an API key curl -X POST \"https://api.riddledc.com/billing/api-keys\" \\\\ -H \"Authorization: Bearer $JWT_TOKEN\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"name\": \"Production Key\"}' # Response { \"apiKey\": \"rdc_live_abc123********************xyz789\", \"keyId\": \"key_0844fb72799dd7b3\", \"warning\": \"Save this key now - you will not be able to see it again!\" } ``` Use in the Authorization header: ``` Authorization: Bearer rdc_live_abc123********************xyz789 ``` ### JWT Tokens (Alternative) Authenticate via AWS Cognito for short-lived tokens: ``` curl -X POST \"https://cognito-idp.us-east-1.amazonaws.com/\" \\\\ -H \"Content-Type: application/x-amz-json-1.1\" \\\\ -H \"X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth\" \\\\ -d '{ \"AuthFlow\": \"USER_PASSWORD_AUTH\", \"ClientId\": \"7u1bt3r20v613j7eaki9dkbhve\", \"AuthParameters\": { \"USERNAME\": \"your-email@example.com\", \"PASSWORD\": \"your-password\" } }' # Use the IdToken from the response ``` **When to use which:** API keys for production (never expire, can rotate). JWT for quick testing (expires in 1 hour). ## Input Modes Four ways to use `/v1/run`. Pick based on complexity. | Mode | Use Case | Example | | --- | --- | --- | | url | Single screenshot | `{ \"url\": \"https://example.com\" }` | | urls | Batch screenshots | `{ \"urls\": [\"https://a.com\", \"https://b.com\"] }` | | script | Full Playwright (recommended) | `{ \"script\": \"await page.goto(...)\" }` | | steps | JSON workflows (simple automation) | `{ \"steps\": [{ \"goto\": \"...\" }, { \"click\": \"...\" }] }` | ### URL Mode Simplest option. Returns PNG directly. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{\"url\": \"https://example.com\"}' -o screenshot.png ``` ### Batch Mode Multiple URLs in one job. More cost-efficient. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"urls\": [ \"https://example.com\", \"https://example.com/pricing\", \"https://example.com/docs\" ] }' ``` ### Script Mode (Recommended) Full Playwright API. Write the same code you'd write locally - we run it on our infrastructure. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"script\": \"await page.goto('\\\\''https://example.com'\\\\''); const title = await page.title(); console.log('\\\\''Title:'\\\\''', title); await saveScreenshot('\\\\''homepage'\\\\''');\" }' ``` Script Helpers Scripts can wait on window hooks and emit structured JSON artifacts. ``` await waitForWindow(\"appReady\", 30000); const data = await page.evaluate(() => window.appState); await saveJson(\"state\", data); ``` To return a JSON value directly in the response, set `options.returnResult: true` in the request. ### Steps Mode JSON-based workflows. Good for simple automation--no code generation needed. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"steps\": [ { \"goto\": \"https://example.com/login\" }, { \"fill\": { \"selector\": \"#email\", \"value\": \"user@example.com\" } }, { \"fill\": { \"selector\": \"#password\", \"value\": \"secret\" } }, { \"click\": \"button[type=submit]\" }, { \"waitForUrl\": \"**/dashboard**\" }, { \"screenshot\": \"dashboard\" } ] }' ``` ### Common Options These work with any input mode: ``` { \"url\": \"https://example.com\", \"timeout_sec\": 60, \"options\": { \"viewport\": { \"width\": 1920, \"height\": 1080 }, \"fullPage\": true, \"cookies\": [{ \"name\": \"session\", \"value\": \"abc\", \"domain\": \"example.com\" }], \"headers\": { \"Authorization\": \"Bearer APP_TOKEN\" }, \"localStorage\": { \"token\": \"xyz\" } } } ``` ### Stealth Mode Enable stealth mode to use the Patchright engine, which bypasses common bot detection systems including Cloudflare, Vercel, and Datadome. Available on any tool - just add `\"stealth\": true`. **Trade-off:** stealth mode disables console capture, so you won't get browser console logs in the response. ``` curl -X POST \"https://api.riddledc.com/v1/run\" \\\\ -H \"Authorization: Bearer YOUR_API_KEY\" \\\\ -H \"Content-Type: application/json\" \\\\ -d '{ \"url\": \"https://protected-site.com\", \"stealth\": true, \"options\": { \"fullPage\": true } }' ``` ## Steps Reference All available step types for `steps` mode. | Step | Description | Example | | --- | --- | --- | | goto | Navigate to URL | `{ \"goto\": \"https://example.com\" }` | | screenshot | Capture screenshot | `{ \"screenshot\": \"step1\" }` | | click | Click element | `{ \"click\": \"button.submit\" }` | | fill | Fill input field | `{ \"fill\": { \"selector\": \"#email\", \"value\": \"test@example.com\" } }` | | type | Type key-by-key | `{ \"type\": { \"selector\": \"#search\", \"text\": \"query\" } }` | | select | Select dropdown | `{ \"select\": { \"selector\": \"select#country\", \"value\": \"US\" } }` | | check | Check checkbox | `{ \"check\": \"#agree\" }` | | uncheck | Uncheck checkbox | `{ \"uncheck\": \"#newsletter\" }` | | hover | Hover element | `{ \"hover\": \".dropdown\" }` | | press | Press key | `{ \"press\": \"Enter\" }` | | waitFor | Wait for selector | `{ \"waitFor\": \".loaded\" }` | | waitForUrl | Wait for URL match | `{ \"waitForUrl\": \"**/success**\" }` | | waitForTimeout | Wait milliseconds | `{ \"waitForTimeout\": 2000 }` | | waitForLoadState | Wait for load state | `{ \"waitForLoadState\": \"networkidle\" }` | | assert | Assert conditions | `{ \"assert\": [{ \"selectorExists\": \".success\" }] }` | | saveHtml | Save page HTML | `{ \"saveHtml\": \"page\" }` | | log | Log message | `{ \"log\": \"Starting flow\" }` | | eval | Run Playwright code | `{ \"eval\": \"await page.evaluate(() => localStorage.clear())\" }` | ### Assertions Check page state without screenshotting. Abort early on failure. ``` { \"steps\": [ { \"goto\": \"https://example.com/checkout\" }, { \"assert\": [{ \"selectorExists\": \".checkout-form\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"fill\": { \"selector\": \"#card\", \"value\": \"4242424242424242\" } }, { \"click\": \"button.pay\" }, { \"assert\": [{ \"urlIncludes\": \"/confirmation\" }], \"onFail\": [\"screenshot\", \"abort\"] }, { \"screenshot\": \"confirmation\" } ] } // Assertion types: selectorExists, selectorMissing, urlIncludes, urlExcludes, textContains ``` ### Eval Context The `eval` step runs in Node.js with Playwright's `page` object. Browser code is auto-wrapped: ``` // Both work - browser code auto-wrapped: { \"eval\": \"document.title\" } { \"eval\": \"await page.evaluate(() => document.title)\" } // Playwright APIs work directly: { \"eval\": \"await page.click('.submit')\" } ``` ## Screenshot Options Control what and how you capture. ### Simple (Full Page) ``` { \"screenshot\": \"homepage\" } ``` ### Element Screenshot Capture just "
            ]
          }
        },
        "text_matches": {
          "text:Script Helpers": true,
          "text:Async Mode": true
        },
        "http_statuses": {},
        "link_statuses": {},
        "setup_action_results": [],
        "screenshot_label": "riddle-site-v492-docs-markdown-code-entity-ipad"
      }
    ],
    "console": {
      "events": [],
      "fatal_count": 0
    },
    "page_errors": [],
    "dialogs": [],
    "network_mocks": [],
    "dom_summary": {
      "expected_viewport_count": 4,
      "viewport_count": 4,
      "partial": false,
      "routes": [
        {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        },
        {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        },
        {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        },
        {
          "requested": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
          "observed": "/s/ps_0a64108e/docs/markdown.md",
          "expected_path": "/docs/markdown.md",
          "matched": true,
          "http_status": 200
        }
      ],
      "titles": [
        "",
        "",
        "",
        ""
      ],
      "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": [
        {
          "viewport": "desktop",
          "requests": [
            {
              "key": "GET https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
              "url": "https://preview.riddledc.com/s/ps_0a64108e/docs/markdown.md",
              "method": "GET",
              "status": 200,
              "ok": true,
              "error": null
            }
          ]
        }
      ],
      "link_status": [],
      "route_inventory": [],
      "network_mock_count": 0,
      "network_mock_hit_count": 0,
      "dialog_count": 0,
      "dialog_accept_count": 0,
      "dialog_dismiss_count": 0
    }
  }
}