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:
- Name: Descriptive identifier
- Trigger node: Geofence event that starts the workflow
- 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.
With an incoming webhook, the destination channel is selected in Slack when the webhook is created. Omit channel for webhook delivery. Use channel or default_channel only with a bot-token/OAuth Slack integration that supports channel selection.
{
"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}}"
}
}
}
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
Insert workflow event data into an external PostgreSQL or MySQL table.
The Database action performs parameterized inserts only — there is no raw SQL surface (no UPDATE, DELETE, or arbitrary queries). Required config: table_name. Optional config: data (an object whose keys become column names and whose values become parameterized values; defaults to the trigger payload when omitted).
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "database",
"label": "Log Entry",
"config": {
"integration_id": "your-database-integration-id",
"table_name": "arrivals",
"data": {
"device_id": "{{trigger.device_id}}",
"geofence_id": "{{trigger.geofence_id}}",
"arrived_at": "{{trigger.timestamp}}"
}
}
}
}
Connection settings (host, port, database, username, password, ssl_mode) are supplied via the Database integration referenced by integration_id, not via the action config itself.
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"
}
}
},
{
"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_message": "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
}
The error_message field is populated when status is failed; it contains the human-readable failure reason from the action handler.
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