# Slack Request URL Cutover Checklist

Status: Ready to Execute  
Last Updated: 2026-03-02

This checklist is for running the Slack cutover from Socket Mode to HTTP Request URLs in production.

It assumes the code and DB migration for `slack_installations` are already deployed.

---

## 0. Fill In Your Variables

Use these in your shell first:

```bash
export APP="<your-heroku-app-name>"
export DOMAIN="<your-app-domain>" # e.g. cat.fish.dog (no protocol)
export REDIRECT_URI="https://${DOMAIN}/slack/oauth/callback"
export EVENTS_URL="https://${DOMAIN}/slack/events"
```

Optional convenience:

```bash
echo "APP=${APP}"
echo "DOMAIN=${DOMAIN}"
echo "REDIRECT_URI=${REDIRECT_URI}"
echo "EVENTS_URL=${EVENTS_URL}"
```

---

## 1. Pre-Flight Checks

1. Confirm app + latest release:

```bash
heroku apps:info -a "$APP"
heroku releases -a "$APP" -n 5
```

2. Confirm route is live (`/slack/events` should exist):

```bash
curl -i -X POST "$EVENTS_URL" \
  -H "Content-Type: application/json" \
  --data '{}'
```

Expected:

1. `401` (`invalid_signature`) if signing secret is configured.
2. `503` (`slack_signing_secret_missing`) if signing secret is not yet configured.

---

## 2. Set Required Runtime Config

Set Request URL transport + OAuth + signing env vars:

```bash
heroku config:set -a "$APP" \
  SLACK_TRANSPORT=http \
  SLACK_SIGNING_SECRET="<from-slack-app-basic-info>" \
  SLACK_CLIENT_ID="<from-slack-app-basic-info>" \
  SLACK_CLIENT_SECRET="<from-slack-app-basic-info>" \
  SLACK_OAUTH_REDIRECT_URI="$REDIRECT_URI" \
  SLACK_OAUTH_SCOPES="app_mentions:read,channels:history,groups:history,im:history,im:write,mpim:history,chat:write,chat:write.public,reactions:write,channels:join"
```

Keep fallback token during migration window:

```bash
heroku config:set -a "$APP" SLACK_BOT_TOKEN="<existing-xoxb-token>"
```

Restart dynos:

```bash
heroku ps:restart -a "$APP"
```

Verify config is present:

```bash
heroku config -a "$APP" | rg '^SLACK_(TRANSPORT|SIGNING_SECRET|CLIENT_ID|CLIENT_SECRET|OAUTH_REDIRECT_URI|OAUTH_SCOPES|BOT_TOKEN)='
```

---

## 3. Configure Slack App (UI Steps)

In Slack App settings:

1. **OAuth & Permissions**
1. Add Redirect URL: `https://<your-domain>/slack/oauth/callback`
2. Ensure bot scopes include:
   `app_mentions:read`, `channels:history`, `groups:history`, `im:history`, `im:write`, `mpim:history`, `chat:write`, `chat:write.public`, `reactions:write`, `channels:join`

2. **Event Subscriptions**
1. Turn ON.
2. Request URL: `https://<your-domain>/slack/events`
3. Subscribe to bot events:
   `app_mention`, `message.channels`, `message.groups`, `message.im`, `message.mpim`

3. **Socket Mode**
1. Turn OFF.

4. **Install App**
1. Reinstall to workspace after scope/event changes.

---

## 4. Run OAuth Install Flow

Start install in browser:

```bash
echo "Open: https://${DOMAIN}/slack/install"
```

After consent, callback should auto-link the workspace and show a success status page (no manual field entry required).
If org context is missing, callback falls back to `/slack/connect?team_id=...&user_id=...`.

If you want to check callback endpoint quickly:

```bash
curl -i "https://${DOMAIN}/slack/oauth/callback"
```

Expected: `400` (missing state/code), confirming route is live.

---

## 5. Verify Installation Saved

### Option A: Heroku Postgres query

```bash
heroku pg:psql -a "$APP" -c \
"select workspace_external_id, workspace_id, bot_user_id, installed_by_slack_user_id, installed_at from slack_installations order by installed_at desc limit 20;"
```

### Option B: App-context Python query

```bash
python - <<'PY'
from app import create_app
from sixseeds_shared.models import SlackInstallation
app = create_app()
with app.app_context():
    rows = SlackInstallation.query.order_by(SlackInstallation.installed_at.desc()).limit(20).all()
    for r in rows:
        print(r.workspace_external_id, r.workspace_id, r.bot_user_id, r.installed_by_slack_user_id, r.installed_at)
PY
```

You should see the newly installed workspace.

---

## 6. Link Workspace To FishDog Org (Fallback Only)

OAuth callback now attempts to auto-link using:
1. Org context from query/referrer/current user.
2. `organization.default_api_key`.
3. Team/user values from OAuth install payload.

Only use manual connect if callback could not resolve org context:

```text
https://<your-domain>/slack/connect?team_id=<TEAM_ID>&user_id=<USER_ID>&org_uuid=<ORG_UUID>
```

Verify workspace row:

```bash
heroku pg:psql -a "$APP" -c \
"select id, workspace_id, organization_id, team_name, updated_at from slack_workspaces order by updated_at desc limit 20;"
```

---

## 7. Functional Smoke Tests In Slack

Run these in Slack after install/link:

1. DM the app: `run a quick test study`
2. Public channel mention: `@YourApp summarize latest findings`
3. Private channel mention (after `/invite @YourApp`): `@YourApp hello`
4. Ask for links: `@YourApp share the study link`

Expected:

1. Bot replies in DM + channel mentions.
2. Links are clickable.
3. No duplicate repeated replies.

---

## 8. Logs And Health Monitoring (First 24 Hours)

Tail logs:

```bash
heroku logs -a "$APP" --tail
```

Watch for:

1. `invalid_signature`
2. `queue_failed`
3. `slack_ingress_event_total`
4. `slack_reply_delivery_total`
5. `not_in_channel` (should auto-join/retry for public channels)

Quick spot-check:

```bash
heroku logs -a "$APP" -n 300 | rg "slack_ingress_event_total|slack_reply_delivery_total|invalid_signature|queue_failed|not_in_channel"
```

---

## 9. Cutover Completion Criteria

All should be true:

1. `/slack/events` is receiving events.
2. OAuth installs create/update `slack_installations`.
3. DM + public + private mention behavior works.
4. Link rendering is correct.
5. No meaningful ingress/reply failure spikes in logs.

---

## 10. Rollback Plan (If Needed)

1. Re-enable Socket Mode in Slack App UI.
2. Set transport back:

```bash
heroku config:set -a "$APP" SLACK_TRANSPORT=socket
heroku ps:restart -a "$APP"
```

3. Keep Event Subscriptions off until issue is resolved.

---

## 11. Post-Cutover Cleanup (After Stable Period)

After all active workspaces have completed OAuth install:

1. Remove dependency on fallback global token:

```bash
heroku config:unset -a "$APP" SLACK_BOT_TOKEN
```

2. Restart dynos:

```bash
heroku ps:restart -a "$APP"
```

3. Run smoke tests again.
