Open source Apache 2.0

A high-level language and platform for AI workflows and agents.

AI writes your code now. Can you read it? Hot's expressiveness keeps it focused on your problem domain, not syntax, boilerplate, or infrastructure.

Start building

The whole system in view

Follow workflows from the dashboard down to each run, event, and call.

Dashboard
Hot App dashboard showing function-call traces, projects, and live event streams
Agents / Workflow
Hot Dev workflow graph view showing a Lead Qualifier agent with handlers, schedules, function calls, and downstream events
Stream Graph
Hot App Stream Graph
Execution Trace
Hot App Execution Trace

Build with Hot

Workflows, agents, tools, and events in a few lines.

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/get(or(event.data.url, "https://hot.dev/status"))
    if(not(::http/is-ok-response(result)), fail(`Request failed: ${result.status}`))
    {
        status: result.status,
        bytes: length(to-json(result.body)),
    }
}

Build and ship

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

Teach your AI Hot

Context for your coding assistant

terminal
hot ai add

Adds AGENTS.md and Hot skills to your project, so your coding assistant knows Hot's syntax and patterns. Learn more →

4

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>"
    })
  )
}

Tip: Install the Hot extension for syntax highlighting, autocomplete, and diagnostics. It is available for VS Code-compatible editors (search "Hot" in Extensions). Learn more →

5

Run the dev servers

App, API, Scheduler, Worker. All in one command.

terminal
hot dev --open
6

Observe your workflow

Real-time observability at localhost:4680

Runs

Every execution captured. Status, timing, input, output, full trace.

Events

Inspect 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.

7

Deploy to Hot Cloud

The same workflow runs locally and in production.

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

terminal
hot deploy

The system behind the work

From workflow logic to production, every layer works as one.

Create

Define workflows, agents, and tools in one place.

Hot Language

Familiar data, optional types, execution flows, and a workflow-ready standard library

AI Agents

Build agents with tools, memory, and MCP integration

MCP Tools

Turn any Hot function into an MCP tool

Hot Packages

Use pre-built integrations for AI and SaaS services

Hot CLI

Run, test, and deploy from the command line

VS Code & LSP

Syntax highlighting, autocomplete, and diagnostics

Run

Execute every kind of work on the same runtime.

::hot::box

Run any container as a Hot function

Tasks

Long-running work with checkpoints and recovery

Event Handlers

Trigger functions automatically when events occur

Webhooks

Turn any Hot function into a webhook endpoint

Scheduled Runs

Run functions on cron or natural-language schedules

HTTP API

Expose OpenAPI-compatible REST endpoints

File Storage

Read, write, and manage files in your environment

Configurable Retries

Control attempts, delay, and backoff for failed runs

Observe

Follow the work from a live run to a single expression.

Execution Tracing

Intermediate values, timing, and the full call stack

Hot App

A live view of runs, events, streams, and traces

Testing Framework

Built-in unit and integration testing

Alerts

Email, Slack, PagerDuty, and webhook notifications

Control

Keep environments, teams, and access together.

Environments

Separate staging, production, and everything between

Teams

Role-based access to projects and environments

Secret Variables

AES-256-GCM encrypted secrets and configuration

API Keys

Create, rotate, and manage API keys

Service Keys & Sessions

Scoped credentials and short-lived customer tokens

Granular Permissions

Resource and action-level access control

Ship

Carry the same system from local development into production.

Hot Cloud

Deploy with one command and scale automatically

Custom Domains

Serve APIs, MCP, and webhooks from your own domains

Bring the tools you already use

Models, data, messaging, and media connect through ready-made Hot packages.

Latest from Hot Dev

Build and run it all in one place

Free to start. One command when you are ready for production.

Get started free