Light Dark

What You Can Build

Production-ready patterns, just a few lines of Hot

agents.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`,
    })
}
mcp.hot
::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"}
}
events.hot
::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}`,
        })
    }
}
containers.hot Hot Box
::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, "{}")))
}
tasks.hot
::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, "")),
    }
}
ffmpeg.hot Pkg
::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)
    }
}
playwright.hot Pkg
::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),
    }
}
schedules.hot
::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}`,
    })
}
file-storage.hot
::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)}
}
webhooks.hot
::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} }
    }
}
retries.hot
::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

1

Install Hot

One command install

terminal
curl -fsSL https://get.hot.dev/install.sh | sh
PowerShell
irm https://get.hot.dev/install.ps1 | iex

Also available via Homebrew, installers, and more

2

Initialize a new Hot project

Add to any existing codebase

terminal
cd dev/so-fire-backend/
hot init

Hot lives alongside your existing code. Adds hot.hot config, hot/ source directory, and .hot/ local data

3

Write some Hot code

in hot/src

hot/src/so-fire/user.hot
::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>"
    })
  )
}
4

Run the dev servers

App, API, Scheduler, Worker — all in one command

terminal
hot dev --open
5

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.

6

Deploy to Hot Cloud

Optional — when you're ready to go live

Start your free Hot Cloud account and get your API key, then...

terminal
hot deploy

The Hot App

Real-time observability for your workflows

Dashboard
Hot App Dashboard
Stream Graph
Hot App Stream Graph
Execution Trace
Hot App Execution Trace
Hot App Dashboard Hot App Stream Graph Hot App Execution Trace

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

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

Ready to ship?

Start for free.
Simple, transparent pricing when you need to scale.

Start for Free

Connect Your Services

Pre-built integrations for the tools you use

OpenAI OpenAI
Anthropic Anthropic
Gemini Gemini
xAI xAI
AWS AWS
Resend Resend
Postmark Postmark
Slack Slack
MCP MCP
JSON-RPC JSON-RPC

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