Slack Ticket Approval (Dry-Run Linear Pipeline)
Purpose: Allow approving, rejecting, or reassigning planned Linear tickets from the dry-run Slack message before they are pushed to Linear.
1. Overview
When the dry-run Linear ticket pipeline runs (e.g. after meeting ingestion or via npm run dry-run-linear -- <meetingId>), the platform sends a Block Kit message to Slack with the same content as before (updates first, then creates) plus Approve, Reject, and Reassign buttons per ticket.
- Approve: Slack app calls platform
POST /api/brainforge/tickets/approve; platform re-runs dry-run, executes that single action (create or update in Linear), then the Slack message is updated to show “Approved”. - Reject: Slack app only updates the message to show “Rejected” (no Linear write, no platform call).
- Reassign (v1): Slack app only updates the message to show “Reassign requested”; assignee picker modal can be added later.
2. Configuration
Platform (The Forge)
- Route:
POST /api/brainforge/tickets/approve
Body:{ meetingId: string, actionIndex: number, assigneeId?: string, assigneeName?: string } - Auth: Request must include either:
Authorization: Bearer <PLATFORM_API_SECRET>, orx-internal-secret: <PLATFORM_API_SECRET>
- Env: Set
PLATFORM_API_SECRETin the platform (e.g. in Vercel /.env.local). Use a strong random value; same value must be set in the Slack app.
Slack app (brainforge-assistant on Railway)
- Interactivity: Request URL is already set (e.g.
https://<railway-domain>/slack/interactive). No change needed. - Env (Railway):
PLATFORM_API_URL– Base URL of the platform (e.g.https://platform.brainforge.ai). No trailing slash.PLATFORM_API_SECRET(orSLACK_TICKET_APPROVAL_SECRET) – Same secret as the platform’sPLATFORM_API_SECRET.
If either PLATFORM_API_URL or PLATFORM_API_SECRET is missing, Approve button clicks will log an error and post an ephemeral message to the user; Reject and Reassign still update the message.
3. Credentials
- Platform secret: Generate a random string (e.g.
openssl rand -hex 32) and set it in both:- Platform:
PLATFORM_API_SECRET - Slack app (Railway):
PLATFORM_API_SECRETorSLACK_TICKET_APPROVAL_SECRET
- Platform:
- Prefer storing in 1Password and injecting via deployment secrets rather than committing to the repo.
4. Flow
- Meeting created (or manual dry-run) → platform runs
runDryRunAndNotifySlack(meetingId). - Platform builds Block Kit via
buildDryRunSlackBlocks()(updates first, then creates; buttonvalue=meetingId:actionIndex) and sends withsendSlackMessage(..., blocks). - User clicks Approve/Reject/Reassign → Slack sends
block_actionsto the Slack app’s Request URL. - Slack app (Bolt) runs
app.action('ticket_approve'|'ticket_reject'|'ticket_reassign'), acks, then calls platform (Approve) or updates the message (all three). - For Approve, platform re-runs dry-run, runs
executeTicketActions([action], meetingId), returns 200; Slack app updates the message to show “Approved”.
5. References
- Platform:
apps/platform/src/lib/linear-ticket-pipeline/dry-run.ts(buildDryRunSlackBlocks,sendDryRunResultToSlack),apps/platform/src/app/api/brainforge/tickets/approve/route.ts - Slack app:
apps/slack-apps/brainforge-assistant/src/ticket-approval.js,apps/slack-apps/brainforge-assistant/src/index.js(action handlers) - Slack app env:
apps/slack-apps/brainforge-assistant/ENVIRONMENT.md(Slack ticket approval section)