LoopFour

Wait Steps

Pause workflow execution for timed delays

Wait Steps

Wait steps pause workflow execution for a specified duration. They're useful for rate limiting, scheduling delays, and allowing external processes to complete.

Configuration

{
  "id": "wait-before-retry",
  "type": "wait",
  "name": "Wait Before Retry",
  "config": {
    "duration": 5000
  }
}
FieldTypeRequiredDescription
idstringYesUnique step identifier
typestringYesMust be "wait"
namestringYesHuman-readable name
config.durationnumberYesWait time in milliseconds

Duration

The duration field specifies how long to pause in milliseconds:

{
  "config": {
    "duration": 30000
  }
}

Common Durations

DurationMillisecondsUse Case
1 second1000Quick pause
5 seconds5000Brief delay
30 seconds30000Rate limiting
1 minute60000Processing time
5 minutes300000External sync
1 hour3600000Scheduled delay

Output

Wait steps return information about the wait:

{
  "waited": 5000
}
FieldDescription
waitedThe actual duration waited (in ms)

Common Patterns

Rate Limiting

Add delays between API calls to avoid rate limits:

{
  "steps": [
    {
      "id": "first-api-call",
      "type": "action",
      "action": "hubspot.getContact",
      "config": { "contactId": "{{input.contactId}}" }
    },
    {
      "id": "rate-limit-delay",
      "type": "wait",
      "name": "Rate Limit Delay",
      "config": {
        "duration": 1000
      }
    },
    {
      "id": "second-api-call",
      "type": "action",
      "action": "hubspot.updateContact",
      "config": {
        "contactId": "{{input.contactId}}",
        "properties": { ... }
      }
    }
  ]
}

Retry with Delay

Wait before retrying a failed operation:

{
  "steps": [
    {
      "id": "attempt-sync",
      "type": "action",
      "action": "salesforce.updateAccount",
      "config": { ... },
      "errorHandling": {
        "strategy": "continue",
        "fallback": { "success": false }
      }
    },
    {
      "id": "check-success",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{steps.attempt-sync.output.success}}",
          "operator": "eq",
          "right": "false"
        },
        "then": ["wait-and-retry"],
        "else": ["continue-flow"]
      }
    },
    {
      "id": "wait-and-retry",
      "type": "wait",
      "name": "Wait Before Retry",
      "config": {
        "duration": 5000
      }
    },
    {
      "id": "retry-sync",
      "type": "action",
      "action": "salesforce.updateAccount",
      "config": { ... }
    }
  ]
}

Processing Delay

Wait for external system to process:

{
  "steps": [
    {
      "id": "submit-document",
      "type": "action",
      "action": "pandadoc.sendDocument",
      "config": {
        "documentId": "{{input.documentId}}"
      }
    },
    {
      "id": "wait-for-processing",
      "type": "wait",
      "name": "Wait for Document Processing",
      "config": {
        "duration": 10000
      }
    },
    {
      "id": "check-status",
      "type": "action",
      "action": "pandadoc.getDocumentStatus",
      "config": {
        "documentId": "{{input.documentId}}"
      }
    }
  ]
}

Scheduled Notification Delay

Delay a notification:

{
  "steps": [
    {
      "id": "create-invoice",
      "type": "action",
      "action": "stripe.createInvoice",
      "config": { ... }
    },
    {
      "id": "wait-before-reminder",
      "type": "wait",
      "name": "Wait 24 Hours",
      "config": {
        "duration": 86400000
      }
    },
    {
      "id": "send-reminder",
      "type": "action",
      "action": "slack.sendMessage",
      "config": {
        "channel": "#finance",
        "text": "Invoice {{steps.create-invoice.output.id}} is still unpaid"
      }
    }
  ]
}

Polling Pattern

Poll for status with delays:

{
  "steps": [
    {
      "id": "start-export",
      "type": "action",
      "action": "netsuite.createExport",
      "config": { "type": "invoices" }
    },
    {
      "id": "poll-status",
      "type": "loop",
      "config": {
        "collection": [1, 2, 3, 4, 5],
        "itemVariable": "attempt",
        "steps": ["check-export", "wait-if-pending"]
      }
    },
    {
      "id": "check-export",
      "type": "action",
      "action": "netsuite.getExportStatus",
      "config": {
        "exportId": "{{steps.start-export.output.id}}"
      }
    },
    {
      "id": "wait-if-pending",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{steps.check-export.output.status}}",
          "operator": "eq",
          "right": "pending"
        },
        "then": ["wait-poll"],
        "else": []
      }
    },
    {
      "id": "wait-poll",
      "type": "wait",
      "name": "Poll Delay",
      "config": {
        "duration": 5000
      }
    }
  ]
}

Dynamic Duration

Use template expressions for dynamic wait times:

{
  "id": "dynamic-wait",
  "type": "wait",
  "name": "Dynamic Delay",
  "config": {
    "duration": "{{input.delayMs}}"
  }
}

Or calculate based on previous step output:

{
  "steps": [
    {
      "id": "get-retry-delay",
      "type": "transform",
      "config": {
        "action": "map",
        "sourceData": "{{input}}",
        "mappingRules": [
          {
            "source": "retryCount",
            "target": "delayMs",
            "customTransform": "Multiply by 1000 for exponential backoff"
          }
        ]
      }
    },
    {
      "id": "backoff-wait",
      "type": "wait",
      "name": "Exponential Backoff",
      "config": {
        "duration": "{{steps.get-retry-delay.output.delayMs}}"
      }
    }
  ]
}

Best Practices

Reasonable Timeouts

Keep wait durations reasonable for your use case:

  • API rate limiting: 100-1000ms
  • Processing delays: 5-30 seconds
  • Polling intervals: 5-60 seconds
  • Scheduled delays: Use schedule triggers instead for hours/days

Avoid Long Waits

For delays longer than a few minutes, consider:

  1. Schedule triggers - Use cron or interval triggers instead
  2. Subworkflows - Split workflow at natural pause points
  3. External scheduling - Let external systems handle timing

Idempotency

Design steps after waits to be idempotent in case of restarts:

{
  "steps": [
    {
      "id": "wait-for-sync",
      "type": "wait",
      "config": { "duration": 60000 }
    },
    {
      "id": "check-already-processed",
      "type": "condition",
      "config": {
        "conditions": {
          "left": "{{steps.lookup.output.processed}}",
          "operator": "eq",
          "right": "true"
        },
        "then": [],
        "else": ["do-processing"]
      }
    }
  ]
}

Troubleshooting

Wait Not Working

  1. Check duration type - Must be a number in milliseconds
  2. Verify template resolution - Dynamic values must resolve to numbers
  3. Check workflow status - Workflow must be active

Workflow Timeout

If your total wait time exceeds the workflow timeout:

  1. Split into subworkflows - Break at natural boundaries
  2. Use schedule triggers - For long delays
  3. Reduce polling frequency - Increase wait duration between checks

Resource Usage

Long-running waits consume resources:

  1. Use appropriate intervals - Don't poll too frequently
  2. Consider schedules - For regular intervals
  3. Monitor run duration - Track total workflow time

Next Steps