← All code samples

DevOps → sync Ops Inbox

Pull tickets from Linear, Jira, or ServiceNow into the Ops Inbox, then list requests with status and matched workflow.

Step What the SDK does
1. Sync sync_requests (ticket pull finishes in this call)
2. List list_requests

Classification keeps running after sync. Use approve request when a row reaches awaiting_approval.

Request status

Status Meaning
new, triaging Still classifying. Use wait_for_triage.
awaiting_approval Ready to approve.
triage_stopped Operator stopped triage.
approved, executing Run in flight.
completed, deferred, closed Settled.

Minimal

from stackgen import StackgenClient, StackgenConfig

client = StackgenClient(
    StackgenConfig(
        base_url="https://app.stackgen.com",
        api_token="stackgen_…",
        workspace_id="<workspace-uuid>",
    )
)

print(client.devops.sync_requests())
listed = client.devops.list_requests(page_size=20)
for row in listed.get("requests") or []:
    print(row["identifier"], row["status"], row.get("matched_workflow_name"), row["title"])

How to run

export STACKGEN_URL=https://app.stackgen.com
export STACKGEN_TOKEN=stackgen_…
export STACKGEN_WORKSPACE_ID=<workspace-uuid>

python examples/devops/sync-inbox.py
# read-only: python examples/devops/sync-inbox.py --no-sync

Complete script

#!/usr/bin/env python3
"""DevOps: sync tickets into the Ops Inbox and list requests.

Documented at:
  https://appcd-dev.github.io/stackgen-sdk/code-samples/devops-sync-inbox/

Usage:
  python examples/devops/sync-inbox.py --no-sync
  python examples/devops/sync-inbox.py
"""

from __future__ import annotations

import argparse
import json
import os
import sys

from stackgen import StackgenClient, StackgenConfig
from stackgen.errors import StackgenError


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 = (
        (args.workspace_id 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()
    )
    if not base or not token:
        raise SystemExit("set STACKGEN_URL / STACKGEN_TOKEN")
    return StackgenConfig(base_url=base, api_token=token, workspace_id=org)


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("--no-sync", action="store_true")
    parser.add_argument("--status", default="")
    parser.add_argument("--q", default="")
    args = parser.parse_args()

    client = StackgenClient(_config(args))
    if not args.no_sync:
        sync = client.devops.sync_requests()
        print("sync:", json.dumps(sync, indent=2, default=str))

    params: dict = {"page_size": 20}
    if args.status:
        params["status"] = args.status
    if args.q:
        params["q"] = args.q
    listed = client.devops.list_requests(**params)
    print("requests:", json.dumps(listed, indent=2, default=str))
    rows = []
    if isinstance(listed, dict):
        rows = [r for r in (listed.get("requests") or []) if isinstance(r, dict)]
    for row in rows:
        print(
            f"{row.get('identifier')}\t{row.get('status')}\t"
            f"{row.get('matched_workflow_name')}\t{row.get('title')}",
            file=sys.stderr,
        )
    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):
            print(
                "hint: DevOps /app/devops routes need the edge to inject workspace identity.",
                file=sys.stderr,
            )
        raise SystemExit(1)

Related: Approve request · Discover workflows