> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.hyjal.cloud/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.hyjal.cloud/_mcp/server.

# Schedule a job

> Run a Resource on a cron schedule with the schedule field, branch on HYJAL_TRIGGER, and watch each run in the Logs tab.

This tutorial adds a scheduled run to an existing Resource. You will set a cron
`schedule` in `hyjal.toml`, branch your handler on the `HYJAL_TRIGGER`
environment variable, deploy, and watch each scheduled run land in the Logs
tab. It takes about ten minutes.

Scheduled runs are ordinary metered [instances](/get-started/what-is-hyjal):
the platform invokes the Resource on the cron cadence, the same way an HTTP
request would. There is no separate worker type to provision.

## Prerequisites

* A deployed Project. This tutorial uses `acme` with an `api` Resource on the
  Go lane.
* The [`hyjal` CLI](/reference/cli) installed and logged in.

## 1. Add a schedule to the Resource

Open `hyjal.toml` and add a `schedule` field to the Resource you want to run
on a cadence. The value is a five-field cron expression evaluated in UTC.

```toml
[project]
name = "acme"

[resource.api]
lane = "go"
schedule = "*/15 * * * *"    # every 15 minutes, UTC
```

The expression `*/15 * * * *` runs the Resource every fifteen minutes. See
[Jobs and crons](/app-types/jobs-and-crons) for the full field grammar.

## 2. Branch the handler on the trigger

A scheduled run invokes the same component as an HTTP request. Read the
`HYJAL_TRIGGER` environment variable to tell them apart: it is `schedule` on a
scheduled run and absent (or `http`) on a normal request.

```go
package main

import (
	"fmt"
	"net/http"
	"os"
)

func handler(w http.ResponseWriter, r *http.Request) {
	if os.Getenv("HYJAL_TRIGGER") == "schedule" {
		runScheduledWork()
		fmt.Fprintln(w, "scheduled run complete")
		return
	}
	fmt.Fprintln(w, "hello from api")
}

func runScheduledWork() {
	// Reconcile state, prune expired rows, send a digest, and so on.
	fmt.Println("scheduled work: pruned 12 expired sessions")
}
```

The line written to stdout is captured per request and appears in the Logs
tab, correlated with the scheduled run.

## 3. Deploy

Deploy the Resource. The schedule is registered as part of applying policy.

```bash
$ hyjal deploy
Building resource api (lane: go)...
  tinygo build -target=wasip2 ✓
Uploading artifact... digest sha256:a71c4f…9d02
Applying policy... ✓
  schedule registered: */15 * * * * (UTC)
Flipping route... ✓

Deployed acme/api
  https://api--acme.hyjal.cloud
```

The `schedule registered` line confirms the cron entry is active. The next run
fires at the next fifteen-minute boundary.

## 4. Watch runs in the Logs

Stream the Resource's logs and wait for the next scheduled boundary, or open
the [Logs tab](/observability/logs) in the Console.

```bash
$ hyjal logs -f --resource api
[14:15:00] SCHEDULE  /            200   38ms
           scheduled work: pruned 12 expired sessions
[14:30:00] SCHEDULE  /            200   41ms
           scheduled work: pruned 9 expired sessions
```

Each scheduled run is a row in the same chronological table as HTTP requests,
with its method shown as `SCHEDULE`. The stdout line is attached to the run.
Filter to scheduled runs in the Console with the method filter chips.

## What you built

* A Resource that runs every fifteen minutes on a UTC cron schedule.
* A handler that branches on `HYJAL_TRIGGER=schedule` to do scheduled work
  separately from HTTP traffic.
* Per-run visibility in the Logs tab, with stdout correlated to each run.

## Next steps

* [Jobs and crons](/app-types/jobs-and-crons): cron field grammar and
  external scheduler invocation.
* [Environment variables](/deploy/environment-variables): inject config and
  secrets into each run.
* [Logs](/observability/logs): search and filter the run timeline.