Skip to content

Temporal Client with Go: a roadmap ​

Why this matters, in 30 seconds ​

Every Workflow starts with a Client call. So does every Signal from your API, every "where is my order?" screen, and every operator clean-up script. The Client looks simple (client.Dial, ExecuteWorkflow, Get), but the details decide whether your system works in production:

  • Where it connects: dev server, self-hosted, Temporal Cloud, with credentials you can rotate.
  • What "start" means when a request is retried: one Workflow, two, or an error.
  • How you get results across processes, failures, timeouts and Continue-As-New chains.

This guide covers those details, chapter by chapter.


The roadmap ​

Stage 1: Foundations (core concepts, must-know) ​

#ChapterYou'll be able to…
01What is a Temporal Client?Explain what a Client does and doesn't do, list its operations, and run a first start-and-wait. Use it in an HTTP service and inside an Activity.
02ConnectingConnect with defaults, environment variables, TOML profiles or code. Know which source wins, and choose between Dial, DialContext and NewLazyClient.
03Temporal Cloud and secured ServicesConnect to Temporal Cloud with an API key or mTLS, rotate credentials without restarts, and connect to a self-hosted cluster with a private CA.

Stage 2: Daily use (starting Workflows and getting results) ​

#ChapterYou'll be able to…
04Starting Workflow ExecutionsStart by function or by Type name (other services and languages), set the Task Queue and a business Workflow ID, and know what happens on the Service.
05Workflow IDs and duplicate startsMake starts idempotent with the conflict and reuse policies, and decide when a duplicate should be an error.
06StartWorkflowOptions in practiceUse Workflow timeouts, retries, Search Attributes, Memo, summaries, StartDelay, Priority and cron correctly.
07Getting Workflow resultsWait synchronously or fetch later from any process, handle each failure type, time out the wait, and read Continue-As-New chains.

Stage 3: Beyond starting (interacting and managing) ​

#ChapterYou'll be able to…
08Interacting with and managing running WorkflowsSignal, Signal-With-Start, Query, Update, cancel, terminate, describe, list and count Workflows. Run Standalone Activities.

Stage 4: Production ​

#ChapterYou'll be able to…
09The Client in productionManage the Client's lifecycle, wire identity, logging and multiple Namespaces, shut down cleanly, and unit-test code with mocks.Client.

Core concepts vs. implementation details ​

Core concepts: understand these firstImplementation details, options & sub-features: learn when needed
The Client talks to the Service. It never runs code (01)Client inside Activities, sharing with Workers (01, 09)
Connection = address + Namespace (+ TLS + credentials) (02)envconfig layers and profiles, DialContext, NewLazyClient, NewClientFromExisting (02, 09)
Cloud auth: API key or mTLS (03)Dynamic credentials, certificate rotation, private CAs, legacy header providers (03)
ExecuteWorkflow + Task Queue + Workflow ID (04)Start by Type name, custom Type names (04)
Workflow ID uniqueness (05)Conflict and reuse policies, WorkflowExecutionErrorWhenAlreadyStarted (05)
WorkflowRun and Get (07)GetWithOptions, DisableFollowingRuns, GetFirstExecutionRunID, last completion result (07)
—Timeouts, RetryPolicy, Search Attributes, Memo, summaries, StartDelay, Priority, cron (06)
List, Query, Signal, Standalone Activities (08)Update, Signal-With-Start, cancel vs terminate, pagination, Nexus (08)

Which start options do I need? ​

text
Will the same start be attempted more than once (HTTP retries, queue re-delivery, double clicks)?
├── No  → ID: business key, TaskQueue. Done.                                    → chapter 04
└── Yes → What should a duplicate do?
    ├── Return the same Workflow and its result, forever
    │       → ConflictPolicy USE_EXISTING + ReusePolicy REJECT_DUPLICATE         → chapter 05
    ├── Return the running one, but allow a new run once it's closed
    │       → ConflictPolicy USE_EXISTING (ReusePolicy default)                  → chapter 05
    ├── Be reported to the caller (HTTP 409)
    │       → ErrorWhenAlreadyStarted: true, catch WorkflowExecutionAlreadyStarted → chapter 05
    └── Replace the old run
            → ConflictPolicy TERMINATE_EXISTING                                  → chapter 05

Should the Workflow be searchable by business fields?   → TypedSearchAttributes  → chapter 06
Must it start later?                                    → StartDelay             → chapter 06
Must it run on a calendar?                              → Schedules (not CronSchedule) → chapter 06
Must it give up after N days whatever happens?          → WorkflowExecutionTimeout → chapter 06

Go API cheat sheet ​

I want to…Go APIChapter
Connect to the dev serverclient.Dial(client.Options{})01, 02
Connect from env vars / TOML profileenvconfig.LoadDefaultClientOptions(), envconfig.LoadClientOptions(LoadClientOptionsRequest{ConfigFileProfile: …})02
Fail fast at startupclient.DialContext(ctx, opts), c.CheckHealth(ctx, &client.CheckHealthRequest{})02, 09
Connect to Cloud with an API keyCredentials: client.NewAPIKeyStaticCredentials(key) / NewAPIKeyDynamicCredentials(fn) + ConnectionOptions{TLS: &tls.Config{}}03
Connect with mTLS, rotatableConnectionOptions{TLS: &tls.Config{GetClientCertificate: …}}03
Start a Workflowc.ExecuteWorkflow(ctx, client.StartWorkflowOptions{ID, TaskQueue}, wf, args...)04
Start by Type namec.ExecuteWorkflow(ctx, opts, "AllocateStock", req)04
Make a start idempotentWorkflowIDConflictPolicy: enumspb.WORKFLOW_ID_CONFLICT_POLICY_USE_EXISTING, WorkflowIDReusePolicy: …REJECT_DUPLICATE05
Get an error on duplicatesWorkflowExecutionErrorWhenAlreadyStarted: true + errors.As(err, &*serviceerror.WorkflowExecutionAlreadyStarted)05
Index business fieldsTypedSearchAttributes: temporal.NewSearchAttributes(key.ValueSet(v))06
Wait for the resultrun.Get(ctx, &out)07
Get the result elsewherec.GetWorkflow(ctx, workflowID, "").Get(ctx, &out)07
Inspect a failureerrors.As(err, &*temporal.ApplicationError / CanceledError / TerminatedError / TimeoutError)07
Read one run of a chainGetWithOptions(ctx, &out, client.WorkflowRunGetOptions{DisableFollowingRuns: true}), run.GetFirstExecutionRunID()07
Signal / Signal-With-Startc.SignalWorkflow(ctx, id, "", name, arg), c.SignalWithStartWorkflow(ctx, id, name, arg, opts, wf, args...)08
Query / Updatec.QueryWorkflow(ctx, id, "", name), c.UpdateWorkflow(ctx, client.UpdateWorkflowOptions{…})08
Cancel / terminatec.CancelWorkflow(ctx, id, ""), c.TerminateWorkflow(ctx, id, "", reason)08
List / countc.ListWorkflow(ctx, &workflowservice.ListWorkflowExecutionsRequest{Query, NextPageToken}), c.CountWorkflow(…)08
Run a Standalone Activityc.ExecuteActivity(ctx, client.StartActivityOptions{ID, TaskQueue, StartToCloseTimeout}, fn, args...)08
Second Namespace, same connectionclient.NewClientFromExisting(c, client.Options{Namespace: …})09
Unit-test code using the Clientmocks.NewClient(t), mocks.NewWorkflowRun(t)09

About the code samples ​

  • Each chapter is a self-contained module named github.com/acme/shop. Every Go block starts with // file: <path> so you can paste it into that path.
  • All samples were compiled, gofmt-checked and go vet-checked against Go SDK v1.50.0 (go.temporal.io/sdk) and go.temporal.io/sdk/contrib/envconfig v1.0.2. The unit tests in chapter 09 pass.
  • The behaviors in chapters 05, 06, 07 and 08 (duplicate-start policies, Get timeouts and error types, Continue-As-New chains, Query/Update/cancel/terminate, StartDelay) were checked against a dev server (Temporal CLI 1.9.1, Server 1.32.0).
  • External systems are interfaces or fakes. Run everything locally with temporal server start-dev (UI at http://localhost:8233).

Notes from checking the docs against the SDK ​

  • GetRunID() after Get. The Go Client page says GetRunID() "always returns the Run ID of the initial Run". In SDK v1.50.0 it returns the run the handle currently points to: after Get follows a Continue-As-New chain, it returns the last run's ID (checked on a dev server). Use GetFirstExecutionRunID() for the first run (chapter 07).
  • mTLS in a TOML profile. The Go Client page's Cloud mTLS example puts tls_client_cert_data and tls_client_key_path directly under [profile.cloud]. envconfig v1.0.2 doesn't know these keys and silently ignores them (the profile loads with no TLS settings). With ConfigFileStrict: true it fails with key(s) unrecognized. The keys belong in a [profile.<name>.tls] table: client_cert_path, client_cert_data, client_key_path, client_key_data (chapter 03).
  • StartWorkflowOptions reference is incomplete. The page lists 11 fields and links to pkg.go.dev pages pinned to SDK v1.10.0. SDK v1.50.0 also has WorkflowIDConflictPolicy, TypedSearchAttributes (the map-based SearchAttributes is deprecated), StartDelay, StaticSummary, StaticDetails, Priority, EnableEagerStart and VersioningOverride (chapters 05 and 06).
  • Duplicate starts don't fail by default. The page lists WorkflowExecutionErrorWhenAlreadyStarted (default false) without explaining it. With the defaults, starting a running Workflow ID again returns the existing run with no error (checked on a dev server) (chapter 05).
  • Run ID "optional". The page says a Run ID "is optional if more than one closed Workflow Execution has the same Workflow ID". In practice an empty Run ID means "the latest run". You need a Run ID only to read a specific older run.
  • Get with a context timeout. It returns *serviceerror.DeadlineExceeded, which doesn't match errors.Is(err, context.DeadlineExceeded). Check ctx.Err() instead. (This also fixed a sample in the Architecture guide, chapter 03.)
  • Standalone Nexus Operations. The Encyclopedia lists them among Client operations. In Go SDK v1.50.0 they go through c.NewNexusClient(...), marked experimental.
  • Credentials wording. The page asks for "the mTLS CA certificate and mTLS private key". The Client actually needs its client certificate and private key. The CA certificate is uploaded to the Namespace.

Sources ​

Encyclopedia: Temporal Client

Go SDK: Temporal Client · linked from it: Environment Configuration reference