Light Dark

Runs, Events & Streams

Hot Platform uses three core primitives for workflow execution: Runs, Events, and Streams. Understanding these concepts is essential for building effective Hot applications.

Runs

A Run is a single execution of a Hot function. Every function call creates a run, whether triggered by an API request, event, or schedule.

Run Lifecycle

running succeeded failed cancelled
StateDescription
runningWorker is executing the function
succeededFunction completed successfully
failedFunction threw an error or timed out
cancelledRun was cancelled before completion
pending_retryFunction failed but will be retried automatically

Runs with "retry" metadata that fail are temporarily set to pending_retry until the retry executes. See Retry Configuration for details.

Run Data

Every run captures:

{
  "run_id": "run_abc123xyz",
  "function": "::myapp::orders/process-order",
  "status": "succeeded",
  "input": {
    "order_id": "ord_12345"
  },
  "result": {
    "status": "processed",
    "total": 99.99
  },
  "started_at": "2024-12-04T10:30:00Z",
  "completed_at": "2024-12-04T10:30:02Z",
  "duration_ms": 2150,
  "trigger": {
    "type": "event",
    "event_id": "evt_xyz789"
  }
}

Execution Trace

Hot captures a full execution trace for every run, showing:

  • Each expression evaluated
  • Intermediate values
  • Function calls and returns
  • Timing for each step
  • Any errors with stack traces

Triggering Runs

Runs can be triggered in several ways:

1. API Call (via hot:call event)

curl -X POST https://api.hot.dev/v1/events \
  -H "Authorization: Bearer $HOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "hot:call", "data": {"fn": "::myapp::orders/process-order", "args": [{"order_id": "12345"}]}}'

2. Event Handler

on-order-created
meta {on-event: "order:created"}
fn (event) {
  process-order(event.data.order_id)
}

3. Schedule (recurring)

daily-report
meta {schedule: "0 0 * * *"}
fn () {
  generate-report()
}

4. Dynamic Schedule (one-time or created at runtime)

// Schedule a function to run in 10 minutes
send("hot:schedule:new", {
  fn: "::myapp::tasks/process",
  args: [{task_id: "123"}],
  schedule: "in 10 minutes"
})

See Dynamic Schedules for more details.

5. Direct Call (from another run)

process-batch fn (orders) {
  // Each send creates a separate run
  map(orders, (order) {
    send("hot:call", {
      fn: "::myapp::orders/process-order",
      args: [order]
    })
  })
}

Events

Events are messages that trigger asynchronous workflows. They decouple event producers from consumers, enabling scalable and maintainable systems.

Event Structure

An Event in Hot has two fields:

Event type {
  type: Str,
  data: Any
}

The send function has two arities:

// Pass event type and data directly
send("user:created", {id: "usr_12345", email: "alice@example.com"})

// Or pass an Event
send(Event({type: "user:created", data: {id: "usr_12345", email: "alice@example.com"}}))

Sending Events

From Hot Code:

// Send an event after user creation
create-user fn (data) {
  user insert-user(data)

  // Send event for other handlers (send is a core function)
  send("user:created", {
    id: user.id,
    email: user.email,
    name: user.name
  })

  user
}

From the API:

curl -X POST https://api.hot.dev/v1/events \
  -H "Authorization: Bearer $HOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "user:created",
    "data": {"id": "usr_12345", "email": "alice@example.com"}
  }'

From External Systems (Webhooks): Configure webhooks to forward events from services like Stripe, GitHub, or Slack directly to Hot.

Event Handlers

Define handlers using the on-event metadata:

::myapp::notifications ns

// Handle a specific event type
on-user-created meta {on-event: "user:created"}
fn (event) {
  send-welcome-email(event.data.email)
}

Event Delivery

Hot guarantees at-least-once delivery for events:

  • Events are persisted before acknowledgment
  • Failed handlers can be retried automatically with configurable attempts and delay
  • Retry status is visible in the Hot App UI

Streams

Streams provide real-time, bidirectional data flow for scenarios where request/response isn't sufficient.

Use Cases

  • AI/LLM Responses - Stream tokens as they're generated
  • Live Updates - Push data to clients in real-time
  • Long-Running Operations - Report progress incrementally
  • Bidirectional Communication - WebSocket-style interactions

Server-Sent Events (SSE)

Stream data to clients in real-time using ::hot::stream/data.

Hot code — emit chunks as they arrive:

handle-chat
meta { on-event: "chat:message" }
fn (event) {
  // Call a streaming AI API
  response ::anthropic::messages/post-stream({
    model: "claude-sonnet-4-20250514",
    max_tokens: 4096,
    messages: [{role: "user", content: event.data.message}]
  })

  // Process stream and emit chunks to the client
  process-stream(response.body, "")
}

// Recursive stream processor
process-stream fn (iter, accumulated: Str): Str {
  result next(iter)
  cond {
    result.done => { accumulated }
    => {
      delta or(result.value.data.delta.text, "")
      // Emit chunk to client in real-time
      ::hot::stream/data("ai:delta", { text: delta })
      process-stream(iter, concat(accumulated, delta))
    }
  }
}

JavaScript client — publish an event, then subscribe to the stream:

// 1. Publish event to trigger the handler
const eventRes = await fetch('/v1/events', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    event_type: 'chat:message',
    event_data: { message: 'Hello!' }
  })
});
const { data: { stream_id } } = await eventRes.json();

// 2. Subscribe to stream for real-time updates
const response = await fetch(`/v1/streams/${stream_id}/subscribe`, {
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Accept': 'text/event-stream'
  }
});

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  const text = decoder.decode(value);
  // Parse SSE events (data: {...}\n\n format)
  for (const line of text.split('\n')) {
    if (line.startsWith('data: ')) {
      const event = JSON.parse(line.slice(6));
      if (event.type === 'stream:data') {
        // Real-time chunk from ::hot::stream/data
        appendToResponse(event.payload.text);
      }
      if (event.type === 'run:stop') {
        // Run completed
        console.log('Final result:', event.run.result);
      }
    }
  }
}

Stream States

┌─────────┐    ┌─────────┐    ┌─────────┐
│  open   │ -> │ active  │ -> │ closed  │
└─────────┘    └─────────┘    └─────────┘
                    │
                    v
              ┌─────────┐
              │  error  │
              └─────────┘

Viewing Streams

Active and completed streams are visible in the Hot App:

  • Connection status and duration
  • Messages sent/received
  • Bandwidth usage
  • Error details

Monitoring

All runs, events, and streams are visible in the Hot App with:

  • Real-time updates as executions happen
  • Filtering by status, function, event type
  • Full-text search across payloads
  • Detailed drill-down views