Clipform Nodes API

Add, edit, and remove the nodes inside a form, including routing logic.

GitHub OpenAPI

Operations 3

POST /forms/{formId}/nodes Add a node #
PATCH /forms/{formId}/nodes/{nodeId} Update a node #
DELETE /forms/{formId}/nodes/{nodeId} Delete a node #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/clipform-io:clipform-io-nodes-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

clipform-io-nodes-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Clipform Io Nodes API
  version: 1.0.0
  contact:
    name: Clipform
    url: https://clipform.io
    email: support@clipform.io
  termsOfService: https://clipform.io/terms
  description: 'Operations tagged Nodes across 2 of this provider''s published API definitions: api.clipform.io_openapi.json, clipform-io-openapi.yml. Each path carries the servers of the definition it was published in.'
servers:
- url: https://api.clipform.io/v1
  description: Production
  x-internal: false
tags:
- name: Nodes
  x-displayName: Nodes
  description: Add, edit, and remove the nodes inside a form, including routing logic.
paths:
  /forms/{formId}/nodes:
    post:
      operationId: addNode
      tags:
      - Nodes
      summary: Add a node
      description: Adds a node to a form.
      parameters:
      - name: formId
        in: path
        required: true
        schema:
          type: string
          format: uuid
          maxLength: 36
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - node
              properties:
                node:
                  $ref: '#/components/schemas/NodeInput'
                after_node_id:
                  type: string
                  format: uuid
                  description: Insert after this node. Omit to append before end screen. Ignored when auto_arrange is false.
                  maxLength: 36
                auto_arrange:
                  type: boolean
                  default: true
                  description: When true (default), the new node is wired into the linear logic chain and the canvas is flagged for auto-layout. Set false for manual builder creates so the node lands orphaned (unconnected) and the caller controls its position.
      responses:
        '201':
          description: Node added
          content:
            application/json:
              schema:
                type: object
                properties:
                  node_id:
                    type: string
                    format: uuid
                    maxLength: 36
                  warnings:
                    type: array
                    description: Advisory notices, returned only when present - e.g. inserting a terminal node (end_screen/redirect/link_list/ shopping) via after_node_id truncated the flow there, orphaning whatever was previously downstream.
                    items:
                      type: string
                      maxLength: 2000
                    maxItems: 500
        '429':
          $ref: '#/components/responses/RateLimited'
    servers:
    - url: https://api.clipform.io/v1
      description: Production
      x-internal: false
  /forms/{formId}/nodes/{nodeId}:
    patch:
      operationId: updateNode
      tags:
      - Nodes
      summary: Update a node
      description: Updates a node.
      parameters:
      - name: formId
        in: path
        required: true
        schema:
          type: string
          format: uuid
          maxLength: 36
      - name: nodeId
        in: path
        required: true
        schema:
          type: string
          format: uuid
          maxLength: 36
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                prompt:
                  type: string
                  maxLength: 500
                type:
                  $ref: '#/components/schemas/NodeType'
                required:
                  type: boolean
                public_results_hidden:
                  type: boolean
                  description: Hide this node's data from the public results portal without affecting the owner Results tab.
                config:
                  type: object
                  description: Type-specific configuration, merged shallowly into the stored config. A null value deletes that key; nested objects are replaced wholesale. When type is changed in the same request, config replaces the stored configuration instead of merging.
                options:
                  type: array
                  maxItems: 50
                  items:
                    type: object
                    required:
                    - content
                    properties:
                      id:
                        type: string
                        format: uuid
                        description: Existing option id to update in place. Omit to insert a new option.
                        maxLength: 36
                      content:
                        type: string
                        maxLength: 200
                      score:
                        type: number
                      scores:
                        type: object
                        additionalProperties:
                          type: number
                  description: 'Upsert-by-id: the node''s options are set to this array. Items with a known id are updated in place (ids preserved), items without one are inserted, and existing options absent from the array are deleted. Order follows array position. Omitting all ids performs a full replace. Routes (node_logic actions) that reference a removed option are cleaned up; paused routes to surviving options are kept. The response then includes the cleaned `logic` actions. Each node type has its own option limit. A write that grows the list past the limit returns 400; re-saving or shrinking a list that is already over the limit succeeds.'
      responses:
        '200':
          description: Node updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  node_id:
                    type: string
                    format: uuid
                    maxLength: 36
                  options:
                    type: array
                    description: The node's resulting option set (real ids, in order), returned only when the request updated options. Lets callers reconcile optimistic temp ids without a refetch.
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                          maxLength: 36
                        content:
                          type: string
                          maxLength: 2000
                        order:
                          type: integer
                          format: int32
                          minimum: 0
                          maximum: 2147483647
                        score:
                          type: number
                          nullable: true
                        scores:
                          type: object
                          nullable: true
                          additionalProperties:
                            type: number
                    maxItems: 500
                  logic:
                    type: array
                    description: The node's logic actions after stale option references were cleaned up. Returned when options were updated and the node's logic row was read and pruned successfully. Omitted when the node has no logic row or the prune read or write failed.
                    items:
                      type: object
                      additionalProperties: true
                    maxItems: 500
                  variables:
                    type: array
                    description: The form's full stable variable registry (#4173), returned only when this request's options wrote a scores map - a new category key is auto-registered as a stable row so a conditional branch can route on its id.
                    items:
                      $ref: '#/components/schemas/FormVariable'
                    maxItems: 200
                  warnings:
                    type: array
                    description: Advisory notices, returned only when present - e.g. scored options were written to a node whose choice.record_scores is explicitly false.
                    items:
                      type: string
                      maxLength: 2000
                    maxItems: 500
        '429':
          $ref: '#/components/responses/RateLimited'
    delete:
      operationId: deleteNode
      tags:
      - Nodes
      summary: Delete a node
      description: Deletes a node and re-links the ones around it, and on a live form the node stays on the live form until the next publish.
      parameters:
      - name: formId
        in: path
        required: true
        schema:
          type: string
          format: uuid
          maxLength: 36
      - name: nodeId
        in: path
        required: true
        schema:
          type: string
          format: uuid
          maxLength: 36
      security:
      - bearerAuth: []
      responses:
        '204':
          description: Node deleted
        '429':
          $ref: '#/components/responses/RateLimited'
    servers:
    - url: https://api.clipform.io/v1
      description: Production
      x-internal: false
components:
  schemas:
    Error:
      type: object
      description: Standard error envelope returned by every failed request.
      required:
      - code
      - error
      properties:
        code:
          type: string
          description: Machine-readable error code. Keep in lockstep with the ErrorCode union in apps/api/src/lib/errors.ts (source of truth).
          enum:
          - VALIDATION_ERROR
          - UNKNOWN_SESSION
          - RATE_LIMITED
          - UNAUTHORIZED
          - FORBIDDEN
          - NOT_FOUND
          - INTERNAL_ERROR
          - RENDER_FAILED
          - PAYMENT_ERROR
          - BOOKING_ERROR
          - PLAN_LIMIT
          - RESPONSE_LIMIT
          - CONFLICT
        error:
          type: string
          description: Human-readable error message.
          maxLength: 2000
        details:
          description: 'Optional structured detail; present on some validation errors. The fallback 401 ("Authentication required...") carries `details.reason` for a rejected Bearer session token: `no_token`, `token_rejected`, `auth_unavailable`, `not_a_member`, `no_workspace`, `membership_lookup_failed`, or `not_evaluated`, plus `auth_code` and `auth_status` when the auth provider returned them.'
    NodeType:
      type: string
      enum:
      - choice
      - open
      - button
      - details
      - draw
      - camera
      - payment
      - file_download
      - redirect
      - end_screen
    FormVariable:
      type: object
      description: One row of a form's stable variable registry (#4173) - a category or custom scoring variable a conditional branch can route on by its stable id instead of a mutable name. Built-in variables (score, max_score) are reserved keys resolved specially and never appear here.
      properties:
        id:
          type: string
          format: uuid
          maxLength: 36
        key:
          type: string
          description: Immutable internal identifier, frozen at creation. Never rewritten - a rename only edits `label`.
          maxLength: 200
        label:
          type: string
          description: Editable display name.
          maxLength: 200
        type:
          type: string
          enum:
          - number
          - text
        kind:
          type: string
          enum:
          - builtin
          - category
          - custom
    NodeInput:
      type: object
      required:
      - type
      - prompt
      properties:
        id:
          type: string
          format: uuid
          description: Optional client-generated node id (RF-style stable identity). When provided the node is inserted with this id so the builder's optimistic node needs no temp-id swap. Omit to let the server generate one.
          maxLength: 36
        type:
          $ref: '#/components/schemas/NodeType'
        prompt:
          type: string
          maxLength: 500
          description: The text shown to the respondent
        label:
          type: string
          description: Internal short label used in exports and reports (defaults to prompt)
          maxLength: 2000
        required:
          type: boolean
          default: true
        config:
          type: object
          description: 'Type-specific configuration. Key options by type:

            - choice: { choice, selection_mode, allow_text_response, randomise_options, show_option_count, option_display, content_media_type }

            - open: { formats, content_media_type, max_recording_seconds }

            - button: { button_text, button_style }

            - details: { title, fields, description, consent_items }

            - draw: { title, description, paper_color, stroke_colors, stroke_widths, diagram }

            - camera: { title, description, camera_facing, allowed_capture, camera_capture, allow_library_upload, capture_order, max_files, min_files }

            - payment: { amount, currency, provider, workspace_integration_id, title, description }

            - file_download: { files, button_text, description }

            - redirect: { url, auto_redirect }

            - end_screen: { title, message, show_score, icon, show_share_button, cta_type, cta_text, cta_url, score_ranges, scoring_results }

            '
        options:
          type: array
          maxItems: 100
          items:
            type: object
            required:
            - content
            properties:
              id:
                type: string
                format: uuid
                description: Client-generated option id (optional). When provided it's used as the row id so the builder's optimistic option is the persisted row (no temp id swap). Omit to let the server generate one.
                maxLength: 36
              content:
                type: string
                maxLength: 200
              score:
                type: integer
                nullable: true
                description: Points awarded when this option is selected. Use `scores` instead to score across multiple named categories.
                format: int32
                minimum: 0
                maximum: 2147483647
              scores:
                type: object
                additionalProperties:
                  type: integer
                  format: int32
                  minimum: 0
                  maximum: 2147483647
                nullable: true
                description: Per-category scoring map. Keys are category names, values are points. -1 = knockout.
          description: Answer options for choice/button nodes
  responses:
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Access-Control-Allow-Origin:
          description: CORS origin permitted to read the response.
          schema:
            type: string
            maxLength: 2048
            example: '*'
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
            format: int32
            minimum: 0
            maximum: 86400
          example: 42
        RateLimit-Limit:
          description: Request quota for the current window.
          schema:
            type: integer
            format: int32
            minimum: 0
            maximum: 1000000
          example: 60
        RateLimit-Remaining:
          description: Requests remaining in the current window.
          schema:
            type: integer
            format: int32
            minimum: 0
            maximum: 1000000
          example: 0
        RateLimit-Reset:
          description: Seconds until the current window resets.
          schema:
            type: integer
            format: int32
            minimum: 0
            maximum: 86400
          example: 42
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key (cf_*) passed as Bearer token
    oauth2:
      type: oauth2
      description: Authorization-code OAuth for installed apps acting on a user's workspace. Access tokens (cf_at_*) are passed as Bearer tokens and authorize the Integrations operations. They are opaque, single-audience, server-side records. Client credentials are issued per app - contact support@clipform.io.
      flows:
        authorizationCode:
          authorizationUrl: https://api.clipform.io/oauth/authorize
          tokenUrl: https://api.clipform.io/oauth/token
          refreshUrl: https://api.clipform.io/oauth/token
          scopes:
            forms:read: Read the workspace's forms.
            responses:read: Read the workspace's form responses.
            webhooks:manage: Subscribe and unsubscribe webhook endpoints.
x-refined-from:
- api.clipform.io_openapi.json
- clipform-io-openapi.yml