Swirls Docs
Agentic systems as code. Write .swirls files locally and deploy to Swirls Cloud.
Swirls is the deployment target for agentic systems. Describe agents, workflows, triggers, and auth in .swirls files. You can author and inspect those files locally, then deploy them to Swirls Cloud.
Data has SQL. Infrastructure has Terraform. Agents have Swirls.
Example workflow
This workflow accepts a contact form submission, summarizes it with an LLM, and sends a confirmation email. Workflows can span multiple .swirls files. The compiler composes them into a single workflow.
schema contact_payload {
label: "Contact submission"
schema: @json {
{
"type": "object",
"required": ["name", "email", "message"],
"properties": {
"name": { "type": "string", "title": "Name" },
"email": { "type": "string", "title": "Email" },
"message": { "type": "string", "title": "Message" }
},
"additionalProperties": false
}
}
}
form contact_form {
label: "Contact Form"
enabled: true
schema: contact_payload
}
workflow process_form {
label: "Process Form"
root {
type: code
label: "Normalize"
inputSchema: contact_payload
outputSchema: contact_payload
code: @ts {
const { name, email, message } = context.nodes.root.input
return { name: name.trim(), email: email.toLowerCase(), message: message.trim() }
}
}
node summarize {
type: ai
label: "Summarize"
model: "google/gemini-2.5-flash"
kind: object
schema: @json {
{ "type": "object", "required": ["text"], "properties": { "text": { "type": "string" } } }
}
prompt: @ts {
const { name, email, message } = context.nodes.root.output
return `Summarize in one sentence:\n\nFrom: ${name} <${email}>\n\n${message}`
}
}
node notify {
type: email
label: "Send confirmation"
to: @ts { return context.nodes.root.output.email }
subject: @ts { return "We received your message" }
text: @ts {
return `Thanks for reaching out. Summary: ${context.nodes.summarize.output.text}`
}
}
flow {
root -> summarize
summarize -> notify
}
}
trigger on_contact {
form:contact_form -> process_form
enabled: true
}Five ideas
Sixteen top-level blocks, five ideas. Learn the five and the rest is reference:
- Agents: actors that reason. Chat with them, give them tools, put them inside workflows.
- Workflows: deterministic procedures. Triggered by forms, webhooks, and schedules; available to agents as tools.
- Memory: streams for structured output, disks for files, the Postgres you already have.
- Connections: how your system talks to the outside world, from secrets you hold to brokered OAuth with no keys at all.
- Access: who may reach in. Roles derived from identity, grants declared in the file.
The Primitives page maps all of it on one screen, and From intent to primitives translates plain-language goals into blocks.
What else you get
- Every node type:
ai,agent,code,http,email,postgres,scrape,stream,bucket,disk,workflow,parallel,switch,wait,map,while - Inline TypeScript blocks (
@ts { … }) with LSP-powered IntelliSense - Triggers: connect forms, webhooks, and schedules to workflows
- Human-in-the-loop: add
review: { enabled: true }to any node to pause for approval - Local authoring with validation, type generation, and visual inspection
- Cloud deployment via
git pushorswirls deploy. No CI/CD required.
When to use Swirls
- AI content pipelines: draft, review, and publish content with LLM nodes and human review gates
- Lead enrichment and scoring: enrich form submissions via HTTP, score with an LLM, route to CRM
- Support ticket triage: classify urgency with a switch node, draft responses with an LLM
- Automated reporting: schedule workflows to query data streams and deliver formatted summaries
- Form-driven workflows: collect structured input, process it with AI, notify via email or webhook
Quick reference
Code node: run TypeScript, read upstream outputs via context.nodes.
node normalize {
type: code
label: "Normalize email"
code: @ts {
const { email } = context.nodes.root.input
return { email: email.trim().toLowerCase() }
}
}AI node: prompt an LLM and get a typed response.
node classify {
type: ai
label: "Classify intent"
model: "google/gemini-2.5-flash"
kind: object
schema: @json {
{ "type": "object", "required": ["intent"], "properties": { "intent": { "type": "string" } } }
}
prompt: @ts {
return `Classify the intent of: ${context.nodes.root.input.message}`
}
}Switch node: route execution based on a TypeScript expression.
node route {
type: switch
label: "Route by urgency"
cases: ["urgent", "normal", "low"]
router: @ts {
const body = context.nodes.root.output.body.toLowerCase()
if (body.includes("urgent")) return "urgent"
if (body.length > 500) return "normal"
return "low"
}
}
flow {
root -> route
route -["urgent"]-> handle_urgent
route -["normal"]-> handle_normal
route -["low"]-> handle_low
}Next steps
- Quickstart: deploy a
.swirlsproject in 5 minutes - Primitives: the whole language in five ideas
- From intent to primitives: plain-language goals, mapped to blocks
- Writing Swirls: syntax, embedded code, and common mistakes
- Platform: author locally and deploy to the cloud
- Cookbook: copy-pasteable recipes for common workflows
- Reference: SDK, node types, and CLI reference