Skip to main content
chief-go is the official Go client for the Chief API. It depends only on the standard library, so you can vendor it into external tools without pulling in third-party packages.

Install

go get github.com/Storytell-ai/chief-go/chief

Requirements

Go 1.26 or newer. The client depends only on the standard library, so it adds no third-party transitive dependencies to your module.

Credentials

A Personal Access Token is required and sent as the X-API-Key header. Most routes are project-scoped and also need a project id (X-Project-Id). The Projects.List call is the exception — it works with only an API key and returns the projects the key can reach. Create a token in the Chief app under Settings → API tokens — see Introduction for details on tokens and project ids. Pass credentials as options or let the client read them from the environment:
OptionEnvironment variableRequired
WithAPIKeyCHIEF_API_KEYyes
WithProjectIDCHIEF_PROJECT_IDfor project-scoped routes
WithBaseURLCHIEF_BASE_URLno (defaults to https://api.storytell.ai)

Resources

Each resource is its own service on the client: client.Chats, client.Assets, client.Labels, client.Actions, client.Sessions, client.Skills, client.Memories, and client.Projects.
Chats, assets, and labels are available now. Actions, skills, memories, live sessions, and projects require the latest Chief API release and are rolling out.

Chats

Chat turns run asynchronously: Create and SendMessage return as soon as the workflow is accepted. WaitForResponse polls GetMessage until the turn finishes — or call GetMessage directly to poll on your own schedule.
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/Storytell-ai/chief-go/chief"
)

func main() {
	client, err := chief.New(
		chief.WithAPIKey(os.Getenv("CHIEF_API_KEY")),
		chief.WithProjectID(os.Getenv("CHIEF_PROJECT_ID")),
	)
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()

	// Open a chat with its first turn; the turn runs asynchronously.
	created, err := client.Chats.Create(ctx, &chief.CreateChatRequest{
		Prompt: "What changed in the codebase this week?",
	})
	if err != nil {
		log.Fatal(err)
	}

	// Block until the response is populated.
	msg, err := client.Chats.WaitForResponse(ctx, created.ChatID, created.MessageID, 2*time.Minute)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("answer: %s\n", msg.Response)

	// Ask a follow-up in the same chat.
	sent, err := client.Chats.SendMessage(ctx, created.ChatID, &chief.SendMessageRequest{
		Prompt: "Now summarize that in one sentence.",
	})
	if err != nil {
		log.Fatal(err)
	}

	follow, err := client.Chats.WaitForResponse(ctx, created.ChatID, sent.MessageID, 2*time.Minute)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("follow-up: %s\n", follow.Response)
}

Assets

UploadFile runs the full three-step flow — create the asset row, PUT the bytes to the signed URL, then complete the upload to start ingest. The returned bool is true when the content was a dedup hit and no bytes were uploaded.
// Upload a local file, then block until ingest reaches a terminal status.
asset, deduped, err := client.Assets.UploadFile(ctx, "report.pdf")
if err != nil {
	log.Fatal(err)
}
fmt.Printf("asset %q (dedup hit: %t)\n", asset.AssetID, deduped)

asset, err = client.Assets.WaitForReady(ctx, asset.AssetID, 2*time.Minute)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("status: %s\n", asset.Status)

// Attach a label by name; an unmatched name is auto-created.
label, err := client.Assets.AttachLabel(ctx, asset.AssetID, "quarterly")
if err != nil {
	log.Fatal(err)
}
fmt.Printf("attached label %q (%s)\n", label.Name, label.LabelID)

// List the assets in the project.
page, err := client.Assets.List(ctx, chief.WithLimit(10))
if err != nil {
	log.Fatal(err)
}
for _, a := range page.Data {
	fmt.Printf("%s\t%s\t%s\n", a.AssetID, a.Status, a.Filename)
}

Actions

Actions run a prompt on a schedule or in response to events, optionally emailing the result.
// An action that runs every day at 09:00 and emails its result.
action, err := client.Actions.Create(ctx, &chief.ActionRequest{
	Name:   "daily-digest",
	Prompt: "Summarize everything uploaded in the last 24 hours.",
	Schedule: &chief.ScheduleRequest{
		Hour:     "9",
		Weekday:  "*",
		MonthDay: "*",
		Timezone: "America/Sao_Paulo",
	},
	Email: &chief.EmailOutcome{
		To:                   []string{"team@example.com"},
		Subject:              "Daily digest",
		IncludeDateInSubject: true,
	},
})
if err != nil {
	log.Fatal(err)
}
fmt.Printf("created action %q\n", action.ActionID)

// Pause it, then resume it.
_, _ = client.Actions.Disable(ctx, action.ActionID)
_, _ = client.Actions.Enable(ctx, action.ActionID)

Pagination

List calls are cursor-paginated. Size a page with WithLimit, then feed the page’s LastID to WithAfterID to fetch the next one until HasMore is false:
var after string
for {
	opts := []chief.ListOption{chief.WithLimit(50)}
	if after != "" {
		opts = append(opts, chief.WithAfterID(after))
	}

	page, err := client.Assets.List(ctx, opts...)
	if err != nil {
		log.Fatal(err)
	}
	for _, a := range page.Data {
		fmt.Println(a.AssetID)
	}

	if !page.HasMore {
		break
	}
	after = page.LastID
}
WithBeforeID pages backward instead. The two cursors are mutually exclusive.

Errors

Every non-2xx response is returned as an *APIError carrying the HTTP status, a stable machine-readable Code, and a user-facing Humane message. Helpers cover the common cases:
session, err := client.Sessions.Get(ctx, "missing-id")
if err != nil {
	switch {
	case chief.IsNotFound(err):
		// 404
	default:
		if apiErr, ok := chief.IsAPIError(err); ok {
			log.Printf("chief: %s (%s)", apiErr.Humane, apiErr.Code)
		}
	}
}

Debugging

Pass WithDebug(true) when building the client to dump every HTTP request and response to the standard logger:
client, err := chief.New(
	chief.WithAPIKey(os.Getenv("CHIEF_API_KEY")),
	chief.WithProjectID(os.Getenv("CHIEF_PROJECT_ID")),
	chief.WithDebug(true),
)

Versioning

The package follows semantic versioning. Pin a version in your go.mod and review the release notes before upgrading across a major version:
go get github.com/Storytell-ai/chief-go/chief@latest

Reference