---
title: "Quickstart: Go"
description: "Install the Go SDK from GitHub, run a pull worker on your machine, and start a run."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.ezghcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart: Go

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 `ezgh` CLI, version 0.16.0 or later** ([install it](/cli/install), or run `ezgh update`),
  logged in.
- **An organization ID and a project ID.** Run `ezgh whoami` for the organization and
  `ezgh projects list` for the project, or copy them from the console. The SDKs take IDs, not
  slugs.
- **An [API key](/iam/api-keys)** whose owner has `WorkflowsFullAccess` on the project, or the
  root user's. Create one with `ezgh iam api-keys create workflows-quickstart`.

Set them in your shell:

```sh
export EZGH_API_KEY=ezgh_…
export EZGH_ORG_ID=<org-id>
export EZGH_PROJECT_ID=<project-id>
```

## 1. Create a pull app

```sh
ezgh workflows apps create quickstart --kind pull --project "$EZGH_PROJECT_ID"
```

The output shows the app. Keep its ID:

```sh
export EZGH_APP_ID=<ID>
```

## 2. Install the SDK

Install it by tag:

```sh
mkdir workflows-quickstart && cd workflows-quickstart
go mod init example.com/quickstart
go get github.com/ezgamehost/workflows-sdk-go@v0.1.0
```

It 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.

```go title="main.go"
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

```sh
go run .
```

Leave it running.

## 5. Start a run

In a second terminal with the same environment variables:

```sh
go run . start
```

After about five seconds:

```text
Started run 0d415599-27ed-80a5-8ac6-9eb1cd742b40
completed Hello, Ada! 2026-10-11 20:30:16.879 +0000 UTC
```

The 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:

```sh
ezgh workflows apps delete quickstart --project "$EZGH_PROJECT_ID"
unset EZGH_API_KEY
ezgh iam api-keys delete workflows-quickstart
```

## Next

- [Concepts](/workflows/concepts): what replay means for your code.
- [Push and pull apps](/workflows/push-and-pull): serve workflows from an HTTPS endpoint instead.
- The [SDK's README](https://github.com/ezgamehost/workflows-sdk-go#readme) lists its whole API.

Source: https://docs.ezghcloud.com/workflows/quickstart-go/index.mdx
