Skip to Content
DocumentationBuilding with Loopstack️DocumentsCreating Documents

Creating Documents

Documents are structured data containers in Loopstack that enable user input, data validation, and form rendering in the Loopstack Studio. They’re essential for capturing and displaying structured information within your workflows.

Creating a Document

A document is created by decorating a class with @Document and implementing DocumentInterface. Documents define their content schema using the @Input decorator with a Zod schema, and optionally include UI configuration in a YAML file for form rendering.

Basic Document Definition

import { z } from 'zod'; import { Document, Input } from '@loopstack/common'; export const TaskDocumentSchema = z.object({ title: z.string().min(1, "Title is required"), description: z.string().optional(), dueDate: z.string().datetime(), priority: z.enum(['low', 'medium', 'high']).default('medium'), assignees: z.array(z.string()).default([]), }); @Document({ configFile: __dirname + '/task.document.yaml', }) export class TaskDocument { @Input({ schema: TaskDocumentSchema, }) content: z.infer<typeof TaskDocumentSchema>; }

Key Components

@Document Decorator

  • Links the document class to its YAML configuration via configFile
  • Defines metadata and configuration for the document

@Input() Decorator

  • Defines the document’s content schema and validation rules using a Zod schema
  • The schema property accepts a Zod object schema
  • The decorated content property holds the document’s data at runtime

YAML Config

  • ui.form.order - Controls the display order of fields in the Studio
  • ui.form.properties - Defines how each field is rendered in the frontend
  • widget - Specifies the input type (text, textarea, select, date, etc.)
  • readonly - Makes fields display-only when set to true
  • collapsed - Collapses array fields by default when set to true
  • items - Configures rendering of individual items in array fields
  • ui.actions - Defines interactive buttons that trigger workflow transitions

Registering the Document

Documents are registered in workflows using the @InjectDocument() decorator and must be provided in your module.

import { z } from 'zod'; import { Workflow, InjectDocument, InjectTool, Input, State, Runtime, } from '@loopstack/common'; import { CreateDocument } from '@loopstack/core-ui-module'; import { TaskDocument, TaskDocumentSchema } from './documents/task.document'; @Workflow({ configFile: __dirname + '/task-workflow.yaml', }) export class TaskWorkflow { @InjectTool() createDocument: CreateDocument; @InjectDocument() taskDocument: TaskDocument; @Input({ schema: z.object({ userId: z.string(), }), }) args: { userId: string; }; @State({ schema: z.object({ task: TaskDocumentSchema.optional(), }), }) state: { task?: z.infer<typeof TaskDocumentSchema>; }; @Runtime() runtime: any; }

Using Documents in Workflows

Creating Document Instances

Use the createDocument tool to instantiate documents in your workflow:

title: "Task Management Workflow" description: A workflow for creating and managing tasks. transitions: - id: initialize_task from: start to: task_created call: - tool: createDocument args: id: task document: taskDocument update: content: title: "Review quarterly reports" description: "Analyze Q4 performance metrics" dueDate: "2025-12-31T23:59:59Z" priority: "high" assignees: ["alice@example.com", "bob@example.com"]

Using Template Variables

Documents can use workflow arguments and state variables with template syntax:

transitions: - id: create_personalized_task from: start to: task_created call: - tool: createDocument args: id: task document: taskDocument update: content: title: "Task for {{ args.userId }}" priority: "medium"

Storing Document Output in State

Use assign to store document data in workflow state for later use:

transitions: - id: generate_task from: start to: task_generated call: - tool: aiGenerateDocument args: llm: provider: openai model: gpt-4o response: document: taskDocument prompt: | Create a task for reviewing the quarterly report. assign: task: ${{ result.data.content }} - id: display_task from: task_generated to: end call: - tool: createDocument args: id: summary document: messageDocument update: content: role: assistant parts: - type: text text: | Created task: {{ state.task.title }} Due: {{ state.task.dueDate }}

Capturing User Input

Combine documents with manual transitions to collect user input through forms. Use trigger: manual to pause the workflow and wait for user interaction, and access the submitted data via runtime.transition.payload:

transitions: - id: display_form from: start to: awaiting_input call: - tool: createDocument args: id: taskForm document: taskDocument update: content: title: "" priority: "medium" - id: process_input from: awaiting_input to: task_saved trigger: manual call: - tool: createDocument args: id: taskForm document: taskDocument update: content: ${{ runtime.transition.payload }} assign: task: ${{ result.data.content }}

Available UI Widgets

Documents support various widget types for different data formats:

WidgetDescriptionBest For
textSingle-line text inputShort text, names, titles, email, url, numbers
textareaMulti-line text input with fixed heightDescriptions, notes, long text
textarea-expandMulti-line text input with expandable heightLong-form content that varies in length
selectDropdown selectionSingle choice from predefined options
radioRadio button groupSingle choice with visible options
checkboxBoolean checkboxYes/no, true/false values
switchToggle switchEnable/disable, on/off states
sliderNumeric sliderNumeric values within a defined range
code-viewCode display fieldRead-only code snippets or formatted text
Last updated on