Skip to Content
DocumentationGuidesCreating Documents

Creating Documents

Documents are typed data objects displayed in the Loopstack Studio UI. They have a Zod schema for validation and a YAML config for rendering.

Basic Document

import { z } from 'zod'; import { Document } from '@loopstack/common'; export const NotesSchema = z.object({ text: z.string(), }); @Document({ schema: NotesSchema, uiConfig: __dirname + '/notes.ui.yaml', }) export class NotesDocument { text: string; }
# notes.ui.yaml type: document ui: widgets: - widget: form options: properties: text: title: Notes widget: textarea rows: 8

The @Document Decorator

@Document({ schema: NotesSchema, // Zod schema for validation uiConfig: __dirname + '/notes.ui.yaml', // Path to UI YAML config })
  • schema — Zod schema that validates document content
  • uiConfig — Path to YAML file defining how the document renders in the UI

Saving Documents

Use this.repository.save() inside workflow transition methods. Reference document classes directly — no injection needed.

// Create a new document await this.repository.save(NotesDocument, { text: 'Hello!' }); // Create/update with a specific ID await this.repository.save(NotesDocument, { text: 'Updated content' }, { id: 'notes-1' }); // With meta options await this.repository.save(NotesDocument, { text: 'Hidden note' }, { id: 'hidden', meta: { hidden: true } });

Save Options

OptionTypeDescription
idstringCustom ID — use for updating existing documents
meta.hiddenbooleanHide from the UI

Built-in Document Types

These are available without creating custom documents:

DocumentSourceKey Fields
LlmMessageDocumentrole, content@loopstack/llm-provider-module
LinkDocument@loopstack/commonlabel, workflowId, href, status
MessageDocument@loopstack/commonrole, content
MarkdownDocument@loopstack/commonmarkdown
PlainDocument@loopstack/commontext
ErrorDocument@loopstack/commonerror
import { LlmMessageDocument } from '@loopstack/llm-provider-module'; import { LinkDocument, MarkdownDocument } from '@loopstack/common'; await this.repository.save(LlmMessageDocument, { role: 'assistant', content: 'Hello! How can I help?', }); await this.repository.save(MarkdownDocument, { markdown: '# Report\n- Item 1\n- Item 2', });

YAML UI Configuration

Form Widget

The form widget renders document fields as an editable form:

type: document ui: widgets: - widget: form options: order: [name, description, items] properties: name: title: Name description: title: Description widget: textarea items: title: Items collapsed: true items: title: Item actions: - type: button transition: submit label: 'Submit'

Available Widget Types

Use these in options.properties.<field>.widget:

WidgetDescription
textSingle-line text input (default)
textareaMulti-line text area
selectDropdown select
radioRadio button group
checkboxCheckbox
switchToggle switch
sliderNumeric slider
code-viewCode editor with syntax highlighting

Property Options

OptionTypeDescription
titlestringDisplay label
widgetstringWidget type
placeholderstringPlaceholder text
rowsnumberVisible rows (textarea)
readonlybooleanRead-only field
hiddenbooleanHide the field
disabledbooleanDisable interaction
collapsedbooleanCollapse arrays/objects by default
itemsobjectUI config for array items

Document Actions

Buttons that trigger wait: true transitions in the workflow:

actions: - type: button transition: confirm # Must match the method name label: 'Confirm'

Tags

Categorize documents for filtering and searching:

type: document tags: - message - important

Tags are used by tools like llmGenerateText with messagesSearchTag to collect documents as conversation history.

Structured Output Example

Documents work with LlmGenerateObjectTool for AI-generated structured data:

export const FileDocumentSchema = z .object({ filename: z.string(), description: z.string(), code: z.string(), }) .strict(); @Document({ schema: FileDocumentSchema, uiConfig: __dirname + '/file-document.yaml', }) export class FileDocument { filename: string; description: string; code: string; }
# file-document.yaml type: document ui: widgets: - widget: form options: order: [filename, description, code] properties: filename: title: File Name readonly: true description: title: Description readonly: true code: title: Code widget: code-view

Used in a workflow:

const result = await this.llmGenerateObject.call({ model: 'claude-sonnet-4-6' }, outputSchema: toJSONSchema(FileDocumentSchema) as Record<string, unknown>, prompt: 'Generate a Hello World script in Python', });

Registry References

Last updated on