Validate: prototype tests

Validate lets product teams test a design before building it. You give testers a few tasks on a clickable prototype, and Guidely measures whether they succeed, how long it takes, where they click by mistake and which routes they take, then asks a few follow-up questions.

Dashboard: Prototype tests in the project sidebar (/app/<project>/prototypes).

Concepts

TermWhat it is
TestOne prototype, its tasks and its follow-up questions. draft → live → closed (and back to live with Reopen).
ScreenAn uploaded image (PNG, JPEG, GIF or WebP) of one state of the design.
HotspotA rectangle on a screen (stored as fractions of the image, so it scales with it) that takes the tester to another screen. A click anywhere else is a misclick.
TaskWhat the tester is asked to do ("Send an invoice to Acme"). It starts on a screen and succeeds when the tester reaches one of its success screens. Optional time limit.
Follow-up questionsAsked once, after the last task. They are guide survey questions (NPS, CSAT, rating, thumbs, single or multiple choice, open text, with conditions) and are edited with the same panel as surveys.
SessionOne tester taking the test: their task results, clicks and answers.

Who can do what: anyone in the workspace can read tests and results; creating, editing, launching and closing tests needs the Run prototype tests permission (prototypes.edit, editors by default). Creating the in-app recruitment guide also needs Edit guides.

Once a test is live, its screens, tasks and questions are locked so every tester sees the same thing and results stay comparable. Its name and the welcome and thank-you texts can still change. To change the design, Duplicate the test (the copy is a draft with a new link).

Launch checks

A test can't launch until: it has at least one task; every task has a prompt; for image tests, every screen has an image, every task's start screen exists, it has at least one success screen and one of them can be reached from the start screen by following hotspots; hotspots link to screens that exist; choice questions have at least two options. The editor lists what's missing.

Image prototypes vs Figma

Image prototypes. Upload screens (they're stored as project media, like article images, under the project's media storage, and count towards its 5 GB), then drag on a screen to draw a hotspot and pick where it goes. Hotspots can also be added and positioned with the keyboard (Add hotspot, then the position fields). Guidely draws the screens itself, so it knows exactly where each click lands: results include click maps (dots for hits and misclicks, plus a heat map) per screen, misclick rates, direct success (no misclicks, shortest route) and the most common paths.

Figma prototypes. Paste a prototype link (https://www.figma.com/proto/…, or a /design/… link to the same file); anyone with the link must be able to view it. Tasks name Figma frames by node id (12:345; in a frame's link it appears as node-id=12-345): an optional starting frame and one or more success frames. The tester page embeds the prototype with Figma's Embed Kit 2.0 (https://embed.figma.com/proto/…, hotspot hints off) and follows its events: PRESENTED_NODE_CHANGED builds the path and ends the task when a success frame is shown; MOUSE_PRESS_OR_RELEASE with handled: false counts a misclick. Messages are only accepted from https://www.figma.com and https://embed.figma.com and from the embedded frame itself.

Figma only sends these events to embeds that name a registered Figma app: set FIGMA_EMBED_CLIENT_ID to your Figma app's client id (and allow your Guidely origin in that app). Without it the prototype still works, but Guidely can't see navigation, so testers report success themselves with I'm done, and there are no paths or misclicks. Figma doesn't report click positions, so Figma tests have no click maps.

Sharing and recruiting

Share link. Every test has an unguessable link, <API origin>/validate/<token>. It works while the test is live; closed tests say so, drafts are not found. No sign-in is needed. The page is mobile friendly and not indexed by search engines.

Recruit in your app. Share → Create recruitment guide creates a draft modal guide ("Help shape what we build next" with Take the test / Not now) whose button opens the test link, and links it to the test. Target and publish it like any other guide. With the SDK installed, a button that opens a Guidely test URL shows the test in a full-screen overlay in your app instead of navigating away (see sdk.md), and the session is attributed to the end user who took it. The overlay closes when the tester finishes or closes it.

Results

/app/<project>/prototypes/<test>/results:

  • Testers, finished (and completion rate), overall task success.
  • Per task: success, give-up and direct-success rates, median and average time, misclick rate (share of clicks that missed every hotspot), and the most common routes (screens in order) with their outcome.
  • Click maps per screen (image tests), for all tasks or one task.
  • Follow-up answers, charted like survey results.
  • Every session: who (end user for in-app sessions; "Anonymous" for link testers), where from, each task's outcome, time and misclicks.

Results are also available to AI assistants through the MCP tools list_prototype_tests and get_prototype_test_results (read-only).

Privacy: what is collected

For each tester, Guidely stores:

  • a random session token (only its SHA-256 hash is stored) that the tester's browser uses to report results;
  • per task: the outcome, the time taken, the screens visited and every click on the prototype (screen and position as fractions of the screen, time since the task started, whether it hit a hotspot); at most 500 clicks and 200 screens per task;
  • the answers to the follow-up questions;
  • for in-app sessions only: the environment, the anonymous id and the identify() user id the SDK passed, and the matching end user of the same project.

Nothing else is recorded: no screen recording, keystrokes, IP address, user agent or page URL of the tester's device. Attribution only applies when the public key belongs to the test's own project; ids sent with any other key are dropped, so results can't be attached to another project's users. The SDK sends attribution in the URL fragment (#key=…&anon=…&uid=…), which browsers never send to servers, and the page removes it from the address bar after reading it. In-app sessions are deleted with the end user (Delete user data). Deleting a test deletes its sessions and results; its screen images are then removed by the nightly unused-media sweep (which keeps images any test still uses).

Frameable page

Every Guidely page refuses to be framed by other sites (X-Frame-Options: SAMEORIGIN) except the how-to embeds (/embed/*) and the tester page (/validate/*), which send Content-Security-Policy: frame-ancestors * so customer apps can show the test in the overlay. The tester page shows only the published test (no author or project data beyond the project's brand name and colour), uses Referrer-Policy: no-referrer, and when framed tells its parent to close with window.parent.postMessage({ type: "guidely:prototype:close" }, "*") (the message carries no data). The SDK accepts that message only from the test frame and the test's origin.

API

Management (session cookie, same as the rest of the dashboard API):

MethodPath
GET / POST/api/v1/projects/:id/prototype-testsList (`?status=draft\live\closed\all) / create { name, source: "images"\"figma", figmaUrl? }`
GET / PATCH / DELETE/api/v1/prototype-tests/:testIdRead / update { name?, description?, intro?, thankYou?, source?, figmaUrl?, screens?, tasks?, questions? } / delete
POST/api/v1/prototype-tests/:testId/duplicateDraft copy with a new link
POST/api/v1/prototype-tests/:testId/launch, /close, /reopenStatus changes (launch runs the launch checks; 400 lists the problems in details.problems)
POST/api/v1/prototype-tests/:testId/recruitCreates (or returns) the draft recruitment guide: { guideId, created }
GET/api/v1/prototype-tests/:testId/results?taskId=Aggregated results (click maps for one task with taskId)
GET/api/v1/prototype-tests/:testId/sessions?page=Tester sessions, newest first

Screens are uploaded with the project media endpoint (POST /api/v1/projects/:id/media, raw image body) and referenced by their media id; the server fills in each screen's image URL and refuses ids from other projects.

Public tester API (no login, Access-Control-Allow-Origin: *, never reads cookies, Cache-Control: no-store; live tests only, otherwise 404 for drafts and unknown tokens and 409 once closed):

MethodPathBody
POST/api/v1/validate/:token/sessions{ key?, anonymousId?, userId? }Starts a session: { sessionToken }. 30 per IP per 10 minutes; 10,000 per test.
POST/api/v1/validate/:token/results`{ sessionToken, taskId, outcome: "success"\"gave_up", durationMs, clicks: [{ screenId, x, y, t, hit }], path }`One task attempt. Idempotent per session and task (the first result wins; retries get duplicate: true). For image tests the server recomputes every hit from the hotspots and records a "success" whose path doesn't end on a success screen as a give-up. Accepted for 12 hours after the session started. 128 KB max.
POST/api/v1/validate/:token/complete{ sessionToken, answers: { [questionId]: value } }Finishes the session with the follow-up answers, validated against the questions (types, options, required and conditional questions). Idempotent.