This quickstart runs a workflow on your own machine: you create a pull app, run a worker that polls EZGH for work, and start a run. Nothing needs a public endpoint. It takes about five minutes.
Before you start
- Go 1.26 or later.
- The
ezghCLI, version 0.16.0 or later (install it, or runezgh update), logged in. - An organization ID and a project ID. Run
ezgh whoamifor the organization andezgh projects listfor the project, or copy them from the console. The SDKs take IDs, not slugs. - An API key whose owner has
WorkflowsFullAccesson the project, or the root user’s. Create one withezgh iam api-keys create workflows-quickstart.
Set them in your shell:
export EZGH_API_KEY=ezgh_…
export EZGH_ORG_ID=<org-id>
export EZGH_PROJECT_ID=<project-id>1. Create a pull app
ezgh workflows apps create quickstart --kind pull --project "$EZGH_PROJECT_ID"The output shows the app. Keep its ID:
export EZGH_APP_ID=<ID>2. Install the SDK
Install it by tag:
mkdir workflows-quickstart && cd workflows-quickstart
go mod init example.com/quickstart
go get github.com/ezgamehost/workflows-sdk-go@v0.1.0It uses only the Go standard library.
3. Write a workflow and a worker
main.go defines a workflow named hello with three steps: it computes a greeting, sleeps for five
seconds, and records the time. Run without arguments, the program is a worker that polls the
app’s task queue and runs the workflow; each poll also tells EZGH which workflows this worker
serves. Run with start, it starts a run, checks it every second until it ends, and prints its
output.
package main
import (
"context"
"fmt"
"log"
"os"
"os/signal"
"time"
workflows "github.com/ezgamehost/workflows-sdk-go"
)
type Input struct {
Name string `json:"name"`
}
type Output struct {
Greeting string `json:"greeting"`
At time.Time `json:"at"`
}
var hello = workflows.New("hello", func(ctx workflows.Context, in Input) (Output, error) {
greeting, err := workflows.Run(ctx, "greet", func(sc workflows.StepContext) (string, error) {
return "Hello, " + in.Name + "!", nil
})
if err != nil {
return Output{}, err
}
if err := workflows.Sleep(ctx, "pause", 5*time.Second); err != nil {
return Output{}, err
}
at, err := workflows.Now(ctx, "finished-at")
if err != nil {
return Output{}, err
}
return Output{Greeting: greeting, At: at}, nil
})
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
if len(os.Args) > 1 && os.Args[1] == "start" {
start(ctx)
return
}
w, err := workflows.NewWorker(workflows.WorkerOptions{
APIKey: os.Getenv("EZGH_API_KEY"),
OrganizationID: os.Getenv("EZGH_ORG_ID"),
ProjectID: os.Getenv("EZGH_PROJECT_ID"),
App: os.Getenv("EZGH_APP_ID"),
Workflows: []workflows.Workflow{hello},
})
if err != nil {
log.Fatal(err)
}
log.Print("Worker polling. Press Ctrl+C to stop.")
if err := w.Run(ctx); err != nil {
log.Fatal(err)
}
}
func start(ctx context.Context) {
c, err := workflows.NewClient(workflows.ClientOptions{
APIKey: os.Getenv("EZGH_API_KEY"),
OrganizationID: os.Getenv("EZGH_ORG_ID"),
ProjectID: os.Getenv("EZGH_PROJECT_ID"),
})
if err != nil {
log.Fatal(err)
}
run, err := c.Start(ctx, os.Getenv("EZGH_APP_ID"), "hello", Input{Name: "Ada"}, workflows.StartOptions{})
if err != nil {
log.Fatal(err)
}
fmt.Println("Started run", run.ID)
for !run.Terminal() {
time.Sleep(time.Second)
if run, err = c.Get(ctx, run.ID); err != nil {
log.Fatal(err)
}
}
var out Output
if run.Status == workflows.RunStatusCompleted {
if err := c.Output(ctx, run.ID, &out); err != nil {
log.Fatal(err)
}
}
fmt.Println(run.Status, out.Greeting, out.At)
}4. Run the worker
go run .Leave it running.
5. Start a run
In a second terminal with the same environment variables:
go run . startAfter about five seconds:
Started run 0d415599-27ed-80a5-8ac6-9eb1cd742b40
completed Hello, Ada! 2026-10-11 20:30:16.879 +0000 UTCThe worker ran the workflow twice: once up to the sleep, and again after it, when the greet
step returned its recorded result without running again.
A pull app starts runs only of workflows that one of its workers announced in the last 10
minutes. Starting a run with no worker running answers 409 workflow_not_announced.
6. Clean up
Stop the worker with Ctrl+C, then delete the app and, if you made one for this, the API key:
ezgh workflows apps delete quickstart --project "$EZGH_PROJECT_ID"
unset EZGH_API_KEY
ezgh iam api-keys delete workflows-quickstartNext
- Concepts: what replay means for your code.
- Push and pull apps: serve workflows from an HTTPS endpoint instead.
- The SDK’s README lists its whole API.