---
title: "Get a form"
method: GET
path: "/forms/{formId}"
tags: ["Forms"]
---

# Get a form

`GET /forms/{formId}`

## Request

- Base URL: `https://api.clipform.io/v1`
- URL: `https://api.clipform.io/v1/forms/{formId}`
- Auth: HTTP bearer

Returns a form and its nodes.

## Path parameters

- `formId` string, uuid, required

## Response `200`

Form details

- FormWithNodes
  - `form_id` string, uuid
  - `title` string
  - `is_live` boolean: Whether the form is online for respondents (separate from publish state).
  - `has_unpublished_changes` boolean: True when the live working copy has drifted from the published snapshot respondents currently see. Cleared by publishing (sending `is_live: true`, which re-freezes the snapshot).
  - `published_version_id` string, uuid, nullable: The currently published form_version, or null if never published.
  - `nodes` Node[]
    - `id` string, uuid
    - `type` 'choice' | 'open' | 'button' | 'details' | 'payment' | 'file_download' | 'redirect' | 'end_screen'
    - `prompt` string
    - `label` string
    - `required` boolean
    - `config` object
    - `options` object[]
      - `id` string, uuid
      - `content` string
      - `order` integer
      - `score` integer, nullable: Single-category point value (simple quiz scoring)
      - `scores` object, nullable: Per-category scoring map (multi-outcome quizzes)
    - `media_id` string, uuid, nullable: Linked node_media row id (returned in builder views - include=canvas/logic/media)
  - `variables` FormVariable[]: The form's stable variable registry (#4173) - category/custom scoring variables a conditional branch can route on by id. Built-ins (score, max_score) are reserved keys, not rows here. Present only with `include=logic`.
    - `id` string, uuid
    - `key` string: Immutable internal identifier, frozen at creation. Never rewritten - a rename only edits `label`.
    - `label` string: Editable display name.
    - `type` 'number' | 'text'
    - `kind` 'builtin' | 'category' | 'custom'
  - `urls` object: Dashboard/viewer deeplinks for this form (#3405). An anonymous caller gets only `viewer` (a live link, or a scoped preview link for a draft); a non-anonymous caller gets every dashboard tab plus `viewer` when the form is live.
    - `viewer` string, uri: Live respondent link, or a scoped preview link for an anonymous caller's draft.
    - `builder` string, uri: Build tab - the node/canvas editor.
    - `summary` string, uri: Summary tab - form overview and key stats.
    - `responses` string, uri: Responses tab - individual submissions.
    - `analytics` string, uri: Analytics tab - views, starts, completion rate, drop-off per node.
    - `share` string, uri: Share tab - link, QR, social.
    - `embed` string, uri: Embed setup for websites.
    - `dynamic_link` string, uri: Dynamic/personalized link setup.

## Other responses

- `429`: Rate limit exceeded.

---

[API](https://skmtc.dev/clipform/apis/clipform-api.md) · [All operations](https://skmtc.dev/clipform/apis/clipform-api/llms.txt) · [OpenAPI document](https://skmtc.dev/clipform/apis/clipform-api/revisions/490de83af482?raw)
