Lead Ops
Python
Cron Schedule

Stale Lead SLA Monitor & Unassigned Alerting (Python)

Identify uncontacted leads older than SLA thresholds, flag SLA breach, and alert managers in Python.

Why this automation is needed

Native HubSpot workflows often struggle with complex multi-object traversals, conditional lookback attribution windows, custom phone format standardizations, and custom scoring calculations.

With Workflood, this recipe runs in a secure gVisor container sandbox with pre-authenticated HubSpot SDK injection, avoiding HubSpot Data Hub execution limits, memory bottlenecks, and per-seat fees.

Action Source Code (Python)

stale-lead-sla-recirculation-python.py
Python
import datetime

def main(input_data, hs_client, http_client, secret, console):
    raw = input_data[0] if isinstance(input_data, list) and input_data else (input_data or {})
    contact_id = raw.get('contact_id') or raw.get('contactId') or raw.get('objectId') or raw.get('hs_object_id')

    sla_days = int(input_data.get('sla_days', 5))
    cutoff = datetime.datetime.utcnow() - datetime.timedelta(days=sla_days)

    contacts_to_scan = []
    if contact_id:
        c = hs_client.crm.contacts.basic_api.get_by_id(contact_id, properties=['email', 'firstname', 'lastname', 'hs_lead_status', 'createdate', 'notes_last_contacted'])
        contacts_to_scan = [c]
    else:
        contacts = hs_client.crm.contacts.basic_api.get_page(
            limit=100,
            properties=['email', 'firstname', 'lastname', 'hs_lead_status', 'createdate', 'notes_last_contacted']
        )
        contacts_to_scan = contacts.results or []

    breached = []
    for c in contacts_to_scan:
        status = (c.properties.get('hs_lead_status') or '').upper()
        is_unworked = not status or status in ('NEW', 'OPEN', 'UNQUALIFIED')
        create_str = c.properties.get('createdate')
        last_contact = c.properties.get('notes_last_contacted')

        if is_unworked and create_str and not last_contact:
            c_date = datetime.datetime.fromisoformat(create_str.replace('Z', '+00:00'))
            if c_date.replace(tzinfo=None) < cutoff:
                breached.append(c.id)
                hs_client.crm.contacts.basic_api.update(c.id, properties={
                    "lead_sla_breached": "true",
                    "lead_sla_breach_date": datetime.datetime.utcnow().isoformat() + "Z"
                })

    webhook_url = input_data.get('slack_webhook_url')
    if breached and webhook_url:
        http_client.post(webhook_url, {"text": f"⚠️ SLA Alert: {len(breached)} unworked leads breached {sla_days}-day SLA."})

    return {"scanned": len(contacts_to_scan), "breachedCount": len(breached), "breachedContactIds": breached}

Configurable Parameters

ParameterTypeDefaultDescription
slaDaysnumber5Number of business/calendar days before marking lead SLA as breached.
slackAlertWebhookstringOptional Slack webhook to notify managers when leads breach SLA.

How to deploy in Workflood

1

Connect your HubSpot Portal

Add your HubSpot Private App token once. Workflood securely encrypts it via libsodium XChaCha20 and injects pre-authenticated SDK clients.

2

Create Action from Template

Select this recipe in Workflood. Edit variables, customize logic in Monaco editor, or execute dry runs with sample JSON payloads.

3

Trigger via Webhook or Cron

Trigger from standard HubSpot workflows via Webhook action, or schedule recurring batch syncs with cron expressions and timezone handling.