← All code samples

Chat subscriptions

Your SRE app (or any installed catalog app) should not be a free-for-all across every Slack channel. A chat subscription is the routing rule: which workspace channels, which triggers (mention, alert bot), and which installed app receives the normalized event.

You create and manage those rules from the SDK the same way you already create webhooks and schedules. Provision them in a bootstrap job, flip them off for a noisy channel, or tear them down when a temporary war-room channel closes.

Aiden fills the POST path from the app’s discovery manifest (chat_subscriptions[].endpoint_path). If the app declares more than one app-target route, pass bundle_name (that route’s manifest name).

When you need this

  • Stand up a new on-call channel. #checkout-sev should mention-route into your SRE app without clicking through the UI for every environment.
  • Silence without uninstalling. Toggle enabled: false during a planned load test so alert bots do not open fifty threads.
  • Ephemeral war rooms. Create a subscription for a temporary channel when an incident starts; delete it when the room archives.
  • Multi-app orgs. Payroll bots and SRE bots share Slack. Subscriptions keep each app’s mentions and alert bots on their own app.
  • Infra as code for chat. Treat channel routing like webhooks: create in CI, assert it exists in a smoke test, delete on teardown.

If you only use the product UI once and never change routing, you may not need the API. The moment routing is part of how you ship environments, you want it in code.

How to run

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

pip install stackgen-sdk==0.3.0
python examples/automation/chat-subscriptions.py --app-name sre

Optional: --channel-id C0123456789 to scope the selector to one channel.

Minimal

from stackgen import StackgenClient, StackgenConfig

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

# Point @mentions in selected channels at your installed SRE app.
# Aiden fills endpoint_path from the app discovery manifest.
sub = client.aiden.create_chat_subscription({
    "name": "checkout-sev-mentions",
    "app_name": "sre",
    "target_type": "app",
    "target_name": "sre",
    "channel_selector": {"channel_ids": ["C0123456789"]},
    "trigger_types": ["mention"],
    "enabled": True,
})

# Quiet the route without deleting it (load test window, maintenance).
client.aiden.toggle_chat_subscription(sub["id"], {"enabled": False})

# War room closed.
client.aiden.delete_chat_subscription(sub["id"])

Complete scripts

#!/usr/bin/env python3
"""Provision Slack/Teams routing for an installed app from code.

Chat subscriptions decide which channels and triggers land on which app.
Aiden resolves the app POST path from the installed app's discovery
manifest. Use them to stand up an on-call channel, mute a route during a
load test, or tear down a war-room subscription when the room closes.

Requires STACKGEN_URL, STACKGEN_TOKEN, and STACKGEN_WORKSPACE_ID.
The default target is an installed app. Pass --target-type agent --agent to
route the same subscription lifecycle at an agent instead.

Usage:
  export STACKGEN_URL=https://app.stackgen.com
  export STACKGEN_TOKEN=stackgen_…
  export STACKGEN_WORKSPACE_ID=<workspace-uuid>
  python examples/automation/chat-subscriptions.py --app-name sre
  python examples/automation/chat-subscriptions.py --target-type agent --agent my-oncall-agent
"""

from __future__ import annotations

import argparse
import os
import sys
import uuid

from stackgen import StackgenClient, StackgenConfig


def env(name: str, *fallbacks: str) -> str:
    for key in (name, *fallbacks):
        value = (os.environ.get(key) or "").strip()
        if value:
            return value
    raise SystemExit(f"missing env {name}")


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--app-name",
        default="",
        help="Installed catalog app name. Required when --target-type is app.",
    )
    parser.add_argument(
        "--target-type",
        choices=("app", "agent"),
        default="app",
        help="Route matching chat events to an installed app or an agent",
    )
    parser.add_argument(
        "--agent",
        default="",
        help="Agent name. Required when --target-type is agent.",
    )
    parser.add_argument(
        "--channel-id",
        default="",
        help="Optional Slack/Teams channel id to constrain the selector",
    )
    parser.add_argument(
        "--bundle-name",
        default="",
        help="Optional AppChatSubscriptionSpec.name when the app declares multiple routes",
    )
    args = parser.parse_args()
    if args.target_type == "agent":
        if not args.agent:
            parser.error("--agent is required when --target-type agent")
        target_name = args.agent
    else:
        if not args.app_name:
            parser.error("--app-name is required when --target-type app")
        target_name = args.app_name

    client = StackgenClient(
        StackgenConfig(
            base_url=env("STACKGEN_URL"),
            api_token=env("STACKGEN_TOKEN"),
            workspace_id=env("STACKGEN_WORKSPACE_ID", "STACKGEN_PROJECT"),
        )
    )
    suffix = uuid.uuid4().hex[:8]
    body: dict = {
        "name": f"sdk-chat-sub-{suffix}",
        "target_type": args.target_type,
        "target_name": target_name,
        # channel_selector is required; tag matches Active-channels routing.
        "channel_selector": (
            {"channel_ids": [args.channel_id]}
            if args.channel_id
            else {"tag": "sdk-ci"}
        ),
        "trigger_types": ["mention"],
        "enabled": True,
    }
    if args.app_name:
        body["app_name"] = args.app_name
    if args.bundle_name:
        body["bundle_name"] = args.bundle_name

    created = client.aiden.create_chat_subscription(body)
    sub_id = str(created.get("id") or "")
    if not sub_id:
        raise SystemExit(f"create_chat_subscription missing id: {created}")
    print(f"created subscription id={sub_id} target_type={args.target_type}")

    try:
        got = client.aiden.get_chat_subscription(sub_id)
        print(f"get enabled={got.get('enabled')} endpoint={got.get('endpoint_path')}")

        list_kwargs = {"app_name": args.app_name} if args.app_name else {}
        listed = client.aiden.list_chat_subscriptions(**list_kwargs)
        print(f"list count={len(listed)}")

        toggled = client.aiden.toggle_chat_subscription(sub_id, {"enabled": False})
        print(f"toggled enabled={toggled.get('enabled')}")
    finally:
        try:
            client.aiden.delete_chat_subscription(sub_id)
            print(f"deleted subscription id={sub_id}")
        except Exception as exc:  # noqa: BLE001
            print(f"delete subscription id={sub_id} failed: {exc}", file=sys.stderr)
            raise

    return 0


if __name__ == "__main__":
    raise SystemExit(main())