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 provides typed API operations 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). client.StatusPages.DraftCreate (POST /status-page/draft/create) stores a status page event draft for a human to review and publish from the console; it is never directly visible to the public: draft is arbitrary JSON stored verbatim, up to 64 KB serialized, with page_id, type (incident or maintenance), name, and message validated; change_id (> 0 appends an update to an existing event), status, and affected_components are optional, and for a new maintenance you can set start_time / end_time (Unix seconds) for the window. The request-level source is an opaque marker of the drafting origin (up to 64 characters, e.g. ai_sre:sess_xxx); the response returns a draft_id matching draft_[A-Za-z0-9]{22}, which the console review link carries. client.Knowledge covers the 9 operations under /safari/knowledge/*: on the pack side PackReadGet (get the account pack), PackReadList (list packs), PackWriteEnsure (ensure a pack exists), PackWriteUpdate (change a pack’s scope), and PackWriteDelete (delete a pack); on the file side FileReadGet, FileReadList, FileWritePut (upload/overwrite), and FileWriteDelete. Exported types include KnowledgePackItem, KnowledgeFileItem, KnowledgeWarning, and the various Knowledge*Request / Knowledge*Response structs. client.Artifacts (AI SRE artifacts) covers the 11 operations under /safari/artifact/*: on the gallery read side ReadGet (get a single published artifact by ID), ReadList (list artifacts visible to the caller, with title substring search and scope (all / personal / team) plus team_ids filtering), and ReadGetFileState (probe up to 50 presented-file IDs (pf_ prefix) in one call for ones that already have a live published artifact); on the file side ReadSign (issue short-lived download/preview URLs for a presented file, valid 5 minutes, expires_in is fixed at 300) and ReadStream (download or preview the file’s bytes with a signed token — the success body is a file, not a JSON envelope, with the raw bytes on Response.Raw); on the write side WritePublish (publish a session-produced file to the gallery), WriteUpdate (rename the artifact or transfer it between personal and team scope), and WriteDelete (detach it from the gallery; the source file stays with its session); public sharing WriteShareEnable (turn on anonymous public sharing and return the public link — anyone with the link can view it, no login required), WriteShareRevoke (turn sharing off; the link stops resolving immediately), and WriteShareSync (refresh the public snapshot with the latest content — when share_enabled is true and share_file_id differs from file_id, the snapshot is stale and calling this refreshes it). Exported types include PublishedArtifactItem, ArtifactShareState, SignedUrLs, and the various Artifact*Request / Artifact*Response structs. client.Diagnostics.QueryData runs a synchronous query via POST /monit/query/data and returns a stable query_result.v1 structured result (result.kind is one of frames, records, or samples). This API requires monit-edge v0.65.0 or later. For log-pattern and metric-trend analysis, use client.DataSources.ToolsInvoke with prometheus.metric_trends, loki.log_patterns, or victorialogs.log_patterns. client.DataSources.ToolsInvoke (POST /monit/datasource/tools/invoke, monit-datasource-tools-invoke) executes one deterministic tool against a configured datasource: tool is a single tool name prefixed by the datasource type (e.g. mysql.overview), and params is the tool-specific JSON parameters (omitted means {}; an explicit null is invalid). Alongside diagnostic tools, the entry supports <type>.query query tools (prometheus, mysql, postgres, oracle, clickhouse, elasticsearch, loki, victorialogs, sls, tencent_cls); the /monit/query/data entry stays unchanged. It requires all currently online routable Edge sessions in the cluster to support the v0.71.0 base invoke protocol (individual tools may require a newer implementation), and there is no tool catalog, no automatic replay, and no fallback to legacy diagnose. The request body limit is 128 KiB, the complete success response limit is 10 MiB, and the tool timeout is at most 25 seconds; the response is a DatasourceToolResult (data is tool-specific JSON, never null, summary is optional, and a truncated object with reason indicates truncation). For client.DataSources, the payload selects a type-specific configuration block by type_ident. Fifteen type_ident values are allowed: prometheus, loki, mysql, oracle, postgres, clickhouse, elasticsearch, sls, tencent_cls, victorialogs, plus the new diagnostic-only types redis_node, redis_sentinel, mongodb_mongod, mongodb_mongos, and kafka — diagnostic-only types always have alerting_enabled false (which does not block non-alerting queries or tools) and reject true. Connection address rules: Redis/MongoDB diagnostic types take a single host:port (bracket IPv6), with no URI, userinfo, or query; kafka takes 1–32 unique comma-separated host:port bootstrap addresses (at most 4096 characters after normalization, and the payload has no broker list); for mongodb_mongod / mongodb_mongos the configuration block’s auth_source defaults to admin, username and password must be configured together, and client certificates are unsupported; the Redis node configuration’s database defaults to 0. Sensitive fields such as the diagnostic types’ password and Kafka’s tls_key support ${env:NAME} references: literal values are omitted from responses (only ${env:...} references are echoed back), omitting the fields on update preserves the stored values, and explicitly sending an empty string clears them. enabled and alerting_enabled are independent: enabled (whether business execution is enabled) defaults to true on create; alerting_enabled (whether alert evaluation is allowed; alerting also requires enabled true and an alerting-capable type) defaults to true for alerting types and false for diagnostic-only types on create. On update, omitting either preserves the current value and an explicit null is invalid; disabling (enabled false) is rejected with a conflict when enabled rules reference the datasource. Also note that payload is always null in /monit/datasource/list responses (the list query does not read the payload column); it is populated in create/update/info responses.
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


An unsuccessful call returned by the Flashduty API — whether the envelope carries an error or the HTTP status is non-2xx — returns *ErrorResponse. It has Code, Message, an optional Reason, and RequestID fields; when troubleshooting, give RequestID to the support team to pinpoint the request. Reason carries the server-supplied optional cause (the envelope’s DutyError has the same field, JSON reason,omitempty); when non-empty it is also appended to the end of the error string, as , reason X. 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.
If you receive a non-2xx response with a non-JSON body, the SDK returns a plain error, not *ErrorResponse. This indicates that a gateway, load balancer, proxy, or other intermediary produced the response, usually because the request exceeded an intermediary timeout. Retry the request or split a long-running batch into smaller batches, and do not assume that errors.As(err, &apiErr) matches this error.
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.