|
| 1 | +--- |
| 2 | +title: "Go SDK" |
| 3 | +description: "go-flashduty — the official Go client for the Flashduty Open API" |
| 4 | +keywords: ["Go SDK", "go-flashduty", "Golang", "Open API", "client", "SDK"] |
| 5 | +--- |
| 6 | + |
| 7 | +[`go-flashduty`](https://github.com/flashcatcloud/go-flashduty) is the official Go client for the Flashduty Open API — a thin, strongly-typed SDK. It is generated from the same OpenAPI specification this documentation is built on, covers every Open API endpoint, and is validated by unit tests and end-to-end against the live API. |
| 8 | + |
| 9 | +If your service is written in Go, use the SDK to call Flashduty rather than hand-writing HTTP requests — you get typed request parameters, response structs, and error codes out of the box, with no manual JSON assembly or response parsing. |
| 10 | + |
| 11 | +<Tip> |
| 12 | +The SDK shares the same endpoint semantics as the Open API. Every request/response structure documented here maps to a corresponding type in the SDK. Authentication uses the same [APP Key](/en/openapi/introduction#authentication). |
| 13 | +</Tip> |
| 14 | + |
| 15 | +## Install |
| 16 | + |
| 17 | +Requires Go 1.24 or later. |
| 18 | + |
| 19 | +```bash |
| 20 | +go get github.com/flashcatcloud/go-flashduty |
| 21 | +``` |
| 22 | + |
| 23 | +## Quick start |
| 24 | + |
| 25 | +Create a client with your APP Key and call endpoints grouped by service (`client.Incidents`, `client.Alerts`, …). Each method maps to exactly one HTTP call and returns `(*T, *Response, error)`: |
| 26 | + |
| 27 | +```go |
| 28 | +package main |
| 29 | + |
| 30 | +import ( |
| 31 | + "context" |
| 32 | + "fmt" |
| 33 | + "log" |
| 34 | + |
| 35 | + flashduty "github.com/flashcatcloud/go-flashduty" |
| 36 | +) |
| 37 | + |
| 38 | +func main() { |
| 39 | + client, err := flashduty.NewClient("YOUR_APP_KEY") |
| 40 | + if err != nil { |
| 41 | + log.Fatal(err) |
| 42 | + } |
| 43 | + |
| 44 | + list, resp, err := client.Incidents.List(context.Background(), &flashduty.ListIncidentsRequest{ |
| 45 | + Progress: "Triggered", |
| 46 | + ListOptions: flashduty.ListOptions{Limit: 20}, |
| 47 | + }) |
| 48 | + if err != nil { |
| 49 | + log.Fatal(err) |
| 50 | + } |
| 51 | + |
| 52 | + fmt.Printf("request_id=%s total=%d has_next=%t\n", resp.RequestID, resp.Total, resp.HasNextPage) |
| 53 | + for _, inc := range list.Items { |
| 54 | + fmt.Printf("[%s] %s\n", inc.IncidentSeverity, inc.Title) |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +## Design |
| 60 | + |
| 61 | +<CardGroup cols={2}> |
| 62 | + <Card title="Thin and typed" icon="feather"> |
| 63 | + Every method maps to exactly one HTTP call and returns `(*T, *Response, error)`. No hidden cross-endpoint enrichment. |
| 64 | + </Card> |
| 65 | + <Card title="Service-grouped" icon="layer-group"> |
| 66 | + Endpoints are organized into services on the client (`client.Incidents`, `client.Alerts`, …), generated from the OpenAPI specification. |
| 67 | + </Card> |
| 68 | + <Card title="Composable transport" icon="puzzle-piece"> |
| 69 | + Cross-cutting concerns (retry, caching, tracing, rate-limit handling) compose as `http.RoundTripper` middleware via `WithTransport`. |
| 70 | + </Card> |
| 71 | + <Card title="Human-readable timestamps" icon="clock"> |
| 72 | + Response time fields are typed `Timestamp` / `TimestampMilli`, rendering as RFC3339 in JSON, logs, and LLM-facing output — while the raw epoch is one call away. |
| 73 | + </Card> |
| 74 | +</CardGroup> |
| 75 | + |
| 76 | +## Options |
| 77 | + |
| 78 | +`NewClient` accepts a set of options: |
| 79 | + |
| 80 | +```go |
| 81 | +client, err := flashduty.NewClient("YOUR_APP_KEY", |
| 82 | + flashduty.WithBaseURL("https://api.flashcat.cloud"), |
| 83 | + flashduty.WithTimeout(10*time.Second), |
| 84 | + flashduty.WithUserAgent("my-app/1.0"), |
| 85 | + flashduty.WithHTTPClient(customHTTPClient), |
| 86 | + flashduty.WithTransport(customRoundTripper), |
| 87 | + flashduty.WithLogger(myLogger), |
| 88 | + flashduty.WithRequestHeaders(staticHeaders), |
| 89 | + flashduty.WithRequestHook(func(req *http.Request) { /* e.g. inject traceparent */ }), |
| 90 | +) |
| 91 | +``` |
| 92 | + |
| 93 | +## Errors and rate limits |
| 94 | + |
| 95 | +The SDK returns typed errors you can unwrap with `errors.As`, plus convenience predicates that see through wrapped errors: |
| 96 | + |
| 97 | +```go |
| 98 | +_, _, err := client.Incidents.Info(ctx, &flashduty.IncidentInfoRequest{IncidentID: "does-not-exist"}) |
| 99 | + |
| 100 | +var apiErr *flashduty.ErrorResponse |
| 101 | +if errors.As(err, &apiErr) { |
| 102 | + fmt.Println(apiErr.Code, apiErr.RequestID) |
| 103 | +} |
| 104 | + |
| 105 | +var rl *flashduty.RateLimitError |
| 106 | +if errors.As(err, &rl) { |
| 107 | + time.Sleep(rl.RetryAfter) |
| 108 | +} |
| 109 | + |
| 110 | +// Convenience predicates |
| 111 | +if flashduty.IsNotFound(err) { /* ... */ } |
| 112 | +if flashduty.IsRateLimited(err) { /* ... */ } |
| 113 | +switch flashduty.ErrorCodeOf(err) { |
| 114 | +case flashduty.ErrorCodeAccessDenied, flashduty.ErrorCodeUnauthorized: |
| 115 | + // handle auth failures |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +Error codes map one-to-one to the [Error Code List](/en/openapi/introduction#error-code-list). |
| 120 | + |
| 121 | +## Timestamps |
| 122 | + |
| 123 | +Time fields on responses are typed `Timestamp` (Unix seconds) or `TimestampMilli` (milliseconds). They marshal to an RFC3339 string in the local timezone and unmarshal from either a numeric epoch or an RFC3339 string, so a value round-trips cleanly. The zero value stays the numeric `0` sentinel (never a 1970 date) and is dropped by `omitempty`. |
| 124 | + |
| 125 | +```go |
| 126 | +inc := list.Items[0] |
| 127 | +fmt.Println(inc.StartTime) // 2026-05-30T14:37:11+08:00 (String / fmt / TOON) |
| 128 | +b, _ := json.Marshal(inc.StartTime) // "2026-05-30T14:37:11+08:00" |
| 129 | +epoch := inc.StartTime.Unix() // 1779514631 (raw wire value) |
| 130 | +t := inc.StartTime.Time() // time.Time |
| 131 | +``` |
| 132 | + |
| 133 | +<Note> |
| 134 | +Request time fields stay plain `int64` — the API expects a numeric epoch on the wire. Most endpoints take **seconds**, but RUM and webhook-history endpoints take **milliseconds**. |
| 135 | +</Note> |
| 136 | + |
| 137 | +## Retries |
| 138 | + |
| 139 | +Automatic retries are **not** built into the core. Compose them at the transport layer with the optional `retry` subpackage — a safe-by-default retrying `http.RoundTripper` (retries 429 and 5xx, honors `Retry-After`, deterministic exponential backoff, and only replays requests whose body is replayable, which all SDK requests are): |
| 140 | + |
| 141 | +```go |
| 142 | +import "github.com/flashcatcloud/go-flashduty/retry" |
| 143 | + |
| 144 | +client, err := flashduty.NewClient("YOUR_APP_KEY", |
| 145 | + flashduty.WithTransport(retry.New( |
| 146 | + retry.WithMaxRetries(3), |
| 147 | + )), |
| 148 | +) |
| 149 | +``` |
| 150 | + |
| 151 | +## Resources |
| 152 | + |
| 153 | +<CardGroup cols={2}> |
| 154 | + <Card title="GitHub repository" icon="github" href="https://github.com/flashcatcloud/go-flashduty"> |
| 155 | + Source, full README, and issues. Licensed under Apache-2.0. |
| 156 | + </Card> |
| 157 | + <Card title="API Catalog" icon="list" href="/en/openapi/api-catalog"> |
| 158 | + Browse every endpoint — each maps to a typed method in the SDK. |
| 159 | + </Card> |
| 160 | +</CardGroup> |
0 commit comments