Slack Handles

A SlackHandle reserves a Slack user-group @handle for Firetiger inside a specific Slack workspace (identified by its Connection). Once a handle exists and the Slack user group has been provisioned, you can create SlackAgentMentioned triggers that route @-mentions of that handle to specific agents.

Service: firetiger.slackhandles.v1.SlackHandlesService

Resource name pattern: connections/{connection}/slack-handles/{slack_handle}

Access: Read-write

Resource type: SlackHandle

Why a separate resource

  • Identity vs routing: the handle is an identity (who can be paged in Slack); a trigger is the routing rule (which agent responds, on what channels). Separating them lets a trigger be edited/enabled/disabled without churning the Slack user group, and lets the same handle fan out to multiple agents via multiple triggers.
  • Workspace scope: @on-call in two different workspaces are legitimately different identities — the parent-child naming (connections/{connection}/slack-handles/{slack_handle}) makes that explicit.

Prerequisites

The parent Connection must be a Slack connection (CONNECTION_TYPE_SLACK) whose stored OAuth scopes include both usergroups:read and usergroups:write. Missing scopes return a structured FailedPrecondition with reason = SLACK_USERGROUP_SCOPE_MISSING.

Example flow

1. Create a handle

Reserves the @-name in the workspace and provisions the backing Slack user group. The call is idempotent on the Slack side (bot-owned groups with the same handle are reused), and the service enforces one SlackHandle row per (connection, handle) pair.

curl -X POST "https://api.cloud.firetiger.com/firetiger.slackhandles.v1.SlackHandlesService/CreateSlackHandle" \
  -u "$USERNAME:$PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{
    "parent": "connections/slack-prod",
    "slack_handle_id": "on-call",
    "slack_handle": {
      "handle": "on-call"
    }
  }'
{
  "slackHandle": {
    "name": "connections/slack-prod/slack-handles/on-call",
    "handle": "on-call",
    "userGroupId": "S0123ABCDEF",
    "createTime": "2026-04-23T08:28:56Z",
    "updateTime": "2026-04-23T08:28:56Z"
  }
}

2. Wire the handle to an agent via a trigger

curl -X POST "https://api.cloud.firetiger.com/firetiger.triggers.v1.TriggersService/CreateTrigger" \
  -u "$USERNAME:$PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{
    "trigger_id": "on-call-mentions",
    "trigger": {
      "display_name": "On-call @mentions",
      "agent": "agents/on-call",
      "enabled": true,
      "configuration": {
        "slack_agent_mentioned": {
          "slack_handle": "connections/slack-prod/slack-handles/on-call",
          "channels": ["#on-call"]
        }
      }
    }
  }'

3. List handles on a workspace

curl -X POST "https://api.cloud.firetiger.com/firetiger.slackhandles.v1.SlackHandlesService/ListSlackHandles" \
  -u "$USERNAME:$PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{"parent": "connections/slack-prod"}'

4. Delete a handle

Refused while any trigger still references the handle — delete those first. Once clear, the Slack user group is disabled best-effort after the Firetiger row is soft-deleted.

curl -X POST "https://api.cloud.firetiger.com/firetiger.slackhandles.v1.SlackHandlesService/DeleteSlackHandle" \
  -u "$USERNAME:$PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{"name": "connections/slack-prod/slack-handles/on-call"}'

Handle drift

The handle string is a snapshot captured at create time. Routing from Slack mentions is keyed off user_group_id, which is stable across renames — so if a Slack admin renames the backing user group out-of-band, mentions continue to route correctly but GetSlackHandle keeps reporting the original handle. The API rejects attempts to change handle in place; the reconcile path is to delete the SlackHandle and create a new one (bot-owned groups are reclaimed idempotently by CreateOrGetByHandle, so you don’t lose your Slack user-group ID).

Co-mentions with @firetiger

If both @firetiger and a custom Slack handle appear in a single message (e.g. @firetiger @on-call help), only the custom handle’s trigger fires. The default @firetiger investigation path is suppressed to avoid double-responding to the same message. To get both, send two separate messages.

Structured errors

CreateSlackHandle maps Slack Web-API failures to google.rpc.ErrorInfo with stable reason codes so clients can branch without parsing messages:

Reason Meaning Remediation
SLACK_USERGROUP_SCOPE_MISSING Connection lacks usergroups:read/usergroups:write Reinstall the Slack app with the updated scopes
SLACK_USERGROUP_RESTRICTED Workspace plan or admin policy blocks user-group management Admin action outside the app (e.g. upgrade plan, lift restriction)
SLACK_USERGROUP_HANDLE_TAKEN_NOT_BOT_OWNED A human-created user group already holds the handle Choose a different handle, or have a workspace admin transfer/delete the existing group
SLACK_CONNECTION_UNAUTHORIZED Stored bot token is invalid/revoked Reinstall the Slack connection

This site uses Just the Docs, a documentation theme for Jekyll.