Geofences

Geofences define geographic boundaries for time tracking. Users can be required to clock in/out within geofenced areas, and automatic actions can be triggered when users exit.

šŸ“˜

Geofence-related events

The Time activity webhook includes geofence override approvals and auto-approvals. Completed punches may also include locationData and geofenceId.

Endpoints

MethodEndpointDescription
GET/time-clock/v1/time-clocks/{timeClockId}/geofencesGet all geofences
POST/time-clock/v1/time-clocks/{timeClockId}/geofencesCreate geofences
GET/time-clock/v1/time-clocks/{timeClockId}/geofences/{fenceId}Get a geofence
DELETE/time-clock/v1/time-clocks/{timeClockId}/geofences/{fenceId}Delete a geofence

Geofence Properties

PropertyTypeDescription
idstringUnique identifier
namestringDisplay name (required, min 1 character)
isEnabledbooleanWhether the geofence is active (default: true)
fenceSizeintegerRadius in meters (75-1524)
jobIdsarrayJob IDs the geofence is scoped to, interpreted by jobIdsMode (max 100)
jobIdsModestringHow jobIds is interpreted: any, specific, or anyExcept (default: specific)
onGeofenceExitActionstringAction when user exits
locationobjectGPS coordinates and address

Exit Actions

ActionDescription
noActionNo automatic action (default)
sendNotificationSend notification to user/admin
autoClockOutAutomatically clock out the user

Job Targeting

jobIdsMode controls which jobs a geofence applies to. It always works together with jobIds.

ModeBehaviorjobIds
specificApplies only to the listed jobs. This is the default.Required (see note below)
anyApplies to every job on the time clock.Ignored - pass an empty array
anyExceptApplies to every job on the time clock except the listed ones, including jobs created later.The jobs to exclude
šŸ“˜

Choosing between any and anyExcept

Use anyExcept when new jobs should automatically inherit the geofence. A geofence created with anyExcept keeps applying to jobs added to the time clock after the geofence was created, without any follow-up API call. With specific, you have to update the geofence yourself every time a new job should be covered.

Rules that apply to jobIds in every mode:

  • If a job has sub-jobs, pass only the parent job ID. Sub-job IDs are rejected.
  • Every job ID must belong to the time clock in the path.
  • Max 100 job IDs per geofence.

Get All Geofences

curl --request GET \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/geofences \
  --header 'X-API-KEY: YOUR_API_KEY'

Response

{
  "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "data": {
    "geofences": [
      {
        "id": "fence-abc123",
        "name": "Main Office",
        "isEnabled": true,
        "fenceSize": 150,
        "jobIds": ["job-123", "job-456"],
        "jobIdsMode": "specific",
        "onGeofenceExitAction": "sendNotification",
        "location": {
          "address": "123 Main St, New York, NY 10001",
          "latitude": "40.7128",
          "longitude": "-74.0060"
        }
      },
      {
        "id": "fence-def456",
        "name": "Warehouse",
        "isEnabled": true,
        "fenceSize": 300,
        "jobIds": [],
        "jobIdsMode": "any",
        "onGeofenceExitAction": "autoClockOut",
        "location": {
          "address": "456 Industrial Blvd, Brooklyn, NY 11201",
          "latitude": "40.6892",
          "longitude": "-73.9857"
        }
      }
    ]
  }
}

Create Geofences

Create one or more geofences for a time clock.

āš ļø

Constraints

  • Max 1000 geofences per request
  • Max 100 job IDs per geofence
  • Fence size: 75-1524 meters
  • Latitude/longitude must be numeric strings

Request Body

FieldTypeRequiredDescription
namestringYesGeofence name (min 1 char)
locationobjectYesGPS coordinates with address
isEnabledbooleanNoWhether active (default: true)
fenceSizeintegerNoRadius in meters (75-1524)
jobIdsarrayConditionalJob IDs, interpreted by jobIdsMode. Required when jobIdsMode is specific and job enforcement is enabled
jobIdsModestringNoany, specific, or anyExcept (default: specific)
onGeofenceExitActionstringNoExit action (default: noAction)

Location Object

FieldTypeRequiredDescription
addressstringYesStreet address
latitudestringYesLatitude as string (e.g., "40.7128")
longitudestringYesLongitude as string (e.g., "-74.0060")

Example: Create Single Geofence

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/geofences \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "geofences": [
      {
        "name": "Client Site A",
        "location": {
          "address": "789 Business Park, Chicago, IL 60601",
          "latitude": "41.8781",
          "longitude": "-87.6298"
        },
        "fenceSize": 200,
        "jobIds": [],
        "jobIdsMode": "any",
        "onGeofenceExitAction": "sendNotification"
      }
    ]
  }'

This geofence uses jobIdsMode: "any", so it applies to every job on the time clock.

Example: Create with Job Association

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/geofences \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "geofences": [
      {
        "name": "Construction Site",
        "location": {
          "address": "100 Build Ave, Houston, TX 77001",
          "latitude": "29.7604",
          "longitude": "-95.3698"
        },
        "fenceSize": 500,
        "jobIds": ["job-construction-001"],
        "jobIdsMode": "specific",
        "onGeofenceExitAction": "autoClockOut"
      }
    ]
  }'

Example: Apply to Every Job Except Some

Use anyExcept when the geofence should cover the whole time clock apart from a few jobs, and should keep covering jobs that are added later.

curl --request POST \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/geofences \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: YOUR_API_KEY' \
  --data '{
    "geofences": [
      {
        "name": "Headquarters",
        "location": {
          "address": "1 Corporate Plaza, Austin, TX 78701",
          "latitude": "30.2672",
          "longitude": "-97.7431"
        },
        "fenceSize": 300,
        "jobIds": ["job-remote-support", "job-field-service"],
        "jobIdsMode": "anyExcept",
        "onGeofenceExitAction": "sendNotification"
      }
    ]
  }'

The geofence applies to every job on time clock 12345 except job-remote-support and job-field-service. Any job created on that time clock afterwards is covered automatically.

Example: Bulk Create

async function createGeofences(timeClockId, sites) {
  const geofences = sites.map(site => ({
    name: site.name,
    location: {
      address: site.address,
      latitude: String(site.lat),
      longitude: String(site.lng)
    },
    fenceSize: site.radius || 150,
    jobIds: site.jobIds || [],
    jobIdsMode: site.jobIdsMode || 'specific',
    onGeofenceExitAction: 'sendNotification'
  }));

  const response = await fetch(
    `https://api.connecteam.com/time-clock/v1/time-clocks/${timeClockId}/geofences`,
    {
      method: 'POST',
      headers: {
        'X-API-KEY': 'YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ geofences })
    }
  );

  return response.json();
}

// Usage
await createGeofences(12345, [
  { name: 'Site A', address: '123 A St', lat: 40.7128, lng: -74.0060, radius: 200 },
  { name: 'Site B', address: '456 B Ave', lat: 40.7145, lng: -74.0055, radius: 150 }
]);

Response

{
  "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "data": {
    "geofences": [
      {
        "fenceId": "fence-new123",
        "name": "Client Site A"
      }
    ]
  }
}

Delete Geofence

Remove a geofence from a time clock.

curl --request DELETE \
  --url https://api.connecteam.com/time-clock/v1/time-clocks/12345/geofences/fence-abc123 \
  --header 'X-API-KEY: YOUR_API_KEY'

Response

{
  "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "data": {}
}

Error Responses

400 Bad Request

Invalid coordinates:

{
  "detail": ["GeoFence 'Site A' has invalid latitude or longitude values. They must be numeric strings."]
}

Jobs not in time clock:

{
  "detail": ["jobs: ['job-invalid'] not in the required time clock"]
}

Jobs required but missing:

Returned when the time clock requires a job on clock in, jobIdsMode is specific, and jobIds is empty. any and anyExcept are accepted with an empty jobIds.

{
  "detail": ["Job IDs are required based on the account configuration."]
}

Sub-job used as a parent job:

{
  "detail": ["The job ID 'job-sub-001' is a sub job and cannot be used as a parent job"]
}

404 Not Found

{
  "detail": "Geofence not found"
}

API Reference


Did this page help you?