Skip to content

Go SDK — Getting Started

sdk-go is a Go client for the Sencai Platform API (/api/v1/*), generated from the public OpenAPI spec (openapi-generator-cli, go template — F4.DEVPORTAL.02). It lives at sdk-go/ in the monorepo, module github.com/sencai/sdk-go.

github.com/sencai/sdk-go is a placeholder import path, not yet published — the final module path is a decision tied to a future public release (its own repository/org), and public pkg.go.dev indexing is a separate business/release decision, out of scope for the SDK generation work itself. Until then:

Terminal window
go get github.com/sencai/sdk-go

then add a replace directive in your consuming project’s go.mod pointing at a local checkout or a git ref of this directory:

replace github.com/sencai/sdk-go => /path/to/code/sdk-go
package main
import (
"context"
"os"
sencaisdk "github.com/sencai/sdk-go"
)
func main() {
cfg := sencaisdk.NewConfiguration()
cfg.Servers = sencaisdk.ServerConfigurations{
// Local dev: http://api.sencai.localhost/api/v1 or http://localhost:1337/api/v1
// Production: https://api.sencai.space/api/v1
{URL: os.Getenv("SENCAI_API_BASE_URL")},
}
// Bearer JWT (Keycloak access token today) — or a future API-key header. The
// client only provides the injection point; it does not fetch or refresh
// the token for you.
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("SENCAI_ACCESS_TOKEN"))
client := sencaisdk.NewAPIClient(cfg)
ctx := context.Background()
_ = ctx
_ = client
}

Configuration.Servers and the Authorization header are always set at runtime — the package never hardcodes a production URL.

Complete example: list organisations, invite a member

Section titled “Complete example: list organisations, invite a member”

Verified against a local run-local-dev.sh stack (backend, backend-db, auth profiles).

package main
import (
"context"
"fmt"
"os"
sencaisdk "github.com/sencai/sdk-go"
)
func main() {
cfg := sencaisdk.NewConfiguration()
cfg.Servers = sencaisdk.ServerConfigurations{{URL: os.Getenv("SENCAI_API_BASE_URL")}}
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("SENCAI_ACCESS_TOKEN"))
client := sencaisdk.NewAPIClient(cfg)
ctx := context.Background()
// List organisations the caller belongs to.
result, _, err := client.OrganisationAPI.FindOrganisation(ctx).PaginationPageSize(10).Execute()
if err != nil {
panic(err)
}
for _, org := range result.Data {
fmt.Println(org.Name)
}
// -> QA Admin Workspace
// e2e-org-d18a54e2
// e2e-org-d9e67836
targetOrg := result.Data[0]
// Invite an existing Strapi user into that organisation. `POST
// /organisations/{id}/invite` requires the invitee to already have a
// registered account (see the Error handling section below for what
// happens otherwise) and the caller to be admin+ in the organisation.
body := sencaisdk.NewOrganisationInviteMemberRequest("new-member@example.com", "viewer")
invite, _, err := client.OrganisationAPI.
OrganisationInviteMember(ctx, *targetOrg.DocumentId).
OrganisationInviteMemberRequest(*body).
Execute()
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", invite.Data)
}

A non-2xx response returns a non-nil error (the typed method’s result is left at its zero value) that can be unwrapped to *sencaisdk.GenericOpenAPIError, exposing the raw response body via .Body(). Error bodies follow Strapi’s standard envelope: { "error": { "status", "name", "message", "details" } }.

import (
"encoding/json"
"errors"
)
type errorEnvelope struct {
Error struct {
Status int `json:"status"`
Name string `json:"name"`
Message string `json:"message"`
Details map[string]any `json:"details"`
} `json:"error"`
}
_, _, err := client.OrganisationAPI.
OrganisationInviteMember(ctx, *targetOrg.DocumentId).
OrganisationInviteMemberRequest(*body).
Execute()
if err != nil {
var apiErr *sencaisdk.GenericOpenAPIError
if errors.As(err, &apiErr) {
var body errorEnvelope
_ = json.Unmarshal(apiErr.Body(), &body)
fmt.Println(body.Error.Status, body.Error.Name, body.Error.Message)
// A real, commonly hit example — the organisation's plan seat limit:
// 402 PaymentRequired "Member limit reached (3/3). Upgrade your plan."
}
}

Other status codes you should expect from this endpoint specifically: 400 (missing/invalid email/role, or no Strapi user registered with that email — this endpoint does not invite not-yet-registered emails), 401 (missing/expired token), 403 (caller is not admin+ in the organisation), 409 (that user is already a member).

The package’s version is tracked in .openapi-generator/VERSION and derived from the OpenAPI spec’s info.version on every regeneration — it is never bumped by hand. Go’s own module versioning works through git tags on a standalone repository, once one exists (out of scope for this task). A breaking API change ships as a new spec major version, which regenerates this SDK accordingly. See API versioning for what counts as breaking, the /api/v1//api/v2/ migration path, and the SUNSET_DATE deprecation-header convention — this page does not repeat that policy, only how it surfaces in the generated client.

For the full generated reference of every operation this client exposes, see the API Reference.