Skip to main content

Overview


go-flashduty is the official open-source Go client for Flashduty, covering every REST endpoint of the Flashduty Open API. It follows the same design as go-github — service groups, typed requests and responses, a composable transport layer — and stays strictly 1:1 with the OpenAPI spec: each method maps to exactly one HTTP call, returns (*T, *Response, error), and performs no implicit cross-endpoint aggregation or enrichment. The SDK currently covers 253 endpoints across 27 services, all generated from the Flashduty OpenAPI spec, covered by unit tests, and end-to-end verified against the live API.
The SDK is deliberately “thin.” Consumer-side logic such as short-ID resolution and cross-endpoint orchestration belongs in the caller (CLI / MCP), not stuffed into the SDK or shoehorned into an endpoint. This keeps the SDK strictly one-to-one with the API — predictable, generatable, and verifiable.
The module path is github.com/flashcatcloud/go-flashduty, the package name is flashduty, and the source is open-sourced under Apache-2.0 at flashcatcloud/go-flashduty.

Open API reference

Request parameters and response fields for every endpoint.

Command-line tool

The CLI for operating Flashduty directly from your terminal.

Installation


1

Requires Go 1.24+

Make sure your local Go toolchain is at least 1.24.
2

Get the dependency

3

Import the package

Quick start


Here is a minimal runnable example: construct the client, list incidents in the “Triggered” state, and handle the returned triple (data, *Response, error).
Every call returns three values:
app_key is used for authentication, and the SDK injects it as a query parameter on every request automatically. Obtain the app_key from “Push integrations” or team configuration in the Flashduty console.

Create a client


NewClient takes an app_key plus zero or more Options. An empty app_key returns an error directly. The default Base URL is https://api.flashcat.cloud, the default HTTP timeout is 30 seconds, and the default User-Agent is go-flashduty.
Private deployment: point the client at your own Flashduty gateway address with WithBaseURL — everything else stays exactly the same.

Services and methods


Endpoints are grouped by service and hang off the client: the call convention is uniformly client.<Service>.<Method>(ctx, req), returning (*T, *Response, error). For example, client.Incidents.List(ctx, req) or client.Sessions.Info(ctx, req).
All identifiers, service field names, and method names match the generated code. For exactly which methods each service has and their request and response types, rely on services_gen.go and the per-service files, plus the Open API reference.

Response timestamps


Time fields in responses are no longer bare integers but self-describing Timestamp (Unix seconds) or TimestampMilli (milliseconds) types. They serialize to RFC3339 strings in the local time zone, so JSON, logs, and LLM-facing output are directly readable; the raw epoch is still one method call away.
  • Serialization (outbound): a non-zero value serializes to a quoted RFC3339 string (TimestampMilli uses RFC3339Nano to preserve millisecond precision). A zero value serializes to the bare integer 0 — an “unset” sentinel rather than a 1970 date, and dropped by json:",omitempty".
  • Deserialization (inbound): it accepts both numeric epoch (the raw wire form) and RFC3339 strings (so a serialized value round-trips losslessly), and also accepts null (→ 0).
Request-side time fields are still int64 — the API expects a numeric epoch on the wire. Note: most endpoints take seconds, but RUM and webhook history-related endpoints take milliseconds.

Pagination


All list endpoints share ListOptions, which you embed in the request struct. Zero values are omitted and never override server defaults (the backend defaults to p=1, limit=20). On the response side, *Response carries Total, HasNextPage, and SearchAfterCtx. We recommend walking page by page with the search-after cursor:

Error handling


Any unsuccessful call — whether the envelope carries an error or the HTTP status is non-2xx — returns *ErrorResponse. It has Code, Message, and RequestID fields; when troubleshooting, give RequestID to the support team to pinpoint the request. When the API returns 429, the error is promoted to *RateLimitError: it embeds *ErrorResponse (so errors.As for *ErrorResponse still matches) and additionally carries a RetryAfter hint.
Typed predicate functions save string comparisons and see through wrapped errors (using errors.As internally):

Retry


The core client has no built-in automatic retry. Compose the optional retry subpackage through the transport layer — a safe-by-default retrying http.RoundTripper. Features of github.com/flashcatcloud/go-flashduty/retry:
  • Retry conditions: HTTP 429, any 5xx (status ≥ 500), and transport errors. Other 4xx and all 2xx/3xx return immediately.
  • Backoff policy: deterministic exponential backoff (MinWait * 2^attempt, capped at MaxWait each time); no random jitter. When a valid integer Retry-After header is present, it takes precedence (also capped at MaxWait).
  • Safe replay: retries only when the request body is replayable (req.Body is nil or req.GetBody is non-nil), rebuilds the body on a clone of the request for each retry, and never mutates the caller’s original *http.Request. All requests the SDK builds set GetBody, so POST bodies are safely replayable.
  • Respects cancellation: if the request context is canceled while waiting out a backoff, it returns the context error immediately.
The retry subpackage is pure net/http and deliberately does not import the parent flashduty package, so it never introduces a circular dependency. Both retry.New() and &retry.Transport{} (zero value) work out of the box.

Streaming export


client.Sessions.Export exports the full event transcript of an AI SRE session, returning an io.ReadCloser (an NDJSON stream, application/x-ndjson) rather than a JSON envelope. The first line is always a session_meta envelope, and each subsequent line is a session event; when req.IncludeSubagents is true, each subagent_dispatch line is followed by the subagent’s own full event stream. Because the response body can be large, read it line by line and write directly to a file — do not buffer the whole transcript into memory. The returned io.ReadCloser is the live HTTP response body, held by the caller and which you must Close (a defer close is correct). Pair it with NewExportScanner to scan line by line and DecodeExportLine to decode a line into an ExportLine:
NewExportScanner is configured with a per-line buffer large enough to hold the wider event lines in a transcript (such as tool output or LLM calls), free of the default 64KB token limit. On any non-2xx status, the response body is still a regular JSON error envelope — Export reads and closes it and returns a typed error (*ErrorResponse, or *RateLimitError on 429), with the io.ReadCloser being nil, consistent with the other generated endpoints.