Go SDK
Integrate with FortyOne using the generated Go client and secure transport helpers.
Use github.com/complexus-tech/fortyone-go to call the FortyOne v1 API with
generated request and response types. The client adds bearer authentication,
targets the production API by default, retries safe reads, and includes helpers
for pagination, idempotency, structured errors, and webhook verification.
Preview availability
The Go SDK is not yet tagged or available from the public Go module proxy. The examples below show the current preview API; installation instructions will be added with the first public release.
Create a client and list stories
import (
"context"
"log"
"os"
fortyone "github.com/complexus-tech/fortyone-go"
"github.com/google/uuid"
)
func listStories(ctx context.Context) error {
client, err := fortyone.New(fortyone.Config{
Token: os.Getenv("FORTYONE_TOKEN"),
})
if err != nil {
return err
}
workspaceID, err := uuid.Parse(os.Getenv("FORTYONE_WORKSPACE_ID"))
if err != nil {
return err
}
pager, err := fortyone.NewStoryPager(
client,
workspaceID,
fortyone.StoryPaginationOptions{Limit: 100},
)
if err != nil {
return err
}
for {
page, ok, err := pager.NextPage(ctx)
if err != nil {
return err
}
if !ok {
return nil
}
for _, story := range page.Data {
log.Printf("story id=%s reference=%s", story.Id, story.Reference)
}
}
}fortyone.New targets https://api.fortyone.app unless you explicitly set
Config.BaseURL for an approved environment. The workspace ID is not a URL or
client setting. It identifies the workspace whose resources you want to access
because FortyOne API operations are workspace-scoped.
Create a story idempotently
Generate one key for each logical write and persist it with the request before the first attempt. Do not generate another key inside a retry loop.
key, err := fortyone.NewIdempotencyKey()
if err != nil {
return err
}
request := fortyone.CreateStoryRequest{
Title: "Investigate latency",
TeamId: teamID,
}
// Persist key and request before the first network attempt.
response, err := client.CreateStoryWithResponse(
ctx,
workspaceID,
&fortyone.CreateStoryParams{IdempotencyKey: key},
request,
)
if err != nil {
// The outcome is unknown. Retry with the same key and unchanged request.
return err
}
if response.JSON201 == nil {
if response.HTTPResponse == nil {
return errors.New("FortyOne returned no HTTP response")
}
return fortyone.NewAPIError(
response.StatusCode(),
response.HTTPResponse.Header,
response.Body,
)
}
log.Printf(
"story created id=%s reference=%s",
response.JSON201.Data.Id,
response.JSON201.Data.Reference,
)NewIdempotencyKey returns 32 cryptographically random bytes as 64 hexadecimal
characters. Use ValidateIdempotencyKey when restoring a retained key. Keep the
SDK version and request encoding unchanged across attempts because the API
compares the exact serialized JSON bytes.
The create operation accepts a personal access token or service-account key
with stories:write. Use separate clients when reads and writes use different
least-privilege credentials.
The default HTTP client has a 30-second timeout, refuses redirects, and uses a
maximum of three attempts for safe reads. Set RetryPolicy{MaxAttempts: 1} to
disable retries. The SDK never retries writes. For an ambiguous network failure
or a documented retryable response, retain and reuse the same key and request,
honor Retry-After, and apply a bounded retry budget. See
Idempotent writes before implementing recovery.
Verify a webhook
NewWebhookVerifier accepts a show-once whsec_ secret and validates rotation
signatures, the timestamp window, the delivery UUID, and the exact raw body.
After verification, record the delivery ID and payload durably before returning
2xx. Do not log the signing secret, signature, or raw payload.