Skip to main content

TMS/CRM Addresses to Tagged Geofences

SpatialFlow does not require a native connector to your TMS or CRM. The bulk import API and workflows together are the integration story: your TMS exports an address list, you import it via the bulk API, the addresses become tagged geofences, a workflow fires on the shared tag, and a webhook or action delivers the result back to your system.

This recipe walks through each step.

Prerequisites

  • A SpatialFlow workspace with bulk import enabled (Professional tier or above)
  • An address export from your TMS or CRM (CSV or JSON format)
  • An API key with write access to geofences and workflows — generate one from Dashboard > Account > API Keys

Step 1 — Export Addresses from Your TMS or CRM

Most TMS and CRM systems (Onfleet, Salesforce, HubSpot, spreadsheets, custom databases) can export a CSV with at minimum a street address column.

For best results, include:

  • address (required) — full street address including city, state, and ZIP (e.g., 123 Main St, Austin TX 78701). The more complete the address, the more accurate the geocode.
  • name (recommended) — customer name, stop name, or site identifier. This becomes the geofence display name and is used for dedup matching on re-import.
  • tags (optional) — use a consistent tag like tms-sync or fleet-stops-2026-06 to group all geofences from this import together.

The CSV must use the address column header exactly as shown. See the Bulk Import CSV Schema for the full column reference including buffer_meters and tag formatting rules.

Example export

address,name,tags
"1234 Commerce Dr, Memphis TN 38116",Acme Distribution Center,"tms-sync,tier:gold"
"567 Industrial Blvd, Nashville TN 37209",Riverside Depot,"tms-sync"
"890 Airport Connector, Atlanta GA 30320",ATL Cross-Dock,"tms-sync,priority"

Step 2 — Import via the Bulk API

Upload the CSV to /api/v1/geofences/bulk using dedup_strategy=skip so the import is safe to re-run (unchanged addresses are skipped). Tag all rows with a shared tag (e.g., tms-sync) so the resulting geofences can be targeted as a group by a single workflow in Step 3.

cURL example

curl -X POST https://api.spatialflow.io/api/v1/geofences/bulk \
-H "X-API-Key: YOUR_API_KEY" \
-F "file=@stops.csv" \
-F "dedup_strategy=skip" \
-F "tags=tms-sync"

The tags field appended here is applied to every row in addition to any tags already in the CSV.

After the import completes, the dashboard shows per-row outcome states (success, low-confidence, interpolated, no-match, ambiguous, duplicate). Rows that could not be geocoded appear in a downloadable error CSV for correction and re-import. See the Bulk Import CSV Schema for a full description of each outcome.

For the JSON request shape, SDK examples, and rate limits, see the Bulk Create Geofences API reference.

Step 3 — Create a Workflow Targeting the Tag

Once the geofences are imported, create a workflow that fires whenever any device enters (or exits) a geofence carrying the tms-sync tag. You do not need to list individual geofence IDs — the tag acts as the grouping selector, so new geofences added in future imports are automatically covered.

In the SpatialFlow dashboard:

  1. Navigate to Workflows > New Workflow.
  2. Add a trigger node. Set the trigger type to Geofence Entry (or Exit).
  3. Set the geofence target mode to Tags and select tms-sync (or whichever tag you used in Step 2).
  4. Connect the trigger to an action node (Step 4).

For the full workflow JSON schema and advanced trigger options, see the Workflow Automation guide.

Step 4 — Add a Webhook or Action

Connect the trigger to an action that delivers the event back to your TMS or CRM.

Webhook to your TMS inbound endpoint

curl -X POST https://api.spatialflow.io/api/v1/workflows \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "TMS Sync — Geofence Entry",
"nodes": [
{
"id": "trigger-1",
"type": "trigger",
"data": {
"triggerType": "geofence_enter",
"label": "Geofence Entry",
"config": {
"target_mode": "tags",
"tags": ["tms-sync"]
}
}
},
{
"id": "action-1",
"type": "action",
"data": {
"actionType": "webhook",
"label": "Notify TMS",
"config": {
"url": "https://your-tms.example.com/api/inbound/arrival",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_TMS_API_KEY"
},
"body": {
"device_id": "{{trigger.device_id}}",
"device_name": "{{trigger.device_name}}",
"stop_name": "{{trigger.geofence_name}}",
"arrived_at": "{{trigger.timestamp}}",
"location": {
"lat": "{{trigger.location.latitude}}",
"lng": "{{trigger.location.longitude}}"
}
}
}
}
}
],
"edges": [
{"id": "edge-1", "source": "trigger-1", "target": "action-1"}
]
}'

The workflow fires for every geofence tagged tms-sync, so a single workflow covers your entire imported stop list.

Other available action types include email, SMS, and Slack. For receiver implementation patterns, retry handling, and security (signature verification), see the Webhook Integration guide.

Keeping Geofences in Sync (Idempotent Re-import)

Most TMS/CRM systems update stop lists daily or on dispatch. Re-importing with dedup_strategy=skip is safe and idempotent:

  • Unchanged addresses — skipped; existing geofences are preserved.
  • New addresses — geocoded and added.
  • Removed addresses — not automatically deleted; archive or tag-filter as needed.

To update geometry for stops whose physical address has changed, use dedup_strategy=override on that import. To guarantee a clean slate (no stale stops), delete geofences carrying the tag first, then re-import with a fresh CSV.


See also: