Create a custom workflow (coding-style)
Compose an agent and workflow in Go, TypeScript, or Python, then publish it with one
SDK call. Stages chain with .then(). You describe the steps in code instead of
editing YAML by hand. Go names are CreateAgent, CreateStage, CreateWorkflow,
and PublishWorkflow.
| Step | What you write |
|---|---|
| 1. Agent | createAgent({ name, persona, … }) |
| 2. Stage | createStage({ id, agent, note, needs? }) |
| 3. Workflow | createWorkflow({ name, domain, … }).then(stage).commit() |
| 4. Publish | client.aiden.publishWorkflow(workflow, { agents, approve: true }) |
Requires an API token and workspace workspace_id. Agents are registered (or updated
on conflict); the workflow is created or updated as a draft, then approved so it
can run.
Minimal — compose and publish
from stackgen import (
StackgenClient,
StackgenConfig,
create_agent,
create_stage,
create_workflow,
)
client = StackgenClient(
StackgenConfig(
base_url="https://app.stackgen.com",
api_token="stackgen_…",
workspace_id="<workspace-uuid>",
)
)
agent = create_agent(
name="hello-agent",
persona="Turn a short topic into three clear bullets.",
always_allowed_tools=["note"],
auto_approve_tools=[{"tool": "note"}],
)
workflow = (
create_workflow(
name="hello-workflow",
domain="product",
required_inputs=["topic"],
)
.then(
create_stage(
id="summarize",
agent="hello-agent",
note="Read required input `topic`. Reply with exactly three bullets.",
)
)
.commit()
)
result = client.aiden.publish_workflow(workflow, agents=[agent], approve=True)
print(result.workflow_name, result.version, result.status)import {
StackgenClient,
StackgenConfig,
createAgent,
createStage,
createWorkflow,
} from "stackgen-sdk";
const client = new StackgenClient(
new StackgenConfig({
baseUrl: "https://app.stackgen.com",
apiToken: "stackgen_…",
workspaceId: "<workspace-uuid>",
}),
);
const agent = createAgent({
name: "hello-agent",
persona: "Turn a short topic into three clear bullets.",
alwaysAllowedTools: ["note"],
autoApproveTools: [{ tool: "note" }],
});
const workflow = createWorkflow({
name: "hello-workflow",
domain: "product",
requiredInputs: ["topic"],
})
.then(
createStage({
id: "summarize",
agent: "hello-agent",
note: "Read required input `topic`. Reply with exactly three bullets.",
}),
)
.commit();
const result = await client.aiden.publishWorkflow(workflow, {
agents: [agent],
approve: true,
});
console.log(result.workflowName, result.version, result.status);agent, err := aiden.CreateAgent("hello-agent",
"Turn a short topic into three clear bullets.",
aiden.AgentOptions{
AlwaysAllowedTools: []string{"note"},
AutoApproveTools: []map[string]string{{"tool": "note"}},
},
)
if err != nil { return err }
stage, err := aiden.CreateStage("summarize", "hello-agent", aiden.StageOptions{
Note: "Read required input `topic`. Reply with exactly three bullets.",
})
if err != nil { return err }
workflow, err := aiden.CreateWorkflow("hello-workflow", "product", aiden.WorkflowOptions{
RequiredInputs: []string{"topic"},
}).Then(stage).Commit()
if err != nil { return err }
result, err := client.Aiden.PublishWorkflow(ctx, workflow, aiden.PublishOptions{
Agents: []aiden.AgentDefinition{agent},
Approve: true,
})
if err != nil { return err }
fmt.Println(result.WorkflowName, result.Version, result.Status)
return nilChain a second stage
Add another agent and stage. Set needs: ["summarize"] (Python needs=["summarize"])
so the second stage waits for the first — the SDK maps that to the Aiden depends_on field.
critique_agent = create_agent(
name="hello-critic",
persona="Critique a short summary for gaps. Stay brief.",
always_allowed_tools=["note"],
)
workflow = (
create_workflow(name="hello-workflow", domain="product", required_inputs=["topic"])
.then(create_stage(id="summarize", agent="hello-agent", note="Three bullets."))
.then(
create_stage(
id="critique",
agent="hello-critic",
needs=["summarize"],
note="Read the summarize output. List up to three improvements.",
)
)
.commit()
)const critiqueAgent = createAgent({
name: "hello-critic",
persona: "Critique a short summary for gaps. Stay brief.",
alwaysAllowedTools: ["note"],
});
const workflow = createWorkflow({
name: "hello-workflow",
domain: "product",
requiredInputs: ["topic"],
})
.then(createStage({ id: "summarize", agent: "hello-agent", note: "Three bullets." }))
.then(
createStage({
id: "critique",
agent: "hello-critic",
needs: ["summarize"],
note: "Read the summarize output. List up to three improvements.",
}),
)
.commit();critique, err := aiden.CreateAgent("hello-critic",
"Critique a short summary for gaps. Stay brief.",
aiden.AgentOptions{AlwaysAllowedTools: []string{"note"}},
)
if err != nil { return err }
_ = critique
summarize, err := aiden.CreateStage("summarize", "hello-agent", aiden.StageOptions{
Note: "Three bullets.",
})
if err != nil { return err }
review, err := aiden.CreateStage("critique", "hello-critic", aiden.StageOptions{
Needs: []string{"summarize"},
Note: "Read the summarize output. List up to three improvements.",
})
if err != nil { return err }
workflow, err := aiden.CreateWorkflow("hello-workflow", "product", aiden.WorkflowOptions{
RequiredInputs: []string{"topic"},
}).Then(summarize).Then(review).Commit()
return errRunnable CLI
- Python:
examples/create-workflow/create-hello-workflow.py - TypeScript:
examples/create-workflow/create-hello-workflow.ts - Go:
examples/go/create-workflow/create-hello-workflow.go
#!/usr/bin/env python3
"""Create a custom hello workflow with the StackGen SDK (coding-style).
Usage:
python examples/create-workflow/create-hello-workflow.py \\
--base-url https://app.stackgen.com \\
--api-token stackgen_… \\
--workspace-id <workspace-uuid>
"""
from __future__ import annotations
import argparse
import os
import sys
from stackgen import (
StackgenClient,
StackgenConfig,
create_agent,
create_stage,
create_workflow,
)
def main() -> int:
p = argparse.ArgumentParser(description=__doc__)
p.add_argument("--base-url", default=os.environ.get("STACKGEN_URL", ""))
p.add_argument("--api-token", default=os.environ.get("STACKGEN_TOKEN", ""))
p.add_argument("--workspace-id",
"--org-id", default=os.environ.get("STACKGEN_WORKSPACE_ID", "") or os.environ.get("STACKGEN_PROJECT", ""))
p.add_argument("--agent-name", default="hello-agent")
p.add_argument("--workflow-name", default="hello-workflow")
args = p.parse_args()
if not args.base_url or not args.api_token:
print("Need --base-url and --api-token (or STACKGEN_URL / STACKGEN_TOKEN)", file=sys.stderr)
return 1
client = StackgenClient(
StackgenConfig(
base_url=args.base_url,
api_token=args.api_token,
workspace_id=args.workspace_id,
)
)
hello_agent = create_agent(
name=args.agent_name,
persona=(
"You turn a short topic into a clear three-bullet summary.\n"
"Stay concrete. Do not invent sources."
),
always_allowed_tools=["note"],
auto_approve_tools=[{"tool": "note"}],
)
summarize = create_stage(
id="summarize",
agent=args.agent_name,
description="Summarize the topic in three bullets.",
note=(
"Read the workflow required input `topic`.\n"
"Reply with exactly three bullets, then stop."
),
)
workflow = (
create_workflow(
name=args.workflow_name,
domain="product",
required_inputs=["topic"],
tags=["example"],
)
.then(summarize)
.commit()
)
published = client.aiden.publish_workflow(
workflow,
agents=[hello_agent],
approve=True,
)
print(
f"published {published.workflow_name} v{published.version} ({published.status})"
)
print(f"run with: stackgen ai run workflow/{args.workflow_name} --input 'topic=…'")
return 0
if __name__ == "__main__":
raise SystemExit(main())/**
* Create a custom hello workflow with the StackGen SDK (coding-style).
*
* Usage:
* npx tsx examples/create-workflow/create-hello-workflow.ts \
* --base-url https://app.stackgen.com \
* --api-token stackgen_… \
* --workspace-id <workspace-uuid>
*/
import {
StackgenClient,
StackgenConfig,
createAgent,
createStage,
createWorkflow,
} from "stackgen-sdk";
function arg(name: string, fallback = ""): string {
const idx = process.argv.indexOf(name);
const value = idx >= 0 ? process.argv[idx + 1] : undefined;
return value || fallback;
}
const baseUrl = arg("--base-url", process.env.STACKGEN_URL || "");
const apiToken = arg("--api-token", process.env.STACKGEN_TOKEN || "");
const orgId = arg("--org-id", process.env.STACKGEN_WORKSPACE_ID || process.env.STACKGEN_PROJECT || "");
const agentName = arg("--agent-name", "hello-agent");
const workflowName = arg("--workflow-name", "hello-workflow");
if (!baseUrl || !apiToken) {
console.error("Need --base-url and --api-token (or STACKGEN_URL / STACKGEN_TOKEN)");
process.exit(1);
}
const client = new StackgenClient(
new StackgenConfig({ baseUrl, apiToken, workspaceId: orgId }),
);
const helloAgent = createAgent({
name: agentName,
persona: `You turn a short topic into a clear three-bullet summary.
Stay concrete. Do not invent sources.`,
alwaysAllowedTools: ["note"],
autoApproveTools: [{ tool: "note" }],
});
const summarize = createStage({
id: "summarize",
agent: agentName,
description: "Summarize the topic in three bullets.",
note: `Read the workflow required input \`topic\`.
Reply with exactly three bullets, then stop.`,
});
const workflow = createWorkflow({
name: workflowName,
domain: "product",
requiredInputs: ["topic"],
tags: ["example"],
})
.then(summarize)
.commit();
const published = await client.aiden.publishWorkflow(workflow, {
agents: [helloAgent],
approve: true,
});
console.log(
`published ${published.workflowName} v${published.version} (${published.status})`,
);
console.log(`run with: stackgen ai run workflow/${workflowName} --input 'topic=…'`);
console.log(`or: client.aiden.runAskAndDownloadArtifact(…, { entityRefs: [{ name: '${workflowName}', type: 'workflow' }], inputs: { topic: '…' } })`);// Publish a hello workflow from Go.
//
// Documented at https://appcd-dev.github.io/stackgen-sdk/code-samples/create-workflow/
//
// go run ./examples/go/create-workflow/create-hello-workflow.go
package main
import (
"context"
"flag"
"fmt"
"os"
"strings"
stackgen "github.com/appcd-dev/stackgen-sdk/clients/go"
"github.com/appcd-dev/stackgen-sdk/clients/go/aiden"
)
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"), "Mothership URL")
apiToken := flag.String("api-token", envOr("STACKGEN_TOKEN"), "API token")
orgID := flag.String("workspace-id", envOr("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT"), "Workspace UUID (sent as orgId)")
flag.StringVar(orgID, "org-id", *orgID, "Deprecated: use -workspace-id")
agentName := flag.String("agent-name", "hello-agent", "Agent name")
workflowName := flag.String("workflow-name", "hello-workflow", "Workflow name")
flag.Parse()
if *baseURL == "" || *apiToken == "" {
fmt.Fprintln(os.Stderr, "Need --base-url and --api-token (or STACKGEN_URL / STACKGEN_TOKEN)")
os.Exit(1)
}
client, err := stackgen.NewClient(stackgen.Config{BaseURL: *baseURL, APIToken: *apiToken, WorkspaceID: *orgID})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
agent, err := aiden.CreateAgent(*agentName, "You turn a short topic into a clear three-bullet summary.\nStay concrete. Do not invent sources.", aiden.AgentOptions{
AlwaysAllowedTools: []string{"note"},
AutoApproveTools: []map[string]string{{"tool": "note"}},
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
stage, err := aiden.CreateStage("summarize", *agentName, aiden.StageOptions{
Description: "Summarize the topic in three bullets.",
Note: "Read the workflow required input `topic`.\nReply with exactly three bullets, then stop.",
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
workflow, err := aiden.CreateWorkflow(*workflowName, "product", aiden.WorkflowOptions{
RequiredInputs: []string{"topic"},
Tags: []string{"example"},
}).Then(stage).Commit()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
published, err := client.Aiden.PublishWorkflow(context.Background(), workflow, aiden.PublishOptions{
Agents: []aiden.AgentDefinition{agent},
Approve: true,
})
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("published %s v%d (%s)\n", published.WorkflowName, published.Version, published.Status)
fmt.Printf("run with: stackgen ai run workflow/%s --input 'topic=…'\n", *workflowName)
}After publish, run with Ask (same SDK) or the CLI:
stackgen ai run workflow/hello-workflow --input 'topic=on-call handoff notes'
Related: Ask → artifact · YAML apply alternate in Kinded YAML apply