Flag HubSpot deals with missing fields and Slack the rep using code
Prerequisites
- Node.js 18+ or Python 3.9+
- HubSpot private app token with
crm.objects.deals.readscope - Slack Bot Token (
xoxb-...) withchat:writescope - A mapping of HubSpot owner IDs to Slack user IDs
- Cron, GitHub Actions, or a cloud function for scheduling
Step 1: Set up the project
# Verify your HubSpot token works
curl -s -X POST "https://api.hubapi.com/crm/v3/objects/deals/search" \
-H "Authorization: Bearer $HUBSPOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filterGroups": [{
"filters": [{
"propertyName": "closedate",
"operator": "NOT_HAS_PROPERTY"
}, {
"propertyName": "dealstage",
"operator": "NOT_IN",
"values": ["closedwon", "closedlost"]
}]
}],
"properties": ["dealname", "amount", "closedate", "hubspot_owner_id"],
"limit": 5
}' | python3 -m json.toolStep 2: Search for deals with missing fields
Use the HubSpot Search API with NOT_HAS_PROPERTY to find deals where closedate or amount is null. Two separate searches are needed because HubSpot applies AND logic within a filter group.
# Search for deals missing close date
curl -s -X POST "https://api.hubapi.com/crm/v3/objects/deals/search" \
-H "Authorization: Bearer $HUBSPOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filterGroups": [{
"filters": [
{"propertyName": "closedate", "operator": "NOT_HAS_PROPERTY"},
{"propertyName": "dealstage", "operator": "NOT_IN", "values": ["closedwon", "closedlost"]}
]
}],
"properties": ["dealname", "amount", "closedate", "dealstage", "hubspot_owner_id"],
"limit": 100
}'
# Search for deals missing amount
curl -s -X POST "https://api.hubapi.com/crm/v3/objects/deals/search" \
-H "Authorization: Bearer $HUBSPOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filterGroups": [{
"filters": [
{"propertyName": "amount", "operator": "NOT_HAS_PROPERTY"},
{"propertyName": "dealstage", "operator": "NOT_IN", "values": ["closedwon", "closedlost"]}
]
}],
"properties": ["dealname", "amount", "closedate", "dealstage", "hubspot_owner_id"],
"limit": 100
}'NOT_HAS_PROPERTY matches deals where the property has never been set. If a rep sets a close date and then clears it, HubSpot may store it as an empty string rather than null. To catch both, add a second filter group with EQ operator and value: "" for each field.
The search endpoint returns max 100 results per page. If you have more than 100 deals with missing fields, implement pagination using the after cursor from paging.next.after in the response. The endpoint caps at 10,000 total results.
Step 3: Schedule
Cron (server-based):
# Daily at 7 AM
0 7 * * * cd /path/to/missing-fields && python missing_fields.pyGitHub Actions (serverless):
name: Missing Deal Fields Alert
on:
schedule:
- cron: '0 12 * * *' # 7 AM ET = 12 PM UTC
workflow_dispatch: {}
jobs:
alert:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install requests slack_sdk && python missing_fields.py
env:
HUBSPOT_TOKEN: ${{ secrets.HUBSPOT_TOKEN }}
HUBSPOT_PORTAL_ID: ${{ secrets.HUBSPOT_PORTAL_ID }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}Never commit tokens to your repo. Use GitHub Secrets, .env files (gitignored), or your hosting platform's secrets manager.
Cost
- Free -- GitHub Actions provides 2,000 minutes/month on the free tier. This script runs in under 10 seconds.
Need help implementing this?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.