---
title: Pilot Go SDK
description: "Go client for the Cuitty Pilot API — manage playbooks, runs, and providers with idiomatic Go error handling."
section: Pilot
order: 15
updatedAt: 2026-06-01
slug: pilot/sdk/go
---
# Pilot Go SDK

`github.com/cuitty/pilot-sdk-go` is a Go client for the Pilot API. It requires Go 1.22+ and has zero external dependencies beyond the standard library.

## Install

```bash
go get github.com/cuitty/pilot-sdk-go
```

## Quick start

```go
package main

import (
	"encoding/json"
	"fmt"
	"log"

	pilot "github.com/cuitty/pilot-sdk-go"
)

func main() {
	client := pilot.NewClient("http://localhost:4320", "your-auth-token")

	// List all playbooks
	playbooks, err := client.ListPlaybooks("")
	if err != nil {
		log.Fatal(err)
	}
	for _, pb := range playbooks {
		fmt.Printf("%s: %s\n", pb.ID, pb.Title)
	}

	// Start a run
	inputs, _ := json.Marshal(map[string]string{
		"zone":        "example.com",
		"record_name": "app",
		"ip_address":  "1.2.3.4",
	})
	resp, err := client.StartRun(&pilot.StartRunRequest{
		PlaybookID: "cloudflare.dns.add-a-record",
		Mode:       "autonomous",
		Inputs:     inputs,
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Run started: %s (%s)\n", resp.ID, resp.Status)
}
```

## NewClient

Creates a new Pilot API client. Pass an empty string for `authToken` to skip authentication.

```go
// With auth
client := pilot.NewClient("http://localhost:4320", "ey...")

// Without auth
client := pilot.NewClient("http://localhost:4320", "")
```

The `Client` struct exposes `BaseURL`, `AuthToken`, and `HTTP` (*http.Client) fields for customization.

| Method | Returns | Description |
|--------|---------|-------------|
| `Health()` | `*HealthStatus, error` | Check server health |
| `ListPlaybooks(target)` | `[]PlaybookSummary, error` | List playbooks; pass `""` for all |
| `GetPlaybook(id)` | `*Playbook, error` | Get playbook by ID (latest version) |
| `CreatePlaybook(req)` | `*CreatePlaybookResponse, error` | Create a new playbook |
| `UpdatePlaybook(id, req)` | `*UpdatePlaybookResponse, error` | Update (version-bump) a playbook |
| `DeletePlaybook(id)` | `*DeletePlaybookResponse, error` | Delete all versions |
| `StartRun(req)` | `*StartRunResponse, error` | Start a new run |
| `ListRuns(opts)` | `[]Run, error` | List runs with optional filters |
| `GetRun(id)` | `*Run, error` | Get run details including steps |
| `AbortRun(id)` | `*AbortRunResponse, error` | Abort a running or queued run |
| `ApproveRun(id, req)` | `*ApproveResponse, error` | Submit approval decision for a step |
| `ListProviders()` | `[]Provider, error` | List all provider profiles |

## Playbooks

```go
// List all
playbooks, err := client.ListPlaybooks("")

// Filter by target
cfPlaybooks, err := client.ListPlaybooks("cloudflare")

// Get one
pb, err := client.GetPlaybook("cloudflare.dns.add-a-record")

// Create
resp, err := client.CreatePlaybook(&pilot.CreatePlaybookRequest{
	Target:     "cloudflare",
	Title:      "Add CNAME record",
	YAML:       playbookYAML,
	AuthoredBy: "human",
})

// Update
resp, err := client.UpdatePlaybook("cloudflare.dns.add-cname",
	&pilot.UpdatePlaybookRequest{YAML: updatedYAML})

// Delete
resp, err := client.DeletePlaybook("cloudflare.dns.add-cname")
```

## Runs

```go
// Start a run
inputs, _ := json.Marshal(map[string]string{
	"zone": "example.com",
	"record_name": "api",
	"ip_address": "1.2.3.4",
})
resp, err := client.StartRun(&pilot.StartRunRequest{
	PlaybookID:        "cloudflare.dns.add-a-record",
	Mode:              "interactive",
	Inputs:            inputs,
	ProviderProfileID: "cf-prod",
})

// Get run with steps
run, err := client.GetRun(resp.ID)
for _, step := range run.Steps {
	fmt.Printf("%s: %s\n", step.StepID, step.Status)
}

// List runs with filters
runs, err := client.ListRuns(&pilot.ListRunsOptions{
	Status: "running",
	Limit:  10,
})

// Abort
resp, err := client.AbortRun(runID)

// Approve a step
resp, err := client.ApproveRun(runID, &pilot.ApproveRequest{
	StepID:   "save",
	Decision: "approve",
})
```

## Types

### Core types

| Type | Key fields |
|------|-----------|
| `Playbook` | `ID`, `Version`, `Target`, `Title`, `DocumentYAML`, `AuthoredBy`, `CreatedAt` |
| `PlaybookSummary` | `ID`, `Version`, `Target`, `Title`, `AuthoredBy` |
| `Run` | `ID`, `ProjectID`, `PlaybookID`, `Mode`, `Status`, `InputsJSON`, `OutputsJSON`, `Steps []RunStep` |
| `RunStep` | `ID`, `RunID`, `StepID`, `Action`, `Status`, `AIUsed`, `DurationMs *int`, `ErrorMessage *string` |
| `Provider` | `ID`, `ProjectID`, `Provider`, `Label`, `BaseURL`, `LastAuthenticated`, `ExpiresAt` |
| `HealthStatus` | `Status`, `Database`, `DriverPool`, `ProviderSessions`, `LastChecked` |

### Request types

| Type | Fields |
|------|--------|
| `StartRunRequest` | `PlaybookID`, `Version *int`, `Mode`, `Inputs json.RawMessage`, `ProviderProfileID`, `ProjectID` |
| `CreatePlaybookRequest` | `Target`, `Title`, `YAML`, `AuthoredBy` |
| `UpdatePlaybookRequest` | `YAML`, `Target`, `Title`, `AuthoredBy` |
| `ApproveRequest` | `StepID`, `Decision` |
| `ListRunsOptions` | `Status`, `PlaybookID`, `Limit int` |

## Error handling

The SDK returns `*APIError` for non-2xx responses. All other errors are standard Go errors from `net/http` or `encoding/json`.

```go
import (
	"errors"
	pilot "github.com/cuitty/pilot-sdk-go"
)

resp, err := client.StartRun(&req)
if err != nil {
	var apiErr *pilot.APIError
	if errors.As(err, &apiErr) {
		fmt.Printf("API error %d: %s\n", apiErr.StatusCode, apiErr.Message)
	} else {
		fmt.Printf("Network error: %v\n", err)
	}
}
```

| Type | Fields | When |
|------|--------|------|
| `*APIError` | `StatusCode int`, `Message string` | Non-2xx HTTP response |
| `error` | standard | Network, JSON, or other failure |

## See also

- [JavaScript SDK](/docs/pilot/sdk/javascript) — TypeScript client with event streaming
- [Playbook reference](/docs/pilot/playbook-reference) — YAML schema for playbook documents
- [Quickstart](/docs/pilot/quickstart) — record and replay your first workflow