Request discount approval in Slack for HubSpot deals using code

high complexityCost: $0

Prerequisites

Prerequisites
  • Node.js or Python
  • HubSpot private app token (read + write scopes for deals)
  • Slack Bot Token with chat:write scope + interactive messages enabled
  • A server to handle Slack interactivity callbacks

Architecture

This requires two endpoints:

  1. Webhook receiver — receives HubSpot deal change events, checks discount threshold, sends Slack message with Approve/Deny buttons
  2. Interactivity handler — receives Slack button clicks, updates HubSpot deal, notifies the rep

Step 1: Send approval request with buttons

When a deal's discount exceeds the threshold, post a message with Block Kit interactive buttons:

# In your webhook handler after checking discount > 15%
slack.chat_postMessage(
    channel=os.environ["DEAL_DESK_CHANNEL"],
    text=f"Discount approval needed for {deal_name}",
    blocks=[
        {"type": "section", "text": {"type": "mrkdwn",
            "text": f"🏷️ *Discount Approval Needed*\n*Deal:* {deal_name}\n*Amount:* ${amount:,.0f}\n*Discount:* {discount}%"}},
        {"type": "actions", "elements": [
            {"type": "button", "text": {"type": "plain_text", "text": "✅ Approve"},
             "style": "primary", "action_id": "approve_discount",
             "value": json.dumps({"deal_id": deal_id, "discount": discount})},
            {"type": "button", "text": {"type": "plain_text", "text": "❌ Deny"},
             "style": "danger", "action_id": "deny_discount",
             "value": json.dumps({"deal_id": deal_id})}
        ]}
    ]
)

Step 2: Handle the button click

Set up a Slack interactivity endpoint (configure in your Slack app under Interactivity & Shortcuts):

@app.route("/slack/interact", methods=["POST"])
def handle_interaction():
    payload = json.loads(request.form["payload"])
    action = payload["actions"][0]
    data = json.loads(action["value"])
    user = payload["user"]["name"]
 
    approved = action["action_id"] == "approve_discount"
    status = "Approved" if approved else "Denied"
 
    # Update HubSpot
    requests.patch(
        f"https://api.hubapi.com/crm/v3/objects/deals/{data['deal_id']}",
        headers={**HEADERS, "Content-Type": "application/json"},
        json={"properties": {"discount_approval": status}}
    )
 
    # Update the Slack message to show the decision
    slack.chat_update(
        channel=payload["channel"]["id"],
        ts=payload["message"]["ts"],
        text=f"Discount {'approved' if approved else 'denied'} by {user}",
        blocks=[
            {"type": "section", "text": {"type": "mrkdwn",
                "text": f"{'✅' if approved else '❌'} Discount *{status}* by {user}"}}
        ]
    )
    return "", 200
Slack interactivity setup

You must configure your Slack app's Request URL under Interactivity & Shortcuts to point to your server's /slack/interact endpoint. Without this, button clicks do nothing.

Cost

  • Free — hosting costs only. No per-execution fees.

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.