Skip to main content

Workflow Automation Guide

Build powerful multi-step location-based automations with SpatialFlow workflows. This guide covers common patterns, best practices, and real-world examples.

Overview

Workflows in SpatialFlow automate actions when geofence events occur. Think of workflows as "if-then" rules:

If a device enters/exits a geofence → Then execute one or more actions

Common use cases:

  • Send Slack notification when vehicle enters delivery zone
  • Trigger webhook and send email when unauthorized device enters restricted area
  • Log to database when employee arrives at job site

Quick Start

Basic Workflow Structure

Every workflow has three components:

  1. Name: Descriptive identifier
  2. Trigger node: Geofence event that starts the workflow
  3. Action nodes: Actions to execute, connected by edges

Example:

{
"name": "Office Entry Notification",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "slack",
"label": "Slack Notification",
"config": {
"webhook_url": "https://hooks.slack.com/services/YOUR/WEBHOOK",
"message": "{{trigger.device_name}} arrived at the office"
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"}
]
}

Creating Your First Workflow

curl -X POST https://api.spatialflow.io/api/v1/workflows \
-H "X-API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Delivery Zone Entry Alert",
"description": "Notify operations team when driver enters delivery zone",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["your-geofence-id"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "webhook",
"label": "HTTP Request",
"config": {
"url": "https://your-app.com/api/delivery-alerts",
"method": "POST",
"headers": {
"Content-Type": "application/json"
}
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"}
]
}'

Trigger Types

Geofence Entry

Fires when a device enters a geofence boundary.

{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"]
}
}
}

Use cases:

  • Customer arrival notifications
  • Check-in automation
  • Zone entry logging

Geofence Exit

Fires when a device leaves a geofence boundary.

{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_exit",
"label": "Geofence Exit",
"config": {
"geofence_ids": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"]
}
}
}

Use cases:

  • Departure confirmations
  • Dwell time calculations
  • Zone exit alerts

Available Actions

1. Webhook (HTTP Request)

Send HTTP request to any URL.

{
"id": "action-1",
"type": "action",
"data": {
"actionType": "webhook",
"label": "HTTP Request",
"config": {
"url": "https://your-app.com/webhooks/geofence-event",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"X-Custom-Header": "value"
},
"body": {
"device": "{{trigger.device_name}}",
"geofence": "{{trigger.geofence_name}}",
"event_type": "{{trigger.event_type}}"
}
}
}
}

Template Variables:

  • {{trigger.device_id}}, {{trigger.device_name}}
  • {{trigger.geofence_id}}, {{trigger.geofence_name}}
  • {{trigger.location.latitude}}, {{trigger.location.longitude}}
  • {{trigger.event_type}}, {{trigger.timestamp}}

2. Slack Notification

Send message to Slack channel.

{
"id": "action-1",
"type": "action",
"data": {
"actionType": "slack",
"label": "Slack Notification",
"config": {
"webhook_url": "https://hooks.slack.com/services/YOUR/WEBHOOK",
"message": "🚗 {{trigger.device_name}} entered {{trigger.geofence_name}}",
"channel": "#operations"
}
}
}

3. Email (SES/SendGrid)

Send email notification.

{
"id": "action-1",
"type": "action",
"data": {
"actionType": "email",
"label": "Email Notification",
"config": {
"to": "operations@example.com",
"subject": "Geofence Alert: {{trigger.geofence_name}}",
"body": "Device {{trigger.device_name}} entered {{trigger.geofence_name}} at {{trigger.timestamp}}"
}
}
}

4. SMS (Twilio)

Send SMS alert.

{
"id": "action-1",
"type": "action",
"data": {
"actionType": "sms",
"label": "SMS Alert",
"config": {
"to": "+1234567890",
"message": "Alert: {{trigger.device_name}} entered {{trigger.geofence_name}}"
}
}
}

5. Database

Execute database operations for custom data persistence.

{
"id": "action-1",
"type": "action",
"data": {
"actionType": "database",
"label": "Log Entry",
"config": {
"query": "INSERT INTO arrivals (device_id, geofence_id, arrived_at) VALUES ($1, $2, $3)",
"params": ["{{trigger.device_id}}", "{{trigger.geofence_id}}", "{{trigger.timestamp}}"]
}
}
}

Common Workflow Patterns

Pattern 1: Multi-Channel Notification

Send alerts to multiple channels simultaneously.

{
"name": "Multi-Channel Security Alert",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["restricted_area_id"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "slack",
"label": "Slack Alert",
"config": {
"webhook_url": "https://hooks.slack.com/...",
"message": "🚨 ALERT: {{trigger.device_name}} entered restricted area",
"channel": "#security"
}
}
},
{
"id": "action-2",
"type": "action",
"data": {
"actionType": "email",
"label": "Email Alert",
"config": {
"to": "security@example.com",
"subject": "Security Alert: Unauthorized Entry",
"body": "Device {{trigger.device_name}} entered restricted area at {{trigger.timestamp}}"
}
}
},
{
"id": "action-3",
"type": "action",
"data": {
"actionType": "sms",
"label": "SMS Alert",
"config": {
"to": "+1234567890",
"message": "SECURITY ALERT: {{trigger.device_name}} entered restricted area"
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"},
{"id": "edge-2", "source": "action-1", "target": "action-2"},
{"id": "edge-3", "source": "action-2", "target": "action-3"}
]
}

Pattern 2: Webhook Processing

Use a webhook for custom backend logic.

{
"name": "Process Vehicle Entry",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["depot_geofence"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "webhook",
"label": "Process Checkin",
"config": {
"url": "https://your-app.com/api/vehicle-checkin",
"method": "POST",
"body": {
"vehicle_id": "{{trigger.device_id}}",
"location": {
"lat": "{{trigger.location.latitude}}",
"lng": "{{trigger.location.longitude}}"
},
"timestamp": "{{trigger.timestamp}}"
}
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"}
]
}

Testing Workflows

1. Create Test Geofence

Create a small test geofence for development:

curl -X POST https://api.spatialflow.io/api/v1/geofences/ \
-H "X-API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Test Geofence - Delete After Testing",
"geometry": {
"type": "Polygon",
"coordinates": [[
[-122.4200, 37.7745],
[-122.4200, 37.7753],
[-122.4188, 37.7753],
[-122.4188, 37.7745],
[-122.4200, 37.7745]
]]
}
}'

2. Create Test Workflow

Use webhook.site for quick testing:

curl -X POST https://api.spatialflow.io/api/v1/workflows \
-H "X-API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Test Workflow",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["test_geofence_id"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "webhook",
"label": "HTTP Request",
"config": {
"url": "https://webhook.site/your-unique-id",
"method": "POST"
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"}
]
}'

3. Send Test Location

Trigger the workflow with a test location:

curl -X POST https://api.spatialflow.io/api/v1/locations \
-H "X-API-KEY: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"device_id": "test_device",
"lat": 37.7749,
"lon": -122.4194,
"ts": "2025-11-10T12:00:00Z"
}'

4. Check Webhook Deliveries

View webhook delivery history:

curl -X GET https://api.spatialflow.io/api/v1/workflows/{workflow_id}/executions \
-H "X-API-KEY: YOUR_API_KEY"

Debugging Failed Workflows

Check Workflow Execution Logs

curl -X GET https://api.spatialflow.io/api/v1/workflows/{workflow_id}/executions?status=failed \
-H "X-API-KEY: YOUR_API_KEY"

Common failure reasons:

  • Webhook endpoint returned 5xx error
  • Request timeout (> 30 seconds)
  • Invalid action configuration
  • Missing required fields

View Execution Details

curl -X GET https://api.spatialflow.io/api/v1/workflows/executions/{execution_id} \
-H "X-API-KEY: YOUR_API_KEY"

Response includes:

{
"execution_id": "exec_123",
"workflow_id": "wf_456",
"status": "failed",
"error": "Webhook returned 500 Internal Server Error",
"started_at": "2025-11-10T12:00:00Z",
"completed_at": "2025-11-10T12:00:05Z",
"duration_seconds": 5.0,
"current_step": 0
}

Best Practices

1. Use Descriptive Names

Good:

  • "Notify Ops Team - Delivery Zone Entry"
  • "Log Employee Arrival - SF Office"
  • "Security Alert - Restricted Area"

Bad:

  • "Workflow 1"
  • "Test"
  • "asdf"

2. Set Workflow Active/Inactive

Disable workflows during maintenance without deleting:

# Deactivate workflow (toggle endpoint)
curl -X POST https://api.spatialflow.io/api/v1/workflows/{workflow_id}/toggle \
-H "X-API-KEY: YOUR_API_KEY"

3. Monitor Action Failures

Set up monitoring for failed actions:

# Get failed executions
curl -X GET https://api.spatialflow.io/api/v1/workflows/executions?status=failed \
-H "X-API-KEY: YOUR_API_KEY"

4. Use Idempotent Actions

Ensure actions can be safely retried:

  • Use unique IDs in database inserts
  • Check for duplicates before processing
  • Handle webhook retries gracefully

5. Keep Actions Fast

  • Webhooks should respond in < 5 seconds
  • Use async processing for slow operations
  • Return 2xx quickly to prevent retries

Performance Considerations

Workflow Execution Speed

  • Actions execute sequentially (one after another)
  • Typical execution time: 100-500ms per action
  • Webhook actions depend on your endpoint response time

Rate Limits

  • No limit on workflow count
  • No limit on executions per workflow
  • Webhook delivery rate limited to 60/min per endpoint

Scaling Tips

  • Use separate workflows for different priorities
  • Group related actions in single workflow (fewer executions)
  • Use webhooks to fan out to external services for high-volume scenarios

Real-World Examples

Example 1: Fleet Check-In System

{
"name": "Vehicle Depot Check-In",
"description": "Log vehicle arrivals and notify dispatch",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["depot_north_bay"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "slack",
"label": "Slack Notification",
"config": {
"webhook_url": "https://hooks.slack.com/...",
"message": "✅ Vehicle {{trigger.device_name}} checked in at {{trigger.geofence_name}}",
"channel": "#dispatch"
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"}
]
}

Example 2: Customer Proximity Marketing

{
"name": "Store Arrival - Send Welcome Offer",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["store_42_sf"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "webhook",
"label": "HTTP Request",
"config": {
"url": "https://marketing-platform.com/api/send-offer",
"method": "POST",
"headers": {
"Authorization": "Bearer MARKETING_API_KEY"
},
"body": {
"customer_id": "{{trigger.device_id}}",
"store_id": "{{trigger.geofence.metadata.store_number}}",
"offer_type": "welcome_discount"
}
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"}
]
}

Example 3: Security Perimeter Alert

{
"name": "Restricted Area - Immediate Alert",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"geofence_ids": ["construction_site_restricted"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "sms",
"label": "SMS Alert",
"config": {
"to": "+1234567890",
"message": "⚠️ ALERT: {{trigger.device_name}} entered restricted construction area"
}
}
},
{
"id": "action-2",
"type": "action",
"data": {
"actionType": "email",
"label": "Email Alert",
"config": {
"to": "security@example.com",
"subject": "URGENT: Restricted Area Entry",
"body": "Device: {{trigger.device_name}}\nArea: {{trigger.geofence_name}}\nTime: {{trigger.timestamp}}\nLocation: {{trigger.location.latitude}}, {{trigger.location.longitude}}"
}
}
},
{
"id": "action-3",
"type": "action",
"data": {
"actionType": "webhook",
"label": "HTTP Request",
"config": {
"url": "https://security-system.com/api/alerts",
"method": "POST"
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"},
{"id": "edge-2", "source": "action-1", "target": "action-2"},
{"id": "edge-3", "source": "action-2", "target": "action-3"}
]
}

Next Steps