What You Can Build
Production-ready patterns, just a few lines of Hot
::demo::agents ns
::store ::hot::store
LeadQualifier
meta {
doc: "Qualifies inbound leads, scores them, and routes to sales or nurture.",
agent: {
name: "Lead Qualifier",
tags: ["sales", "ai"],
},
}
type { threshold: Dec, model: Str }
qualifier LeadQualifier({threshold: 0.7, model: "claude-sonnet"})
leads ::store/Map({name: "demo:leads"})
on-signup
meta {
doc: "Accepts inbound signups via webhook and queues them for qualification",
agent: LeadQualifier,
webhook: {service: "leads", path: "/signup"},
}
fn (request) {
lead or(request.body, request.data)
::store/put(leads, or(lead.email, Uuid()), lead)
send("lead:new", lead)
{ok: true}
}
qualify-lead
meta {
doc: "Scores a lead and routes to sales if qualified, nurture otherwise",
agent: LeadQualifier,
on-event: "lead:new",
}
fn (event) {
lead event.data
score add(mul(rand(), 0.6), 0.3)
if(gte(score, qualifier.threshold),
send("lead:qualified", {email: lead.email, score: score}),
send("lead:nurture", {email: lead.email, score: score}))
}
notify-sales
meta {
doc: "Posts a Slack alert when a lead scores above threshold",
agent: LeadQualifier,
on-event: "lead:qualified",
}
fn (event) {
send("slack:post", {
channel: "#sales",
text: `Hot lead: ${event.data.email} (score ${round(mul(event.data.score, 100))})`,
})
}
weekly-pipeline
meta {
doc: "Generates a weekly pipeline summary and posts it to Slack",
agent: LeadQualifier,
schedule: "every monday at 9am",
}
fn (event) {
total add(20, rand-int(30))
qualified add(5, rand-int(10))
send("slack:post", {
channel: "#sales",
text: `Pipeline: ${total} leads, ${qualified} qualified this week`,
})
}
::demo::mcp ns
::store ::hot::store
lookup-order
meta {
mcp: {service: "support"},
doc: "Look up order by ID"
}
fn (order-id: Str): Map {
::store/get("orders", order-id)
}
cancel-order
meta {
mcp: {service: "support"},
doc: "Cancel an order if not yet shipped"
}
fn (order-id: Str): Map {
order ::store/get("orders", order-id)
if(eq(order.status, "shipped"), fail("Cannot cancel shipped order"))
::store/put("orders", order-id, {...order, status: "cancelled"})
{id: order-id, status: "cancelled"}
}
::demo::events ns
on-order-placed
meta {on-event: "order:placed"}
fn (event) {
order event.data
parallel {
inventory send("inventory:reserve", {items: order.items})
receipt send("email:send", {
to: order.customer.email,
subject: `Order #${order.id} confirmed`,
})
notify send("slack:post", {
channel: "#orders",
text: `New order #${order.id} from ${order.customer.name}`,
})
}
}
::demo::box ns
::box ::hot::box
run-analysis
meta {on-event: "data:analyze"}
fn (event) {
csv-path or(event.data.file, "hot://demo/orders.csv")
escaped-csv-path replace(csv-path, "'", "'\\''")
script ```
hotbox cp '${escaped-csv-path}' /data/data.csv
python3 -c "
import csv, json, statistics
rows = list(csv.DictReader(open('/data/data.csv')))
a = [float(r['amount']) for r in rows]
print(json.dumps({k: round(v, 2) for k, v in {
'rows': len(rows), 'total': sum(a),
'mean': statistics.mean(a), 'median': statistics.median(a)
}.items()}))
"
```
result ::box/start({
image: "python:3.12-alpine",
script,
timeout: 60,
size: "small",
})
::hot::task/await(result.id)
from-json(trim(or(result.stdout, "{}")))
}
::demo::tasks ns
::box ::hot::box
::task ::hot::task
process-upload
meta {on-event: "demo:process-file"}
fn (event) {
jobs parallel {
info ::box/start({
image: "alpine:latest",
script: "echo '{\"width\":1920,\"height\":1080}'",
timeout: 30,
size: "nano",
})
hash ::box/start({
image: "alpine:latest",
script: "echo -n 'demo' | sha256sum | cut -d' ' -f1",
timeout: 30,
size: "nano",
})
}
results parallel {
info ::task/await(jobs.info.id)
hash ::task/await(jobs.hash.id)
}
{
metadata: from-json(trim(or(results.info.stdout, "{}"))),
hash: trim(or(results.hash.stdout, "")),
}
}
::demo::video ns
on-video-upload
meta {on-event: "video:uploaded"}
fn (event) {
path or(event.data.path, "hot://demo/test.mp4")
parallel {
probe ::ffmpeg/probe(path)
thumb ::ffmpeg/thumbnail(path)
audio ::ffmpeg/extract-audio(path)
}
}
::demo::screenshot ns
capture
meta {on-event: "page:capture"}
fn (event) {
url or(event.data.url, "https://hot.dev/docs")
results parallel {
shot ::playwright/screenshot(url)
page ::playwright/scrape(url)
}
{
url: url,
screenshot: results.shot.url,
title: results.page.title,
text: slice(results.page.text, 0, 500),
}
}
::demo::schedules ns
daily-report
meta {
schedule: "daily at 6am",
on-event: "demo:daily-report"
}
fn (event) {
stats {
users: {total: add(1000, rand-int(500)), new_today: add(10, rand-int(40))},
revenue: {today: add(5000, rand-int(3000)), mrr: add(42000, rand-int(8000))},
}
send("slack:post", {
channel: "#metrics",
text: `Users: ${stats.users.total} (+${stats.users.new_today} new) | Revenue: $${stats.revenue.today} | MRR: $${stats.revenue.mrr}`,
})
}
::demo::files ns
generate-report
meta {on-event: "report:generate"}
fn (event) {
orders event.data.orders
header "id,customer,amount,status"
rows orders |> map((o) { `${o.id},${o.customer},${o.amount},${o.status}` })
csv join([header, ...rows], "\n")
path `hot://reports/orders-${::hot::time/format(::hot::time/now(), "yyyy-MM-dd")}.csv`
write-file(path, csv)
{file: path, rows: length(orders)}
}
::demo::webhooks ns
on-webhook
meta {
webhook: {service: "demo", path: "/demo-hook"},
on-event: "demo:webhook"
}
fn (request) {
event-type or(request.body.type, request.data.type)
payload or(request.body.data.object, request.data.data.object)
cond {
eq(event-type, "payment_intent.succeeded") => {
send("payment:received", {customer: payload.customer, amount: payload.amount})
}
eq(event-type, "payment_intent.payment_failed") => {
send("payment:failed", {customer: payload.customer})
}
=> { {status: "ignored", type: event-type} }
}
}
::demo::retries ns
::http ::hot::http
fetch-data
meta {
on-event: "demo:fetch-data",
retry: {attempts: 5, delay: 2000}
}
fn (event) {
result ::http/post(or(event.data.url, "https://httpbin.org/post"), {source: "demo"})
if(not(::http/is-ok-response(result)), fail(`Request failed: ${result.status}`))
{
status: result.status,
bytes: length(to-json(result.body)),
}
}
How It Works
Install Hot
One command install
curl -fsSL https://get.hot.dev/install.sh | sh
irm https://get.hot.dev/install.ps1 | iex
Also available via Homebrew, installers, and more
Initialize a new Hot project
Add to any existing codebase
cd dev/so-fire-backend/
hot init
Hot lives alongside your existing code. Adds hot.hot config, hot/ source directory, and .hot/ local data
Write some Hot code
in hot/src
::so-fire::user ns send-welcome-email meta { on-event: "user:created", doc: "Send a welcome email when user is created" } fn (event) { ::resend::emails/send-email( POSTEmailsRequest({ from: "hello@so-fire.app", to: [event.data.user.email], subject: "Welcome to SoFire!", html: "<p>Welcome to SoFire!</p>" }) ) }
Run the dev servers
App, API, Scheduler, Worker — all in one command
hot dev --open
Watch your workflow running
Real-time observability at localhost:4680
Runs
Every execution captured. Status, timing, input, output, full trace.
Events
See every event, its payload, and which handlers processed it.
Execution Trace
Each expression, intermediate values, return values, timing.
Errors
Failures, cancellations, stack traces. Debug fast.
Deploy to Hot Cloud
Optional — when you're ready to go live
Start your free Hot Cloud account and get your API key, then...
hot deploy
The Hot App
Real-time observability for your workflows
The Platform
Everything you need from local dev to production
Core
AI Agents
Build autonomous AI agents with tool use, memory, and MCP integration
MCP Tools
Turn any Hot function into an MCP Tool for AI agents
::hot::box
Run any container as a Hot function
Tasks
Long-running asynchronous execution with checkpoints and recovery
Event Handlers
Trigger functions automatically when events occur
Hot Packages
Pre-built integrations for AI and SaaS services
Webhooks
Turn any Hot function into a Webhook endpoint
Scheduled Runs
Run functions on a cron or natural-language schedule
HTTP API
OpenAPI-compatible REST endpoints
File Storage
Read, write, and manage files in your environment
Configurable Retries
Retry failed runs with configurable attempts, delay, and backoff
Execution Tracing
Every expression traced with intermediate values, timing, and full call stack
Hot App
Real-time dashboard for runs, events, streams, and execution traces
Environments
Setup separate environments for staging, production, or whatever you need
Teams
Invite team members with role-based access to projects and environments
The Hot Language
Familiar Data Literals
If you know JSON, you know Hot's data syntax. Maps, vectors, strings, numbers — all familiar.
Types Are Optional
Add types where they help, skip them where they don't. Autocomplete and error checking when you want it.
Flows Control Execution
Parallel, conditional, and pipe patterns built into the language. Clean workflow logic.
Run Control
Fail fast, cancel gracefully. Built-in primitives for controlling workflow execution.
Standard Library
Rich built-in functions for collections, strings, math, HTTP, and more. Ready to use.
Development Tools
Hot CLI
Run, test, and deploy from the command line
VS Code & LSP
Syntax highlighting, autocomplete, diagnostics
Testing Framework
Built-in test runner for unit and integration tests
Security
Secret Variables
Encrypted secrets and config with AES-256-GCM
API Keys
Create, rotate, and manage API keys
Service Keys & Sessions
Scoped credentials and short-lived tokens for your customers
Granular Permissions
Fine-grained resource and action-level access control
Cloud
Hot Cloud
Deploy with one command, scale automatically
Alerts
Get notified via email, Slack, PagerDuty, or webhooks when events occur
Custom Domains
Serve your API, MCP, and Webhooks from your own custom domains
Connect Your Services
Pre-built integrations for the tools you use
Hot Takes
Hot Dev's newsletter for product updates, tutorials, and demos.
Start building with Hot
Free to start. Ship your first workflow today.
Get Started Free