Webhook → session → artifact
The most common CI pattern: trigger a webhook, wait for the workflow session,
then download a session artifact (usually session-report.md).
Webhook automation needs two tokens: a webhook token (sg_aios_…) to POST the
trigger, and an API token (stackgen_…) to wait for artifacts and download reports.
flowchart LR
A[Alert or JSON body] --> B[POST webhooks/trigger]
B --> C{session_id on 202?}
C -->|yes| D[Poll session artifacts]
C -->|no| E[Poll getWebhookRun]
E --> D
D --> F[Download session-report.md]
| Step | What the SDK does |
|---|---|
| 1. Trigger | POST /guild/api/v1/webhooks/trigger with webhook token |
| 2. Session | Use session_id from 202 when present; otherwise poll run detail until set |
| 3. Artifact | Poll session artifacts, then download to a local path |
Complete scripts
- Python:
examples/webhook/webhook-download-report.py - TypeScript:
examples/webhook/webhook-download-report.ts - Go:
examples/go/webhook/webhook-download-report.go - Python:
examples/webhook/webhook-wait-session.py - TypeScript:
examples/webhook/webhook-wait-session-step.ts - Go:
examples/go/webhook/webhook-wait-session.go - TypeScript:
examples/webhook/webhook-wait-session.ts
Minimal — one SDK call (recommended)
from stackgen import StackgenClient, StackgenConfig
client = StackgenClient(
StackgenConfig(
base_url="https://app.stackgen.com",
api_token="stackgen_…",
workspace_id="<workspace-uuid>",
webhook_token="sg_aios_…",
artifact_name="session-report.md",
output_path="session-report.md",
)
)
result = client.aiden.run_webhook_and_download_report(
"P1 | checkout-api error rate above SLO"
)
print(result.session_id, result.output_path)import { StackgenClient, StackgenConfig } from "stackgen-sdk";
const client = new StackgenClient(
new StackgenConfig({
baseUrl: "https://app.stackgen.com",
apiToken: "stackgen_…",
workspaceId: "<workspace-uuid>",
webhookToken: "sg_aios_…",
outputPath: "session-report.md",
}),
);
const result = await client.aiden.runWebhookAndDownloadReport(
"P1 | checkout-api error rate above SLO",
);
console.log(result.sessionId, result.outputPath);client, err := stackgen.NewClient(stackgen.Config{
BaseURL: "https://app.stackgen.com",
APIToken: "stackgen_…",
WorkspaceID: "<workspace-uuid>",
WebhookToken: "sg_aios_…",
ArtifactName: "session-report.md",
OutputPath: "session-report.md",
})
if err != nil { return err }
result, err := client.Aiden.RunWebhookAndDownloadReport(ctx,
"P1 | checkout-api error rate above SLO")
if err != nil { return err }
fmt.Println(result.SessionID, result.OutputPath)Runnable CLI scripts
Full command-line examples with flags for CI pipelines.
#!/usr/bin/env python3
"""Webhook trigger → wait for session → download session artifact (CI-friendly).
Documented on https://appcd-dev.github.io/stackgen-sdk/code-samples/webhook-session-artifact/
under **Webhook → session → artifact**.
Usage:
export WEBHOOK_TOKEN='sg_aios_…'
python webhook-download-report.py \\
--base-url https://app.stackgen.com \\
--api-token stackgen_… \\
--workspace-id <workspace-uuid> \\
--query 'P1 | checkout-api error rate above SLO'
"""
from __future__ import annotations
import argparse
import os
import sys
from stackgen import StackgenClient, StackgenConfig
def main() -> int:
parser = argparse.ArgumentParser(
description="Trigger a webhook, wait for session_id, download session-report.md.",
)
parser.add_argument("--base-url", default=os.environ.get("STACKGEN_URL", ""), help="Mothership URL (no /guild suffix)")
parser.add_argument("--api-token", default=os.environ.get("STACKGEN_TOKEN", ""), help="API token (stackgen_…)")
parser.add_argument(
"--workspace-id",
"--org-id",
default=os.environ.get("STACKGEN_WORKSPACE_ID") or os.environ.get("STACKGEN_PROJECT") or os.environ.get("STACKGEN_PROJECT_ID", ""),
help="Workspace UUID (sent as orgId)",
)
parser.add_argument(
"--webhook-token",
default="",
help="Webhook token (sg_aios_…); or set WEBHOOK_TOKEN",
)
parser.add_argument("--query", required=True, help="Alert text or payload for the webhook")
parser.add_argument(
"--artifact-name",
default="session-report.md",
help="Session artifact filename to download",
)
parser.add_argument(
"--output-path",
default="session-report.md",
help="Local path to write the artifact",
)
parser.add_argument("--timeout-seconds", type=int, default=1800)
parser.add_argument("--poll-interval-seconds", type=int, default=5)
args = parser.parse_args()
webhook_token = (
args.webhook_token
or os.environ.get("STACKGEN_WEBHOOK_TOKEN", "")
or os.environ.get("WEBHOOK_TOKEN", "")
)
if not args.base_url or not args.api_token or not args.workspace_id:
print(
"error: set --base-url/--api-token/--workspace-id or STACKGEN_URL/STACKGEN_TOKEN/STACKGEN_WORKSPACE_ID",
file=sys.stderr,
)
return 2
if not webhook_token:
print("error: set --webhook-token or WEBHOOK_TOKEN", file=sys.stderr)
return 2
client = StackgenClient(
StackgenConfig(
base_url=args.base_url,
api_token=args.api_token,
webhook_token=webhook_token,
workspace_id=args.workspace_id,
artifact_name=args.artifact_name,
output_path=args.output_path,
timeout_seconds=args.timeout_seconds,
poll_interval_seconds=args.poll_interval_seconds,
)
)
result = client.aiden.run_webhook_and_download_report(
args.query,
artifact_name=args.artifact_name,
output_path=args.output_path,
)
print(
f"webhook_id={result.webhook_id} session_id={result.session_id} "
f"output={result.output_path}",
file=sys.stderr,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())/**
* Webhook trigger → wait for session → download session artifact (CI-friendly).
*
* Docs: https://appcd-dev.github.io/stackgen-sdk/code-samples/webhook-session-artifact/
*
* Usage:
* WEBHOOK_TOKEN='sg_aios_…' npx tsx webhook-download-report.ts \
* --base-url https://app.stackgen.com \
* --api-token stackgen_… \
* --workspace-id <workspace-uuid> \
* --query 'P1 | checkout-api error rate above SLO'
*/
import { parseArgs } from "node:util";
import { StackgenClient, StackgenConfig } from "stackgen-sdk";
async function main(): Promise<void> {
const { values } = parseArgs({
options: {
"base-url": { type: "string", default: process.env.STACKGEN_URL || "" },
"api-token": { type: "string", default: process.env.STACKGEN_TOKEN || "" },
"org-id": { type: "string" },
"workspace-id": {
type: "string",
default: process.env.STACKGEN_WORKSPACE_ID || process.env.STACKGEN_PROJECT || process.env.STACKGEN_PROJECT_ID || "",
},
"webhook-token": { type: "string" },
query: { type: "string" },
"artifact-name": { type: "string", default: "session-report.md" },
"output-path": { type: "string", default: "session-report.md" },
"timeout-seconds": { type: "string", default: "1800" },
"poll-interval-seconds": { type: "string", default: "5" },
},
});
const webhookToken =
values["webhook-token"] ||
process.env.STACKGEN_WEBHOOK_TOKEN ||
process.env.WEBHOOK_TOKEN ||
"";
if (!values["base-url"] || !values["api-token"] || !(values["org-id"] || values["workspace-id"]) || !values.query) {
throw new Error("require --base-url, --api-token, --workspace-id, --query");
}
if (!webhookToken) {
throw new Error("set --webhook-token or WEBHOOK_TOKEN");
}
const client = new StackgenClient(
new StackgenConfig({
baseUrl: values["base-url"],
apiToken: values["api-token"],
webhookToken,
workspaceId: (values["org-id"] || values["workspace-id"]),
artifactName: values["artifact-name"],
outputPath: values["output-path"],
timeoutSeconds: Number(values["timeout-seconds"]),
pollIntervalSeconds: Number(values["poll-interval-seconds"]),
}),
);
const result = await client.aiden.runWebhookAndDownloadReport(values.query, {
artifactName: values["artifact-name"],
outputPath: values["output-path"],
});
console.error(
`webhookId=${result.webhookId} sessionId=${result.sessionId} output=${result.outputPath}`,
);
}
main().catch((err) => {
console.error(err instanceof Error ? err.message : err);
process.exit(1);
});// Webhook trigger → wait for session → download session artifact.
//
// Documented on https://appcd-dev.github.io/stackgen-sdk/code-samples/webhook-session-artifact/
//
// export WEBHOOK_TOKEN='sg_aios_…'
// go run ./examples/go/webhook/webhook-download-report.go \
// -base-url https://app.stackgen.com \
// -api-token stackgen_… \
// -workspace-id <workspace-uuid> \
// -query 'P1 | checkout-api error rate above SLO'
//
// -retry-max controls how many times transient failures (network errors,
// 429, 5xx) are retried with exponential backoff before giving up.
package main
import (
"context"
"flag"
"fmt"
"os"
"strings"
"time"
"github.com/hashicorp/go-retryablehttp"
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", envOr("STACKGEN_URL", "STACKGEN_BASE_URL"), "Mothership URL (no /guild suffix)")
apiToken := flag.String("api-token", envOr("STACKGEN_TOKEN", "STACKGEN_API_TOKEN"), "API token (stackgen_…)")
orgID := flag.String("workspace-id", envOr("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT", "STACKGEN_PROJECT_ID", "STACKGEN_ORG_ID", "ORG_ID"), "Workspace UUID (sent as orgId)")
flag.StringVar(orgID, "org-id", *orgID, "Deprecated: use -workspace-id")
webhookToken := flag.String("webhook-token", "", "Webhook token (sg_aios_…); or set WEBHOOK_TOKEN")
query := flag.String("query", "", "Alert text or payload for the webhook")
artifactName := flag.String("artifact-name", "session-report.md", "Session artifact filename to download")
outputPath := flag.String("output-path", "session-report.md", "Local path to write the artifact")
timeoutSeconds := flag.Int("timeout-seconds", 1800, "Overall timeout")
pollIntervalSeconds := flag.Int("poll-interval-seconds", 5, "Poll interval")
retryMax := flag.Int("retry-max", 4, "Max retries for transient HTTP failures (network errors, 429, 5xx)")
flag.Parse()
wh := strings.TrimSpace(*webhookToken)
if wh == "" {
wh = envOr("STACKGEN_WEBHOOK_TOKEN", "WEBHOOK_TOKEN")
}
if strings.TrimSpace(*baseURL) == "" || strings.TrimSpace(*apiToken) == "" || strings.TrimSpace(*orgID) == "" {
fmt.Fprintln(os.Stderr, "error: set -base-url/-api-token/-workspace-id or STACKGEN_URL/STACKGEN_TOKEN/STACKGEN_WORKSPACE_ID")
os.Exit(2)
}
if wh == "" {
fmt.Fprintln(os.Stderr, "error: set -webhook-token or WEBHOOK_TOKEN")
os.Exit(2)
}
if strings.TrimSpace(*query) == "" {
fmt.Fprintln(os.Stderr, "error: -query is required")
os.Exit(2)
}
rc := retryablehttp.NewClient()
rc.RetryMax = *retryMax
rc.Logger = nil
httpClient := rc.StandardClient()
client, err := stackgen.NewClient(stackgen.Config{
BaseURL: *baseURL,
APIToken: *apiToken,
WebhookToken: wh,
WorkspaceID: *orgID,
ArtifactName: *artifactName,
OutputPath: *outputPath,
TimeoutSeconds: *timeoutSeconds,
PollIntervalSeconds: *pollIntervalSeconds,
HTTPClient: httpClient,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(*timeoutSeconds)*time.Second)
defer cancel()
result, err := client.Aiden.RunWebhookAndDownloadReport(ctx, *query)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "webhook_id=%s session_id=%s output=%s\n", result.WebhookID, result.SessionID, result.OutputPath)
}Step-by-step (advanced)
Use this when you need the raw trigger response, custom artifact waiting, or to download the
artifact in a separate step. Pass --download-report to add step 3 on the same run.
#!/usr/bin/env python3
"""Webhook trigger → resolve session_id (optional artifact download).
Documented at https://appcd-dev.github.io/stackgen-sdk/code-samples/webhook-session-artifact/
Usage:
export WEBHOOK_TOKEN='sg_aios_…'
python webhook-wait-session.py \\
--base-url https://app.stackgen.com \\
--api-token stackgen_… \\
--workspace-id <workspace-uuid> \\
--json-body \\
--query 'P1 | checkout-api error rate above SLO'
# Also download session-report.md when the workflow finishes:
python webhook-wait-session.py … --download-report
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import time
from pathlib import Path
from stackgen import StackgenClient, StackgenConfig
from stackgen.errors import StackgenError, TimeoutError
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--base-url", default=os.environ.get("STACKGEN_URL", ""))
parser.add_argument("--api-token", default=os.environ.get("STACKGEN_TOKEN", ""))
parser.add_argument(
"--workspace-id",
"--org-id",
default=os.environ.get("STACKGEN_WORKSPACE_ID") or os.environ.get("STACKGEN_PROJECT")
or os.environ.get("STACKGEN_PROJECT_ID", ""),
)
parser.add_argument("--webhook-token", default="", help="Or set WEBHOOK_TOKEN env")
parser.add_argument("--query", default="Test webhook from stackgen-sdk example")
parser.add_argument("--json-body", action="store_true")
parser.add_argument("--download-report", action="store_true")
parser.add_argument("--artifact-name", default="session-report.md")
parser.add_argument("--output-path", default="session-report.md")
parser.add_argument("--timeout-seconds", type=int, default=1800)
parser.add_argument("--poll-interval-seconds", type=int, default=5)
args = parser.parse_args()
webhook_token = (
args.webhook_token
or os.environ.get("STACKGEN_WEBHOOK_TOKEN", "")
or os.environ.get("WEBHOOK_TOKEN", "")
)
if not args.base_url or not args.api_token or not args.workspace_id:
print(
"error: set --base-url/--api-token/--workspace-id or "
"STACKGEN_URL/STACKGEN_TOKEN/STACKGEN_WORKSPACE_ID",
file=sys.stderr,
)
return 2
if not webhook_token:
print("error: set --webhook-token or WEBHOOK_TOKEN", file=sys.stderr)
return 2
client = StackgenClient(
StackgenConfig(
base_url=args.base_url,
api_token=args.api_token,
webhook_token=webhook_token,
workspace_id=args.workspace_id,
artifact_name=args.artifact_name,
output_path=args.output_path,
timeout_seconds=args.timeout_seconds,
poll_interval_seconds=args.poll_interval_seconds,
)
)
payload = (
json.dumps({"message": args.query, "query": args.query})
if args.json_body
else args.query
)
trigger = client.aiden.trigger_webhook(payload)
print(json.dumps(trigger, indent=2, default=str))
session_id = str(trigger.get("session_id") or "")
webhook_id = str(trigger.get("webhook_id") or "")
invocation_id = str(trigger.get("invocation_id") or "")
dispatch_run_id = str(trigger.get("run_id") or trigger.get("id") or "")
if not session_id:
if not webhook_id or not invocation_id:
raise StackgenError("trigger response missing session_id and run ids")
session_id = client.aiden.wait_for_webhook_session(
webhook_id,
invocation_id,
dispatch_run_id=dispatch_run_id,
)
print(f"session_id={session_id}", file=sys.stderr)
if args.download_report:
want = args.artifact_name.casefold()
deadline = time.monotonic() + args.timeout_seconds
while time.monotonic() < deadline:
listed = client.aiden.list_session_artifacts(session_id)
if any(
str(item.get("name") or "").casefold() == want for item in listed
):
break
time.sleep(args.poll_interval_seconds)
else:
raise TimeoutError(args.artifact_name, args.timeout_seconds)
path = client.aiden.download_session_artifact(
session_id, args.artifact_name, args.output_path
)
if not Path(path).is_file():
raise StackgenError(f"download missing file {path}")
print(f"output_path={path}", file=sys.stderr)
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except StackgenError as exc:
print(f"error: {exc}", file=sys.stderr)
raise SystemExit(1)/**
* Webhook trigger → resolve session_id (optional artifact download).
*
* Docs: https://appcd-dev.github.io/stackgen-sdk/code-samples/webhook-session-artifact/
*
* Usage:
* WEBHOOK_TOKEN='sg_aios_…' npx tsx webhook-wait-session-step.ts \
* --base-url https://app.stackgen.com \
* --api-token stackgen_… \
* --workspace-id <workspace-uuid> \
* --query 'P1 | checkout-api error rate above SLO'
*
* # Also download session-report.md when the workflow finishes:
* npx tsx webhook-wait-session-step.ts … --download-report
*/
import { existsSync } from "node:fs";
import { parseArgs } from "node:util";
import { StackgenClient, StackgenConfig, StackgenError, TimeoutError } from "stackgen-sdk";
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function main(): Promise<void> {
const { values } = parseArgs({
options: {
"base-url": { type: "string", default: process.env.STACKGEN_URL || "" },
"api-token": { type: "string", default: process.env.STACKGEN_TOKEN || "" },
"org-id": { type: "string" },
"workspace-id": {
type: "string",
default: process.env.STACKGEN_WORKSPACE_ID || process.env.STACKGEN_PROJECT || process.env.STACKGEN_PROJECT_ID || "",
},
"webhook-token": { type: "string" },
query: { type: "string", default: "Test webhook from stackgen-sdk example" },
"json-body": { type: "boolean", default: false },
"download-report": { type: "boolean", default: false },
"artifact-name": { type: "string", default: "session-report.md" },
"output-path": { type: "string", default: "session-report.md" },
"timeout-seconds": { type: "string", default: "1800" },
"poll-interval-seconds": { type: "string", default: "5" },
},
});
const webhookToken =
values["webhook-token"] ||
process.env.STACKGEN_WEBHOOK_TOKEN ||
process.env.WEBHOOK_TOKEN ||
"";
if (!values["base-url"] || !values["api-token"] || !(values["org-id"] || values["workspace-id"])) {
throw new Error("require --base-url, --api-token, --workspace-id (or STACKGEN_* env)");
}
if (!webhookToken) {
throw new Error("set --webhook-token or WEBHOOK_TOKEN");
}
const timeoutSeconds = Number(values["timeout-seconds"]);
const pollIntervalSeconds = Number(values["poll-interval-seconds"]);
const client = new StackgenClient(
new StackgenConfig({
baseUrl: values["base-url"],
apiToken: values["api-token"],
webhookToken,
workspaceId: (values["org-id"] || values["workspace-id"]),
artifactName: values["artifact-name"],
outputPath: values["output-path"],
timeoutSeconds,
pollIntervalSeconds,
}),
);
const payload = values["json-body"]
? JSON.stringify({ message: values.query, query: values.query })
: values.query!;
const trigger = await client.aiden.triggerWebhook(payload);
console.log(JSON.stringify(trigger, null, 2));
let sessionId = String(trigger.session_id ?? "");
const webhookId = String(trigger.webhook_id ?? "");
const invocationId = String(trigger.invocation_id ?? "");
const dispatchRunId = String(trigger.run_id ?? trigger.id ?? "");
if (!sessionId) {
if (!webhookId || !invocationId) {
throw new StackgenError("trigger response missing session_id and run ids");
}
sessionId = await client.aiden.waitForWebhookSession(webhookId, invocationId, {
dispatchRunId,
});
}
console.error(`sessionId=${sessionId}`);
if (values["download-report"]) {
const want = values["artifact-name"]!.toLowerCase();
const deadline = Date.now() + timeoutSeconds * 1000;
while (Date.now() < deadline) {
const listed = await client.aiden.listSessionArtifacts(sessionId);
if (
listed.some(
(item) => String(item.name ?? "").toLowerCase() === want,
)
) {
break;
}
await sleep(pollIntervalSeconds * 1000);
}
if (Date.now() >= deadline) {
throw new TimeoutError(values["artifact-name"]!, timeoutSeconds);
}
const path = await client.aiden.downloadSessionArtifact(
sessionId,
values["artifact-name"]!,
values["output-path"]!,
);
if (!existsSync(path)) {
throw new StackgenError(`download missing file ${path}`);
}
console.error(`outputPath=${path}`);
}
}
main().catch((err) => {
console.error(err instanceof Error ? err.message : err);
process.exit(1);
});// Webhook trigger → resolve session_id (optional artifact download).
//
// Documented at https://appcd-dev.github.io/stackgen-sdk/code-samples/webhook-session-artifact/
//
// export WEBHOOK_TOKEN='sg_aios_…'
// go run ./examples/go/webhook/webhook-wait-session.go \
// -base-url https://app.stackgen.com \
// -api-token stackgen_… \
// -workspace-id <workspace-uuid> \
// -json-body \
// -query 'P1 | checkout-api error rate above SLO'
//
// -retry-max controls how many times the underlying HTTP client retries
// transient failures (network errors, 429, 5xx) with exponential backoff.
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
"time"
stackgen "github.com/appcd-dev/stackgen-sdk/clients/go"
"github.com/hashicorp/go-retryablehttp"
)
func envOr(keys ...string) string {
for _, key := range keys {
if v := strings.TrimSpace(os.Getenv(key)); v != "" {
return v
}
}
return ""
}
func text(m map[string]any, keys ...string) string {
for _, key := range keys {
if v, ok := m[key]; ok {
if s, ok := v.(string); ok && strings.TrimSpace(s) != "" {
return strings.TrimSpace(s)
}
}
}
return ""
}
func main() {
baseURL := flag.String("base-url", envOr("STACKGEN_URL", "STACKGEN_BASE_URL"), "Mothership URL")
apiToken := flag.String("api-token", envOr("STACKGEN_TOKEN", "STACKGEN_API_TOKEN"), "API token")
orgID := flag.String("workspace-id", envOr("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT", "STACKGEN_PROJECT_ID", "STACKGEN_ORG_ID", "ORG_ID"), "Workspace UUID (sent as orgId)")
flag.StringVar(orgID, "org-id", *orgID, "Deprecated: use -workspace-id")
webhookToken := flag.String("webhook-token", "", "Or set WEBHOOK_TOKEN")
query := flag.String("query", "Test webhook from stackgen-sdk example", "Alert text")
jsonBody := flag.Bool("json-body", false, "Send {\"message\",\"query\"} JSON instead of plain text")
downloadReport := flag.Bool("download-report", false, "Also download the session artifact")
artifactName := flag.String("artifact-name", "session-report.md", "Artifact filename")
outputPath := flag.String("output-path", "session-report.md", "Local output path")
timeoutSeconds := flag.Int("timeout-seconds", 1800, "Overall timeout")
pollIntervalSeconds := flag.Int("poll-interval-seconds", 5, "Poll interval")
retryMax := flag.Int("retry-max", 4, "Max HTTP retries with exponential backoff for transient failures")
flag.Parse()
wh := strings.TrimSpace(*webhookToken)
if wh == "" {
wh = envOr("STACKGEN_WEBHOOK_TOKEN", "WEBHOOK_TOKEN")
}
if strings.TrimSpace(*baseURL) == "" || strings.TrimSpace(*apiToken) == "" || strings.TrimSpace(*orgID) == "" {
fmt.Fprintln(os.Stderr, "error: set -base-url/-api-token/-workspace-id or STACKGEN_URL/STACKGEN_TOKEN/STACKGEN_WORKSPACE_ID")
os.Exit(2)
}
if wh == "" {
fmt.Fprintln(os.Stderr, "error: set -webhook-token or WEBHOOK_TOKEN")
os.Exit(2)
}
rc := retryablehttp.NewClient()
rc.RetryMax = *retryMax
rc.Logger = nil
httpClient := rc.StandardClient()
client, err := stackgen.NewClient(stackgen.Config{
BaseURL: *baseURL,
APIToken: *apiToken,
WebhookToken: wh,
WorkspaceID: *orgID,
ArtifactName: *artifactName,
OutputPath: *outputPath,
TimeoutSeconds: *timeoutSeconds,
PollIntervalSeconds: *pollIntervalSeconds,
HTTPClient: httpClient,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
payload := *query
if *jsonBody {
raw, _ := json.Marshal(map[string]string{"message": *query, "query": *query})
payload = string(raw)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(*timeoutSeconds)*time.Second)
defer cancel()
trigger, err := client.Aiden.TriggerWebhook(ctx, payload)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
_ = enc.Encode(trigger)
sessionID := text(trigger, "session_id", "sessionId")
webhookID := text(trigger, "webhook_id", "webhookId")
invocationID := text(trigger, "invocation_id", "invocationId")
dispatchRunID := text(trigger, "run_id", "runId", "id")
if sessionID == "" {
if webhookID == "" || invocationID == "" {
fmt.Fprintln(os.Stderr, "trigger response missing session_id and run ids")
os.Exit(1)
}
sessionID, err = client.Aiden.WaitForWebhookSession(ctx, webhookID, invocationID, dispatchRunID, nil)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
fmt.Fprintf(os.Stderr, "session_id=%s\n", sessionID)
if *downloadReport {
if _, err := client.Aiden.WaitForSessionArtifact(ctx, sessionID, *artifactName); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
path, err := client.Aiden.DownloadSessionArtifact(ctx, sessionID, *artifactName, *outputPath)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if _, err := os.Stat(path); err != nil {
fmt.Fprintf(os.Stderr, "download missing file %s: %v\n", path, err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "downloaded %s\n", path)
}
}Related: Webhook → session only — same trigger flow without downloading.