LoopFour

Sales Operations

Workflow patterns for sales teams and revenue operations

Sales Operations Workflows

Automate deal progression, contract generation, lead routing, and pipeline management across your CRM and sales tools.

Why Automate Sales Operations?

Sales ops teams face unique challenges:

  • Manual handoffs between sales stages slow deal velocity
  • Contract delays while waiting for legal and document preparation
  • Lead routing inefficiencies causing uneven rep workloads
  • CRM data decay as reps forget to update records
  • Pipeline visibility gaps requiring manual report generation

Workflow automation solves these by:

  • Automating stage transitions and handoffs
  • Generating contracts instantly when deals are ready
  • Routing leads based on rules and rep capacity
  • Keeping CRM data current automatically
  • Providing real-time pipeline visibility

Common Workflow Patterns

Deal-to-Contract Automation

Trigger: Deal reaches contract stage Actions: Create contract from template, send for signature

Automatically generate and send contracts when deals reach the right stage, eliminating manual document creation.

{
  "name": "Deal → Contract",
  "trigger": {
    "type": "webhook",
    "provider": "hubspot",
    "events": ["deal.propertyChange"],
    "filter": {
      "propertyName": { "$eq": "dealstage" },
      "propertyValue": { "$eq": "contractsent" }
    }
  },
  "steps": [
    {
      "id": "get-deal",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "getDeal",
        "dealId": "{{input.objectId}}",
        "associations": ["contacts", "companies"]
      }
    },
    {
      "id": "get-contact",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "getContact",
        "contactId": "{{steps.get-deal.output.associations.contacts.results.[0].id}}"
      }
    },
    {
      "id": "get-company",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "getCompany",
        "companyId": "{{steps.get-deal.output.associations.companies.results.[0].id}}"
      }
    },
    {
      "id": "create-contract",
      "type": "action",
      "config": {
        "connector": "pandadoc",
        "action": "createDocument",
        "templateId": "CONTRACT_TEMPLATE_ID",
        "name": "Contract - {{steps.get-deal.output.properties.dealname}}",
        "recipients": [{
          "email": "{{steps.get-contact.output.properties.email}}",
          "first_name": "{{steps.get-contact.output.properties.firstname}}",
          "last_name": "{{steps.get-contact.output.properties.lastname}}",
          "role": "Signer"
        }],
        "tokens": [
          { "name": "deal_name", "value": "{{steps.get-deal.output.properties.dealname}}" },
          { "name": "deal_amount", "value": "{{steps.get-deal.output.properties.amount}}" },
          { "name": "company_name", "value": "{{steps.get-company.output.properties.name}}" }
        ]
      }
    },
    {
      "id": "send-contract",
      "type": "action",
      "config": {
        "connector": "pandadoc",
        "action": "sendDocument",
        "documentId": "{{steps.create-contract.output.id}}",
        "message": "Please review and sign your contract."
      }
    },
    {
      "id": "update-deal",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "updateDeal",
        "dealId": "{{input.objectId}}",
        "properties": {
          "contract_url": "https://app.pandadoc.com/documents/{{steps.create-contract.output.id}}",
          "contract_status": "Sent"
        }
      }
    },
    {
      "id": "notify-sales",
      "type": "slack",
      "config": {
        "action": "sendMessage",
        "channel": "#sales-contracts",
        "text": "Contract sent for {{steps.get-deal.output.properties.dealname}} (${{steps.get-deal.output.properties.amount}}) to {{steps.get-contact.output.properties.email}}"
      }
    }
  ]
}

Benefits:

  • Contract sent within seconds of stage change
  • No manual document assembly
  • Deal always has contract link for tracking
  • Team visibility into contract status

Full Deal Sync Tutorial →


Quote Generation

Trigger: API call from CRM or form Actions: Calculate pricing, generate PDF, attach to deal

Automatically generate quotes with correct pricing and terms.

{
  "name": "Generate Quote",
  "trigger": {
    "type": "api"
  },
  "steps": [
    {
      "id": "get-pricing",
      "type": "action",
      "config": {
        "connector": "stripe",
        "action": "getPrice",
        "priceId": "{{input.price_id}}"
      }
    },
    {
      "id": "calculate-quote",
      "type": "transform",
      "config": {
        "operation": "map",
        "data": {
          "unit_price": "{{steps.get-pricing.output.unit_amount | divided_by: 100}}",
          "quantity": "{{input.quantity}}",
          "subtotal": "{{steps.get-pricing.output.unit_amount | divided_by: 100 | multiply: input.quantity}}",
          "discount": "{{#if (gt input.quantity 10)}}{{steps.get-pricing.output.unit_amount | divided_by: 100 | multiply: input.quantity | multiply: 0.1}}{{else}}0{{/if}}",
          "total": "{{#if (gt input.quantity 10)}}{{steps.get-pricing.output.unit_amount | divided_by: 100 | multiply: input.quantity | multiply: 0.9}}{{else}}{{steps.get-pricing.output.unit_amount | divided_by: 100 | multiply: input.quantity}}{{/if}}"
        }
      }
    },
    {
      "id": "create-quote-doc",
      "type": "action",
      "config": {
        "connector": "pandadoc",
        "action": "createDocument",
        "templateId": "QUOTE_TEMPLATE_ID",
        "name": "Quote - {{input.company_name}} - {{now | date: '%Y-%m-%d'}}",
        "recipients": [{
          "email": "{{input.contact_email}}",
          "first_name": "{{input.contact_first_name}}",
          "last_name": "{{input.contact_last_name}}"
        }],
        "tokens": [
          { "name": "company_name", "value": "{{input.company_name}}" },
          { "name": "product_name", "value": "{{steps.get-pricing.output.product.name}}" },
          { "name": "unit_price", "value": "{{steps.calculate-quote.output.unit_price}}" },
          { "name": "quantity", "value": "{{input.quantity}}" },
          { "name": "discount", "value": "{{steps.calculate-quote.output.discount}}" },
          { "name": "total", "value": "{{steps.calculate-quote.output.total}}" },
          { "name": "valid_until", "value": "{{now | dateAdd: 30, 'days' | date: '%B %d, %Y'}}" }
        ]
      }
    },
    {
      "id": "update-deal",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "updateDeal",
        "dealId": "{{input.deal_id}}",
        "properties": {
          "quote_url": "https://app.pandadoc.com/documents/{{steps.create-quote-doc.output.id}}",
          "amount": "{{steps.calculate-quote.output.total}}"
        }
      }
    }
  ]
}

Benefits:

  • Accurate pricing every time
  • Volume discounts applied automatically
  • Quote linked to CRM deal
  • Consistent professional documents

Lead Routing

Trigger: New lead created in CRM Actions: Enrich data, score lead, assign to rep, notify

Automatically route leads to the right sales rep based on territory, product interest, or round-robin.

{
  "name": "Lead Router",
  "trigger": {
    "type": "webhook",
    "provider": "hubspot",
    "events": ["contact.creation"]
  },
  "steps": [
    {
      "id": "get-contact",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "getContact",
        "contactId": "{{input.objectId}}",
        "properties": ["email", "company", "country", "jobtitle", "hs_lead_status"]
      }
    },
    {
      "id": "determine-territory",
      "type": "transform",
      "config": {
        "operation": "map",
        "data": {
          "region": "{{#if (or (eq steps.get-contact.output.properties.country 'United States') (eq steps.get-contact.output.properties.country 'Canada'))}}NA{{else if (or (eq steps.get-contact.output.properties.country 'United Kingdom') (eq steps.get-contact.output.properties.country 'Germany'))}}EMEA{{else}}APAC{{/if}}"
        }
      }
    },
    {
      "id": "calculate-lead-score",
      "type": "transform",
      "config": {
        "operation": "map",
        "data": {
          "score": "{{0 | plus: (if (contains steps.get-contact.output.properties.jobtitle 'VP') 30 else 0) | plus: (if (contains steps.get-contact.output.properties.jobtitle 'Director') 20 else 0) | plus: (if (contains steps.get-contact.output.properties.jobtitle 'Manager') 10 else 0) | plus: (if steps.get-contact.output.properties.company 15 else 0)}}"
        }
      }
    },
    {
      "id": "assign-owner",
      "type": "transform",
      "config": {
        "operation": "map",
        "data": {
          "ownerId": "{{#if (and (eq steps.determine-territory.output.region 'NA') (gt steps.calculate-lead-score.output.score 40))}}NA_ENTERPRISE_REP_ID{{else if (eq steps.determine-territory.output.region 'NA')}}NA_SMB_REP_ID{{else if (eq steps.determine-territory.output.region 'EMEA')}}EMEA_REP_ID{{else}}APAC_REP_ID{{/if}}",
          "ownerEmail": "{{#if (eq steps.determine-territory.output.region 'NA')}}sales-na@company.com{{else if (eq steps.determine-territory.output.region 'EMEA')}}sales-emea@company.com{{else}}sales-apac@company.com{{/if}}"
        }
      }
    },
    {
      "id": "update-contact-owner",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "updateContact",
        "contactId": "{{input.objectId}}",
        "properties": {
          "hubspot_owner_id": "{{steps.assign-owner.output.ownerId}}",
          "hs_lead_status": "NEW",
          "lead_score": "{{steps.calculate-lead-score.output.score}}",
          "sales_region": "{{steps.determine-territory.output.region}}"
        }
      }
    },
    {
      "id": "notify-rep",
      "type": "slack",
      "config": {
        "action": "sendMessage",
        "channel": "#sales-leads",
        "blocks": [
          {
            "type": "header",
            "text": { "type": "plain_text", "text": "New Lead Assigned" }
          },
          {
            "type": "section",
            "fields": [
              { "type": "mrkdwn", "text": "*Name:*\n{{steps.get-contact.output.properties.firstname}} {{steps.get-contact.output.properties.lastname}}" },
              { "type": "mrkdwn", "text": "*Company:*\n{{steps.get-contact.output.properties.company}}" },
              { "type": "mrkdwn", "text": "*Title:*\n{{steps.get-contact.output.properties.jobtitle}}" },
              { "type": "mrkdwn", "text": "*Score:*\n{{steps.calculate-lead-score.output.score}}" }
            ]
          },
          {
            "type": "context",
            "elements": [
              { "type": "mrkdwn", "text": "Region: {{steps.determine-territory.output.region}} | Assigned to: {{steps.assign-owner.output.ownerEmail}}" }
            ]
          },
          {
            "type": "actions",
            "elements": [
              {
                "type": "button",
                "text": { "type": "plain_text", "text": "View in HubSpot" },
                "url": "https://app.hubspot.com/contacts/YOUR_PORTAL/contact/{{input.objectId}}"
              }
            ]
          }
        ]
      }
    }
  ]
}

Benefits:

  • Instant lead assignment
  • Consistent territory rules
  • Lead scoring automation
  • Rep notification with context

Pipeline Sync

Trigger: Scheduled hourly Actions: Sync deals between CRM and other systems

Keep multiple systems in sync with scheduled pipeline synchronization.

{
  "name": "Pipeline Sync",
  "trigger": {
    "type": "schedule",
    "cron": "0 * * * *"
  },
  "steps": [
    {
      "id": "get-updated-deals",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "searchDeals",
        "filter": {
          "lastModifiedDate": { "$gte": "{{now | dateAdd: -1, 'hours'}}" }
        }
      }
    },
    {
      "id": "sync-to-salesforce",
      "type": "loop",
      "config": {
        "items": "{{steps.get-updated-deals.output.results}}",
        "step": {
          "type": "action",
          "config": {
            "connector": "salesforce",
            "action": "updateOpportunity",
            "opportunityId": "{{item.properties.salesforce_opportunity_id}}",
            "fields": {
              "Amount": "{{item.properties.amount}}",
              "StageName": "{{item.properties.dealstage}}",
              "CloseDate": "{{item.properties.closedate}}"
            }
          }
        }
      }
    },
    {
      "id": "log-sync",
      "type": "slack",
      "config": {
        "action": "sendMessage",
        "channel": "#sales-ops",
        "text": "Pipeline sync complete. {{steps.get-updated-deals.output.results.length}} deals synced."
      }
    }
  ]
}

Win/Loss Analysis

Trigger: Deal closed (won or lost) Actions: Collect feedback, update analytics, notify team

Automatically capture win/loss data for analysis and reporting.

{
  "name": "Win/Loss Capture",
  "trigger": {
    "type": "webhook",
    "provider": "salesforce",
    "events": ["opportunity.updated"],
    "filter": {
      "IsClosed": { "$eq": true }
    }
  },
  "steps": [
    {
      "id": "get-opportunity",
      "type": "action",
      "config": {
        "connector": "salesforce",
        "action": "getOpportunity",
        "opportunityId": "{{input.payload.Id}}"
      }
    },
    {
      "id": "get-owner",
      "type": "action",
      "config": {
        "connector": "salesforce",
        "action": "query",
        "query": "SELECT Id, Name, Email FROM User WHERE Id = '{{steps.get-opportunity.output.OwnerId}}'"
      }
    },
    {
      "id": "request-feedback",
      "type": "slack",
      "config": {
        "action": "sendMessage",
        "channel": "@{{steps.get-owner.output.records.[0].Email}}",
        "blocks": [
          {
            "type": "header",
            "text": { "type": "plain_text", "text": "{{#if steps.get-opportunity.output.IsWon}}Deal Won!{{else}}Deal Lost{{/if}}" }
          },
          {
            "type": "section",
            "text": {
              "type": "mrkdwn",
              "text": "*{{steps.get-opportunity.output.Name}}*\nAmount: ${{steps.get-opportunity.output.Amount}}"
            }
          },
          {
            "type": "input",
            "block_id": "win_loss_reason",
            "label": { "type": "plain_text", "text": "{{#if steps.get-opportunity.output.IsWon}}What was the key factor in winning?{{else}}What was the primary reason for loss?{{/if}}" },
            "element": {
              "type": "static_select",
              "action_id": "reason_select",
              "options": [
                { "text": { "type": "plain_text", "text": "Price/Budget" }, "value": "price" },
                { "text": { "type": "plain_text", "text": "Product Fit" }, "value": "product" },
                { "text": { "type": "plain_text", "text": "Competitor" }, "value": "competitor" },
                { "text": { "type": "plain_text", "text": "Timing" }, "value": "timing" },
                { "text": { "type": "plain_text", "text": "Relationship" }, "value": "relationship" }
              ]
            }
          }
        ]
      }
    },
    {
      "id": "notify-leadership",
      "type": "slack",
      "config": {
        "action": "sendMessage",
        "channel": "#sales-wins-losses",
        "blocks": [
          {
            "type": "header",
            "text": { "type": "plain_text", "text": "{{#if steps.get-opportunity.output.IsWon}}Won{{else}}Lost{{/if}}: {{steps.get-opportunity.output.Name}}" }
          },
          {
            "type": "section",
            "fields": [
              { "type": "mrkdwn", "text": "*Amount:*\n${{steps.get-opportunity.output.Amount}}" },
              { "type": "mrkdwn", "text": "*Owner:*\n{{steps.get-owner.output.records.[0].Name}}" },
              { "type": "mrkdwn", "text": "*Stage:*\n{{steps.get-opportunity.output.StageName}}" },
              { "type": "mrkdwn", "text": "*Type:*\n{{steps.get-opportunity.output.Type}}" }
            ]
          }
        ]
      }
    }
  ]
}

Account Executive Handoff

Trigger: Lead qualified (MQL → SQL) Actions: Create opportunity, assign AE, schedule meeting

Automate the handoff from SDR to Account Executive.

{
  "name": "SDR → AE Handoff",
  "trigger": {
    "type": "webhook",
    "provider": "hubspot",
    "events": ["contact.propertyChange"],
    "filter": {
      "propertyName": { "$eq": "lifecyclestage" },
      "propertyValue": { "$eq": "salesqualifiedlead" }
    }
  },
  "steps": [
    {
      "id": "get-contact",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "getContact",
        "contactId": "{{input.objectId}}",
        "associations": ["companies"]
      }
    },
    {
      "id": "determine-ae",
      "type": "transform",
      "config": {
        "operation": "map",
        "data": {
          "aeId": "AE_OWNER_ID",
          "aeEmail": "ae@company.com",
          "aeSlackId": "U_AE_SLACK_ID"
        }
      }
    },
    {
      "id": "create-deal",
      "type": "action",
      "config": {
        "connector": "hubspot",
        "action": "createDeal",
        "properties": {
          "dealname": "{{steps.get-contact.output.properties.company}} - New Opportunity",
          "dealstage": "qualifiedtobuy",
          "hubspot_owner_id": "{{steps.determine-ae.output.aeId}}",
          "pipeline": "default"
        },
        "associations": [{
          "to": "{{input.objectId}}",
          "type": "contact"
        }]
      }
    },
    {
      "id": "notify-ae",
      "type": "slack",
      "config": {
        "action": "sendMessage",
        "channel": "@{{steps.determine-ae.output.aeSlackId}}",
        "blocks": [
          {
            "type": "header",
            "text": { "type": "plain_text", "text": "New SQL Handoff" }
          },
          {
            "type": "section",
            "fields": [
              { "type": "mrkdwn", "text": "*Contact:*\n{{steps.get-contact.output.properties.firstname}} {{steps.get-contact.output.properties.lastname}}" },
              { "type": "mrkdwn", "text": "*Company:*\n{{steps.get-contact.output.properties.company}}" },
              { "type": "mrkdwn", "text": "*Title:*\n{{steps.get-contact.output.properties.jobtitle}}" },
              { "type": "mrkdwn", "text": "*Email:*\n{{steps.get-contact.output.properties.email}}" }
            ]
          },
          {
            "type": "actions",
            "elements": [
              {
                "type": "button",
                "text": { "type": "plain_text", "text": "View Deal" },
                "url": "https://app.hubspot.com/contacts/YOUR_PORTAL/deal/{{steps.create-deal.output.id}}"
              },
              {
                "type": "button",
                "text": { "type": "plain_text", "text": "Schedule Meeting" },
                "url": "https://meetings.hubspot.com/ae-calendar"
              }
            ]
          }
        ]
      }
    },
    {
      "id": "send-intro-email",
      "type": "email",
      "config": {
        "action": "send",
        "provider": "gmail",
        "to": "{{steps.get-contact.output.properties.email}}",
        "from": "{{steps.determine-ae.output.aeEmail}}",
        "subject": "Let's schedule a call - {{steps.get-contact.output.properties.company}}",
        "body": {
          "html": "<p>Hi {{steps.get-contact.output.properties.firstname}},</p><p>I'm your dedicated account executive at Company. I'd love to learn more about your needs and show you how we can help.</p><p>Would you have 30 minutes this week for a quick call?</p><p><a href='https://meetings.hubspot.com/ae-calendar'>Schedule a time that works for you</a></p>"
        }
      }
    }
  ]
}

Key Integrations

IntegrationUse Cases
SalesforceCRM, opportunities, accounts, contacts
HubSpotCRM, deals, contacts, companies
PandaDocContracts, quotes, proposals
SlackNotifications, approvals, team alerts
StripePricing, quotes, subscriptions
EmailOutreach, follow-ups, introductions

Best Practices

1. Stage-Based Triggers

Use property change events instead of polling:

{
  "trigger": {
    "type": "webhook",
    "provider": "hubspot",
    "events": ["deal.propertyChange"],
    "filter": {
      "propertyName": { "$eq": "dealstage" }
    }
  }
}

2. Owner Notifications

Always notify the record owner:

{
  "id": "notify-owner",
  "type": "slack",
  "config": {
    "action": "sendMessage",
    "channel": "@{{steps.get-owner.output.slack_id}}"
  }
}

Include CRM links in all notifications:

{
  "type": "button",
  "text": { "type": "plain_text", "text": "View in CRM" },
  "url": "https://app.hubspot.com/contacts/PORTAL/deal/{{dealId}}"
}

4. Idempotency for Contracts

Prevent duplicate contracts:

{
  "metadata": {
    "hubspot_deal_id": "{{input.objectId}}",
    "created_at": "{{now}}"
  }
}

Getting Started

  1. Deal Sync Tutorial - Start with contract automation
  2. HubSpot Integration Guide - Set up your CRM connection
  3. PandaDoc Integration Guide - Connect document generation
  4. Slack Block Reference - Learn notification patterns