Workflows (preview)
A workflow is a TypeScript file in your workspace repo, in the workflows/ folder, written with Hatchet’s own SDK. Commit it on main and it is deployed. Hatchet runs it: schedules, retries and timeouts are Hatchet’s.
Three things, each one verb. Sandbox: Run → Sandbox (or workflows_run with sandbox: true) runs the workflow as it is in your sandbox right now, uncommitted edits included, with real data and no schedule, and shows the finished run. It runs inside your sandbox on a Hatchet engine of its own (embedded mode), so nothing touches production and the run is in nobody’s list. Main: a commit on main is the deploy. Active: a deployed workflow starts inactive, runnable with Run, schedule and webhook off; when a run looks right, an admin flips it to Active on its tab, and from then on it runs by itself. The same as testing and then activating an n8n workflow.
Preview. Workflows are optional: an installation without a Hatchet runs without them. With one, they have their own panel in Mako: the workflows, their files and their runs.
A workflow
Section titled “A workflow”A workflow is a folder: workflows/<name>/workflow.ts. The folder name is the workflow’s name.
workflows/ daily-digest/ workflow.tsThat file is all a workflow needs. ../hatchet (the Hatchet client) and ../lib/mako (Mako data access) are supplied when the workflow is built. Add your own workflows/hatchet.ts or workflows/lib/mako.ts only to replace them.
import { hatchet } from "../hatchet";
const dailyDigest = hatchet.workflow({ name: "daily-digest", // same as the folder on: { cron: "0 7 * * 1-5" }, // weekdays at 07:00 UTC});
dailyDigest.task({ name: "build-digest", retries: 1, fn: async () => ({ date: new Date().toISOString().slice(0, 10) }),});
export default dailyDigest;Every folder with a workflow.ts is picked up. There is no list to keep.
What starts a run
Section titled “What starts a run”| Trigger | How |
|---|---|
| Schedule | on: { cron: "0 7 * * 1-5" } in the workflow, in UTC |
| Webhook | Add webhook on a live workflow’s tab gives a URL. A POST to it starts a run with the JSON as input |
| A person | The Run button, with a JSON input |
| An agent | workflows_run |
Anyone with the member role writes workflows in their sandbox and tests them there; only workspace admins put them on main, activate them and add webhooks. A webhook is off until an admin adds it on the workflow’s tab. Its URL carries its own secret, so treat it like a password; removing the webhook and adding it again gives a new URL. The schedule and the webhook start runs only while the workflow is active; Run always works. The webhook URL with ?sandbox=<your user id> is a test URL: a POST to it runs your sandbox’s version with the body as input and answers with the finished run, whether or not the workflow is active.
Using Mako from a step
Section titled “Using Mako from a step”import { generateText } from "ai";import { agent, call, model, query } from "../lib/mako";
// SQL on a workspace connection (read-only).const rows = await query("<connectionId>", "select count(*) from orders");
// Any Mako tool by name: here, rebuild an app's data.await call("app_materialize", { appId: "<appId>", name: "orders_daily" });
// Mako's own agent, as in chat: a goal in, the answer out.const { text, toolCalls } = await agent( "Which accounts stopped using the product this week, and why?",);
// A plain model call, with Mako's tools if you pass them.const summary = await generateText({ model: model("anthropic/claude-haiku-4.5"), prompt: `Summarize in one line: ${text}`,});How a deploy works
Section titled “How a deploy works”- You commit a change to
workflows/onmain. Mako records the commit as the workspace’s target, durably (an Inngest function, like app deploys), and checks every deployed workspace againstmainonce an hour in case a push was missed. - Mako saves that commit as the one to run.
- The worker notices within ten seconds, typechecks the commit and starts it.
- Once the new code is up, the old code finishes its running tasks and stops.
If the commit does not typecheck, the worker reports the error and keeps running the previous commit. To roll back, revert the commit.
Set it up
Section titled “Set it up”Mako needs two things: a Hatchet API token, and a worker.
Try it locally
Section titled “Try it locally”-
In
.env, uncomment the workflows lines from.env.example. SetWORKFLOWS_WORKER_KEYto any value of your own that starts withrevops_. -
Start Hatchet and the worker:
Terminal window docker compose --profile workflows up -d -
Ask the agent for a workflow, or add
workflows/<name>/workflow.tsto the workspace repo onmain.
Hatchet dashboard: http://localhost:8086 (admin@example.com / Admin123!!). The link icon in the Workflows panel opens it. This bundled Hatchet is for testing, not for production.
Production
Section titled “Production”-
Create an API token in Hatchet Cloud, or in a Hatchet you run yourself.
-
Set
HATCHET_CLIENT_TOKENon the Mako API. -
Choose a
WORKFLOWS_WORKER_KEY(long, random, starting withrevops_) and set it on the Mako API. -
Build the worker image from the Mako repository and run it anywhere that can reach Mako and Hatchet:
Terminal window docker build -f deploy/workflows/runtime/Dockerfile -t mako-workflows-runtime .docker run -e MAKO_URL=https://your-mako -e MAKO_API_KEY=$WORKFLOWS_WORKER_KEY \mako-workflows-runtime -
Turn workflows on for the workspace, as in step 3 above.
A worker run this way serves one workspace and has no sandbox: workflow code runs with the container’s access. Use it for your own team’s code.
Settings
Section titled “Settings”| Variable | Set on | What |
|---|---|---|
HATCHET_CLIENT_TOKEN |
API | One Hatchet tenant for the whole installation, so one workspace: the first to deploy keeps it, a second is refused. Multi-workspace installs set the admin variables below instead, and Mako makes a tenant per workspace |
HATCHET_DASHBOARD_URL |
API | Where Mako links admins to the Hatchet dashboard |
WORKFLOWS_WORKER_KEY |
API, and the worker as MAKO_API_KEY |
The worker’s Mako API key |
MAKO_URL, MAKO_API_KEY |
Worker | All the worker needs. It gets the Hatchet token from Mako. |
HATCHET_API_URL, HATCHET_ADMIN_EMAIL, HATCHET_ADMIN_PASSWORD |
API | Only for a Hatchet you operate: lets Mako create a Hatchet tenant per workspace. Used by the local setup. |
With none of these set, Mako runs without workflows.
All under /api/workspaces/<workspaceId>/workflows:
| Route | Does |
|---|---|
GET / |
What is deployed, build errors, the workflows (with active), schedules and recent runs |
GET /runs/<id> |
One run: its steps in order, with status, output, error and logs |
GET /files, GET /files?path=<file> |
The files under workflows/, or one file’s contents |
POST /<name>/run |
Start a run: { input } answers runId; { input, sandbox: true } runs the caller’s checkout in their sandbox and answers the finished run |
PUT /<name>/triggers |
Change what starts it: { active } (its schedule and webhook), { webhook } (add: answers the new URL; remove). Admins |
Over MCP, an API key needs the workflows:run scope to start runs or add webhooks; looking needs only mcp.