# "Workflows"
_Path: en/concepts/workflows_
> "How Wippy persists long-running workflows, replays execution, receives signals, and recovers from failures."
## Table of Contents
- Workflows
## Content
# Workflows
Workflows persist the state of long-running operations so execution can recover after crashes and restarts. They suit processes such as payments, order fulfillment, and multi-step approvals.
## Why Use Workflows
Functions keep in-flight state in memory, while workflows persist execution state:
| Aspect | Functions | Workflows |
|--------|-----------|-----------|
| State | Call-local | Rebuilt from persisted history |
| Worker crash | In-flight call fails | Replays from recorded history |
| Duration | Seconds to minutes | Hours to months |
| Application failure | Returned to caller | Ends or retries according to provider policy |
## How Workflows Work
Workflow code looks like regular Lua code:
```lua
local funcs = require("funcs")
local time = require("time")
local result, err = funcs.call("app.api:charge_card", payment)
if err then return nil, err end
time.sleep("24h")
local status, err = funcs.call("app.api:check_status", result.id)
if err then return nil, err end
if status == "failed" then
local _, refund_err = funcs.call("app.api:refund", result.id)
if refund_err then return nil, refund_err end
end
return status
```
The workflow engine intercepts calls and records their results. After a crash, it replays execution from the recorded history.
Inside a workflow, each `funcs.call()` target runs as a Temporal activity. A
target `function.*` entry must register with a worker through
`meta.temporal.activity.worker`; unregistered entries are not available to the
workflow. A `process.*` activity target additionally needs
`meta.options.default_host` (or the legacy `meta.default_host`) so it is
registered in the function registry used by the Temporal worker. See
[Activities](../temporal/activities.md) for the function activity example and
activity options.
Workflow authors must still write deterministic code. Wippy limits workflow
modules to those classified as Deterministic or Workflow and supplies
replay-safe implementations for supported operations. funcs.call()
runs a recorded activity, time.sleep() uses a workflow timer,
uuid.v4() records a side effect, and time.now() reads the
workflow's deterministic time reference.
### Saga Pattern
Compensate on failure:
```lua
local funcs = require("funcs")
local inventory, err = funcs.call("app.inventory:reserve", items)
if err then return nil, err end
local payment, err = funcs.call("app.payments:charge", amount)
if err then
local _, compensation_err = funcs.call("app.inventory:release", inventory.id)
return nil, compensation_err or err
end
local shipping, err = funcs.call("app.shipping:create", order)
if err then
local _, refund_err = funcs.call("app.payments:refund", payment.id)
local _, release_err = funcs.call("app.inventory:release", inventory.id)
return nil, refund_err or release_err or err
end
return {inventory = inventory, payment = payment, shipping = shipping}
```
### Waiting for Signals
Wait for external events (approval decisions, webhooks, user actions):
```lua
local funcs = require("funcs")
local _, err = funcs.call("app.approvals:submit", request)
if err then return nil, err end
local inbox = process.inbox()
local msg, open = inbox:receive() -- blocks until signal arrives
if not open then return nil, errors.new("workflow inbox closed") end
local decision, payload_err = msg:payload():data()
if payload_err then return nil, payload_err end
if decision.approved then
return funcs.call("app.orders:fulfill", request.order_id)
else
return funcs.call("app.notifications:send_rejection", request)
end
```
## Choosing a Compute Model
| Use Case | Choose |
|----------|--------|
| HTTP request handling | Functions |
| Data transformation | Functions |
| Background jobs | Processes |
| User session state | Processes |
| Real-time messaging | Processes |
| Payment processing | Workflows |
| Order fulfillment | Workflows |
| Multi-day approvals | Workflows |
## Starting Workflows
Workflows use `process.spawn()` with a workflow host:
```lua
-- Spawn workflow on temporal worker
local pid, err = process.spawn("app.workflows:order_processor", "app:temporal_worker", order_data)
if err then return nil, err end
-- Send signals to workflow
local ok, err = process.send(pid, "update", {status = "approved"})
if err then return nil, err end
return ok
```
The caller uses the same spawn API. The host determines whether the entry runs
on a `temporal.worker` or a `process.host`. Persisted history and replay apply
only to the Temporal-hosted path. A workflow entry run through an ordinary
process host has in-memory process semantics and does not gain Temporal
durability.
When a workflow spawns children via process.spawn(), they become child workflows on the same provider, maintaining durability guarantees.
## Failure and Supervision
Processes can run as supervised services using `process.service`:
```yaml
# Process definition
- name: session_handler
kind: process.lua
source: file://session_handler.lua
method: main
# Supervised service wrapping the process
- name: session_manager
kind: process.service
process: app:session_handler
host: app:processes
lifecycle:
auto_start: true
restart:
max_attempts: 10
```
Workflows do not use process supervision trees. The workflow provider manages
persistence and recovery; application-level retries follow the configured
workflow and activity policies.
## Configuration
Workflow definition (spawned dynamically):
```yaml
- name: order_processor
kind: workflow.lua
source: file://order_processor.lua
method: main
meta:
temporal:
workflow:
worker: app:temporal_worker
modules:
- funcs
- time
```
Every function or process invoked through `funcs.call()` also declares the
activity worker. For example:
```yaml
- name: charge_card
kind: function.lua
source: file://charge_card.lua
method: main
meta:
temporal:
activity:
worker: app:temporal_worker
```
Workflow provider:
```yaml
- name: temporal_worker
kind: temporal.worker
client: app:temporal_client
task_queue: "orders"
lifecycle:
auto_start: true
```
See [Temporal](https://temporal.io) for production workflow infrastructure.
## See Also
- [Functions](concepts/functions.md) — Request-scoped calls
- [Process Model](concepts/process-model.md) — Stateful background work
- [Supervision](guides/supervision.md) — Process restart policies
## Navigation
Previous: "Functions" (concepts/functions)
Next: "Lua Runtime" (lua/overview)