Slack

SlackService sends messages to Slack channels through a configured Connection, resolving channel names to IDs server-side from the bot’s channel membership so callers never page Slack’s workspace directory themselves. It also exposes a connection health check and a channel listing.

Service: firetiger.slack.v1.SlackService

Access: Read-write (requires a Slack Connection, CONNECTION_TYPE_SLACK)

Why this service

Slack has no name→ID lookup API. Paging the entire workspace directory (conversations.list) to find a channel is rate-limited (Tier 2, ~20 req/min) and, on a large workspace, cannot reach a deep channel within any reasonable budget. So SlackService resolves names against users.conversations — the (small) set of channels the bot is a member of — instead. A channel a human has invited the Firetiger app into resolves in ~1 page; a channel the bot is not in returns CHANNEL_NOT_FOUND with instructions to invite the app. Membership is the source of truth for what the bot may post to — there is no configured allowlist. The service still hard-blocks #general and DMs server-side, and makes sends idempotent so retries don’t double-post.

SendSlackMessage

Resolves each target channel (against the bot’s member channels), enforces the #general/no-DM rules, and posts. Provide an idempotency_key to make a retried request replay the original result instead of re-posting.

curl -X POST "https://api.cloud.firetiger.com/firetiger.slack.v1.SlackService/SendSlackMessage" \
  -u "$USERNAME:$PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{
    "connection": "connections/slack-prod",
    "targets": [{ "channel": "#alerts" }],
    "text": "Deploy v1.2.3 completed",
    "idempotency_key": "deploy-v1.2.3-notify"
  }'
{
  "results": [
    {
      "channel": "#alerts",
      "channelId": "C0123ABCD",
      "ts": "1718384000.000100",
      "permalink": "https://acme.slack.com/archives/C0123ABCD/p1718384000000100"
    }
  ]
}

Targets carry the channel plus optional threading: { "channel": "#alerts", "thread_ts": "...", "reply_broadcast": true }. Pass blocks (Slack Block Kit JSON) instead of, or alongside, text.

Per-channel errors are returned inline rather than failing the whole call. Each failed result carries a typed error.kind:

kind Meaning
CHANNEL_NOT_FOUND The bot is not a member of any channel with that name (or no such channel exists). The detail tells the user to invite the Firetiger app to the channel (e.g. /invite @Firetiger).
NOT_ALLOWED Channel is #general or a DM (both are never allowed).
RATE_LIMITED Slack rate-limited the operation after retries.
THREAD_STALE thread_ts referenced a deleted parent; resend without it.
SLACK_API_ERROR Any other Slack API error.

The #general ban and the no-DM rule are enforced server-side and cannot be bypassed by passing a raw channel ID. Beyond those, channel membership governs what the bot can post to — a channel the bot is not in simply does not resolve.

TestSlackConnection

Health-checks a connection without walking the channel directory — auth.test plus a scope diff against the app’s required scopes.

curl -X POST "https://api.cloud.firetiger.com/firetiger.slack.v1.SlackService/TestSlackConnection" \
  -u "$USERNAME:$PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{ "connection": "connections/slack-prod" }'
{
  "ok": true,
  "botUser": "firetiger",
  "team": "Acme",
  "grantedScopes": ["chat:write", "channels:read", "..."],
  "missingScopes": []
}

ok is true only when auth.test succeeds and no required scopes are missing; otherwise error describes the failure.

ListSlackChannels

Lists the channels the connection’s bot is a member of (users.conversations, cursor-paginated, mirroring Slack). Does not walk the full workspace directory.

curl -X POST "https://api.cloud.firetiger.com/firetiger.slack.v1.SlackService/ListSlackChannels" \
  -u "$USERNAME:$PASSWORD" \
  -H "Content-Type: application/json" \
  -d '{ "connection": "connections/slack-prod", "page_size": 200 }'
{
  "channels": [
    { "id": "C0123ABCD", "name": "alerts", "isPrivate": false, "isArchived": false }
  ],
  "nextPageToken": "dXNlcjpVMDYxTkZUVDI="
}

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