Slack Button Workflow - Implementation Guide
Status: Message generator built, webhook integration pending
Date: 2026-02-17
Overview
Interactive Slack messages allow adding contacts to deals with dropdown selections for:
- Role: CH, IAR, EB, TC, EO, L
- Association type: internal, seller, buyer
- Not associated: Skip contact without adding
Message Format
Header
🚫 3160 Crowne Dr, Palmdale, CA | Will (CH) | ? (B)
Stage: Pending | EMD Received
Deal ID: 54223789480
Missing: ❌ TC, EO
For Each Suggested Contact
📧 Jose Maldonado
Email: jose@spgbuyer.com
Source: Gmail (82 messages)
Suggested role: End Buyer (EB)
[Select role ▼] [Association type ▼] [Add to Deal] [Not Associated]
Footer Actions
[View in HubSpot] [Mark Complete]
Dropdown Options
Role Selection
- Contract Holder (CH)
- Internal Acq Rep (IAR)
- End Buyer (EB)
- Transaction Coordinator (TC)
- Escrow Officer (EO)
- Lender (L)
Association Type
- Internal
- Seller
- Buyer
Button Actions
”Add to Deal”
- Reads selected role + association type from dropdowns
- Calls
add-contact-to-deal.jswith:- Deal ID
- Contact email
- Contact name
- Selected role
- Posts confirmation in thread:
✅ Added Jose Maldonado as End Buyer (EB) Association type: buyer Re-validating deal... Still missing: TC, EO
“Not Associated”
- Marks contact as reviewed but not relevant
- Removes from suggestion list
- Posts confirmation:
⏭️ Skipped jose@spgbuyer.com
“Mark Complete”
- User confirms all contacts added manually
- Removes deal from blocking list
- Posts confirmation:
✅ Deal marked complete Will re-validate on next compliance run
Webhook Integration (Pending)
Required Setup
-
OpenClaw webhook endpoint:
POST /api/slack/interactionsReceives button click payloads from Slack
-
Payload structure:
{ "type": "block_actions", "actions": [ { "action_id": "add_contact_54223789480_0", "type": "button", "value": "54223789480|jose@spgbuyer.com|Jose Maldonado" } ], "state": { "values": { "contact_0": { "role_select_54223789480_0": { "selected_option": { "value": "EB" } }, "assoc_select_54223789480_0": { "selected_option": { "value": "buyer" } } } } } } -
Handler script:
// Parse action const [dealId, email, name] = action.value.split('|'); // Get selected role + association type from state const role = state.values.contact_0.role_select.selected_option.value; const assocType = state.values.contact_0.assoc_select.selected_option.value; // Execute add script exec(`node add-contact-to-deal.js ${dealId} "${email}" "${name}" ${role}`); // Post confirmation to thread message.send({ channel: payload.channel.id, thread_ts: payload.message.ts, text: `✅ Added ${name} as ${role} (${assocType})` });
Current Limitations
No Webhook Yet
- Buttons/dropdowns display correctly
- Clicking does nothing (no handler configured)
- Workaround: Use
add-contact-to-deal.jsmanually via command line
Manual Process Until Webhook Built
- View Slack message with suggested contacts
- Copy contact email + suggested role
- Run script:
node /home/opsadmin/.openclaw/workspace/scripts/add-contact-to-deal.js \ 54223789480 \ "jose@spgbuyer.com" \ "Jose Maldonado" \ EB - Re-validate:
node /home/opsadmin/.openclaw/workspace/scripts/validate-deal-v3.js 54223789480
Implementation Steps (When Ready)
Step 1: Configure Slack App
- Go to https://api.slack.com/apps
- Select Better Acquisitions app
- Enable “Interactivity & Shortcuts”
- Set Request URL to OpenClaw webhook endpoint
- Add scopes:
chat:write,chat:write.public
Step 2: Build Webhook Handler
Create /home/opsadmin/.openclaw/workspace/scripts/slack-interaction-handler.js:
// Parse Slack interaction payload
// Extract deal ID, contact email/name, role, association type
// Call add-contact-to-deal.js
// Post confirmation to threadStep 3: Test End-to-End
- Post test message with buttons
- Click “Add to Deal”
- Verify:
- Contact created/found in HubSpot
- Associated with deal
- Role note added
- Confirmation posted to thread
- Re-validation shows updated status
Step 4: Deploy to Daily Cron
Update 6 AM cron to use button messages instead of plain text suggestions.
Message Generator Usage
Script: /workspace/scripts/generate-slack-contact-suggestion.js
Example:
const { generateContactSuggestionBlocks } = require('./generate-slack-contact-suggestion.js');
const deal = {
id: '54223789480',
name: '3160 Crowne Dr, Palmdale, CA',
stage: 'Pending | EMD Received',
missing: [{ role: 'TC' }, { role: 'EO' }]
};
const contacts = [
{
name: 'Jose Maldonado',
email: 'jose@spgbuyer.com',
source: 'Gmail',
suggestedRole: 'EB'
}
];
const blocks = generateContactSuggestionBlocks(deal, contacts);
// Send to Slack
message.send({
channel: 'C0AFCU58QSW',
blocks: blocks
});Future Enhancements
Batch Operations
- “Add All Suggested” button (uses suggested roles)
- “Skip All” button
- Bulk validation after multiple adds
Smart Suggestions
- Learn from past associations (if User X added Contact Y as EB, suggest EB next time)
- Prioritize contacts by communication frequency
- Flag suspicious suggestions (email domain mismatch, etc.)
Validation Feedback
- Show before/after comparison when contact added
- Highlight which missing roles were satisfied
- Show remaining blockers inline
Last updated: 2026-02-17 04:15 AM PST