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-callin 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 |