SRE → pull alerts
Refresh the SRE inbox from Grafana (or another configured source), wait until the pull finishes, then list active alerts.
| Step | What the SDK does |
|---|---|
| 1. Start pull | pull_alerts (joins an in-progress pull on HTTP 409) |
| 2. Wait | wait_for_alert_pull / pull_alerts_and_wait |
| 3. List | list_alerts |
Use this before investigate alert when the inbox may be stale.
Minimal
from stackgen import StackgenClient, StackgenConfig
client = StackgenClient(
StackgenConfig(
base_url="https://app.stackgen.com",
api_token="stackgen_…",
workspace_id="<workspace-uuid>",
)
)
pull = client.sre.pull_alerts_and_wait(source="grafana")
print(pull.get("alerts_created"), pull.get("alerts_updated"), pull.get("alerts_resolved"))
alerts = client.sre.list_alerts(status="active", page_size=10)
for row in alerts.get("alerts") or []:
print(row["id"], row.get("title"), row.get("attention"))import { StackgenClient, StackgenConfig } from "stackgen-sdk";
const client = new StackgenClient(
new StackgenConfig({
baseUrl: "https://app.stackgen.com",
apiToken: "stackgen_…",
workspaceId: "<workspace-uuid>",
}),
);
const pull = await client.sre.pullAlertsAndWait("grafana");
console.log(pull.alerts_created, pull.alerts_updated, pull.alerts_resolved);
const alerts = (await client.sre.listAlerts({
status: "active",
page_size: 10,
})) as { alerts?: Array<Record<string, unknown>> };
for (const row of alerts.alerts ?? []) {
console.log(row.id, row.title, row.attention);
}pull, err := client.SRE.PullAlertsAndWait(ctx, "grafana", "")
if err != nil { return err }
fmt.Println(pull["alerts_created"], pull["alerts_updated"], pull["alerts_resolved"])
alerts, err := client.SRE.ListAlerts(ctx, map[string]string{
"status": "active", "page_size": "10",
})
if err != nil { return err }
fmt.Println(alerts)
return nilHow to run
export STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>
python examples/sre/pull-alerts.py
# or start only: python examples/sre/pull-alerts.py --no-waitnpx tsx examples/sre/pull-alerts.tsgo run ./examples/go/sre/pull-alerts.goComplete script
- Python:
examples/sre/pull-alerts.py - TypeScript:
examples/sre/pull-alerts.ts - Go:
examples/go/sre/pull-alerts.go
#!/usr/bin/env python3
"""SRE: pull fresh alerts, then list the active inbox.
Documented at:
https://appcd-dev.github.io/stackgen-sdk/code-samples/sre-pull-alerts/
Credentials (flags override env):
STACKGEN_URL, STACKGEN_TOKEN, STACKGEN_WORKSPACE_ID (or STACKGEN_ORG_ID / STACKGEN_PROJECT)
Usage:
python examples/sre/pull-alerts.py --no-wait
python examples/sre/pull-alerts.py --source grafana
"""
from __future__ import annotations
import argparse
import json
import os
import sys
from stackgen import StackgenClient, StackgenConfig
from stackgen.errors import StackgenError
def _org_id(cli_value: str | None) -> str:
return (
(cli_value or "").strip()
or os.environ.get("STACKGEN_WORKSPACE_ID", "").strip()
or os.environ.get("STACKGEN_ORG_ID", "").strip()
or os.environ.get("STACKGEN_PROJECT", "").strip()
or os.environ.get("STACKGEN_PROJECT_ID", "").strip()
)
def _config(args: argparse.Namespace) -> StackgenConfig:
base = (args.base_url or os.environ.get("STACKGEN_URL", "")).strip()
token = (args.api_token or os.environ.get("STACKGEN_TOKEN", "")).strip()
org = _org_id(args.workspace_id)
if not base or not token:
raise SystemExit(
"set --base-url/--api-token or STACKGEN_URL / STACKGEN_TOKEN"
)
return StackgenConfig(
base_url=base,
api_token=token,
workspace_id=org,
timeout_seconds=args.timeout_seconds,
poll_interval_seconds=args.poll_interval_seconds,
)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--base-url", default="")
parser.add_argument("--api-token", default="")
parser.add_argument("--workspace-id",
"--org-id", default="")
parser.add_argument("--source", default="grafana")
parser.add_argument("--integration", default="")
parser.add_argument(
"--no-wait",
action="store_true",
help="Start the pull and exit without waiting",
)
parser.add_argument("--timeout-seconds", type=int, default=1800)
parser.add_argument("--poll-interval-seconds", type=int, default=5)
args = parser.parse_args()
client = StackgenClient(_config(args))
integration = args.integration.strip() or None
if args.no_wait:
started = client.sre.pull_alerts(source=args.source, integration=integration)
print("pull:", json.dumps(started, indent=2, default=str))
return 0
finished = client.sre.pull_alerts_and_wait(
source=args.source,
integration=integration,
)
print("pull:", json.dumps(finished, indent=2, default=str))
alerts = client.sre.list_alerts(status="active", page_size=10)
print("alerts:", json.dumps(alerts, indent=2, default=str))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except StackgenError as exc:
print(f"error: {exc}", file=sys.stderr)
if "401" in str(exc) or "missing identity" in str(exc).lower():
print(
"hint: SRE /app/sre routes need the edge to inject workspace "
"identity (X-Appcd-Org). If Aiden works but SRE returns 401, "
"the mothership app proxy is not forwarding PAT identity.",
file=sys.stderr,
)
raise SystemExit(1)/**
* SRE: pull fresh alerts, then list the active inbox.
*
* Documented at:
* https://appcd-dev.github.io/stackgen-sdk/code-samples/sre-pull-alerts/
*
* Credentials (flags override env):
* STACKGEN_URL, STACKGEN_TOKEN, STACKGEN_WORKSPACE_ID (or STACKGEN_ORG_ID / STACKGEN_PROJECT)
*
* Usage:
* npx tsx examples/sre/pull-alerts.ts --no-wait
* npx tsx examples/sre/pull-alerts.ts --source grafana
*/
import { parseArgs } from "node:util";
import { StackgenClient, StackgenConfig, StackgenError } from "stackgen-sdk";
function show(label: string, value: unknown): void {
console.log(`${label}:`, JSON.stringify(value, null, 2));
}
async function main(): Promise<number> {
const { values } = parseArgs({
options: {
"base-url": { type: "string", default: "" },
"api-token": { type: "string", default: "" },
"org-id": { type: "string" },
"workspace-id": { type: "string", default: "" },
source: { type: "string", default: "grafana" },
integration: { type: "string", default: "" },
"no-wait": { type: "boolean", default: false },
"timeout-seconds": { type: "string", default: "1800" },
"poll-interval-seconds": { type: "string", default: "5" },
help: { type: "boolean", short: "h", default: false },
},
});
if (values.help) {
console.log("usage: pull-alerts.ts [--source S] [--integration I] [--no-wait]");
return 0;
}
const baseUrl = (values["base-url"] || process.env.STACKGEN_URL || "").trim();
const apiToken = (values["api-token"] || process.env.STACKGEN_TOKEN || "").trim();
const orgId = (
(values["org-id"] || values["workspace-id"]) ||
process.env.STACKGEN_WORKSPACE_ID ||
process.env.STACKGEN_ORG_ID ||
process.env.STACKGEN_PROJECT ||
process.env.STACKGEN_PROJECT_ID ||
""
).trim();
if (!baseUrl || !apiToken) {
console.error("set --base-url/--api-token or STACKGEN_URL / STACKGEN_TOKEN");
return 2;
}
const client = new StackgenClient(
new StackgenConfig({
baseUrl,
apiToken,
workspaceId: orgId,
timeoutSeconds: Number(values["timeout-seconds"]),
pollIntervalSeconds: Number(values["poll-interval-seconds"]),
}),
);
const integration = (values.integration ?? "").trim() || undefined;
if (values["no-wait"]) {
show("pull", await client.sre.pullAlerts(values.source, integration));
return 0;
}
show("pull", await client.sre.pullAlertsAndWait(values.source, integration));
show("alerts", await client.sre.listAlerts({ status: "active", page_size: 10 }));
return 0;
}
main().then(
(code) => process.exit(code),
(err) => {
console.error(`error: ${err}`);
if (
err instanceof StackgenError &&
(String(err).includes("401") || /missing identity/i.test(String(err)))
) {
console.error(
"hint: SRE /app/sre routes need the edge to inject workspace " +
"identity (X-Appcd-Org). If Aiden works but SRE returns 401, " +
"the mothership app proxy is not forwarding PAT identity.",
);
}
process.exit(1);
},
);// Pull fresh SRE alerts, then list the active inbox.
//
// Documented at https://appcd-dev.github.io/stackgen-sdk/code-samples/sre-pull-alerts/
//
// go run ./examples/go/sre/pull-alerts.go --no-wait
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
stackgen "github.com/appcd-dev/stackgen-sdk/clients/go"
)
func envOr(keys ...string) string {
for _, key := range keys {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
}
return ""
}
func main() {
baseURL := flag.String("base-url", "", "Mothership URL")
apiToken := flag.String("api-token", "", "API token")
orgID := flag.String("workspace-id", "", "Workspace UUID (sent as orgId)")
flag.StringVar(orgID, "org-id", *orgID, "Deprecated: use -workspace-id")
source := flag.String("source", "grafana", "Alert source")
integration := flag.String("integration", "", "Integration instance name")
noWait := flag.Bool("no-wait", false, "Start the pull and exit without waiting")
timeoutSeconds := flag.Int("timeout-seconds", 1800, "Wait timeout")
pollInterval := flag.Int("poll-interval-seconds", 5, "Poll interval")
flag.Parse()
base := strings.TrimSpace(*baseURL)
if base == "" {
base = envOr("STACKGEN_URL")
}
token := strings.TrimSpace(*apiToken)
if token == "" {
token = envOr("STACKGEN_TOKEN")
}
org := strings.TrimSpace(*orgID)
if org == "" {
org = envOr("STACKGEN_WORKSPACE_ID", "STACKGEN_ORG_ID", "STACKGEN_PROJECT", "STACKGEN_PROJECT_ID")
}
if base == "" || token == "" {
fmt.Fprintln(os.Stderr, "set STACKGEN_URL and STACKGEN_TOKEN")
os.Exit(2)
}
client, err := stackgen.NewClient(stackgen.Config{
BaseURL: base,
APIToken: token,
WorkspaceID: org,
TimeoutSeconds: *timeoutSeconds,
PollIntervalSeconds: *pollInterval,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
ctx := context.Background()
integ := strings.TrimSpace(*integration)
if *noWait {
started, err := client.SRE.PullAlerts(ctx, *source, integ)
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
raw, _ := json.MarshalIndent(started, "", " ")
fmt.Println("pull:", string(raw))
return
}
finished, err := client.SRE.PullAlertsAndWait(ctx, *source, integ)
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
raw, _ := json.MarshalIndent(finished, "", " ")
fmt.Println("pull:", string(raw))
alerts, err := client.SRE.ListAlerts(ctx, map[string]string{"status": "active", "page_size": "10"})
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1)
}
raw, _ = json.MarshalIndent(alerts, "", " ")
fmt.Println("alerts:", string(raw))
}Notes
- A pull is async. The start call returns
sync_run_idwith statusrunning. - If another pull is already running for the same source and integration, the server returns 409 with that run. The SDK returns it so you can keep waiting.
- Pass
integrationwhen the workspace has more than one Grafana binding.
Related: Investigate alert · Triage alerts