API Docs

Watchtower Public API Documentation


Overview


The Watchtower Public API allows you to programmatically access your incident data. This REST API provides secure access to submission and dispatch information with flexible filtering and pagination options.


Base URL


https://api.yourwatchtower.com/api


API Versions


Endpoint

Status

Response Version

Timestamp Behavior

/v2/incidents

Recommended

2.0.0-beta.2

ISO 8601 with the agency's DST-aware UTC offset

/incidents

Legacy compatibility

1.0.0-beta.3

Original beta timestamp and filter behavior


The legacy endpoint remains unchanged for existing integrations. It continues to return the historical timestamp representation and lowercase modifieddatetime field used by ISO responses. New integrations should use v2.


Authentication


The API uses client credentials authentication. You'll need to generate API credentials from your Watchtower account.


Generating API Credentials


  1. Log into your Watchtower account
  2. Navigate to Account Settings
  3. Go to the Security section
  4. Click Generate New API Credentials
  5. Provide a descriptive name (e.g., "Mobile App Integration")
  6. Important: Copy and save the Client Secret immediately - it won't be shown again!


Authentication Headers


Include these headers in all API requests:


X-Client-Id: your_client_id_here
X-Client-Secret: your_client_secret_here


Endpoints


GET /v2/incidents


Retrieves incident data with optional filtering and pagination.


Request Headers


Header

Required

Description

X-Client-Id

Yes

Your API client ID

X-Client-Secret

Yes

Your API client secret


Query Parameters


Parameter

Type

Required

Default

Description

startDate

string

No


Filter incidents from this instant (ISO 8601; include Z or a numeric offset)

endDate

string

No


Filter incidents to this instant (ISO 8601; include Z or a numeric offset)

categoryLabel

string

No


Filter by incident category (partial match)

includeNotes

boolean

No

false

Include incident notes in response

includeInvolvedParties

boolean

No

false

Include involved party information

modifiedDateTime

string

No


Filter records modified since this instant (ISO 8601; include Z or an offset)

modifiedBefore

string

No


Exclusive upper bound for modifiedDateTime; required when requesting data older than 14 days

limit

integer

No

1000

Number of records per page (max 1000)

offset

integer

No

0

Number of records to skip

page

integer

No


Page number (alternative to offset)


Example Requests


Basic Request


curl -X GET "https://api.yourwatchtower.com/api/v2/incidents?startDate=2024-01-01T00:00:00Z&endDate=2024-12-31T23:59:59Z&limit=100&includeNotes=true" \
  -H "X-Client-Id: your_client_id" \
  -H "X-Client-Secret: your_client_secret"


Delta Loading Request


curl -X GET "https://api.yourwatchtower.com/api/v2/incidents?modifiedDateTime=2024-01-15T10:30:00Z&modifiedBefore=2024-01-29T10:30:00Z&limit=100" \
  -H "X-Client-Id: your_client_id" \
  -H "X-Client-Secret: your_client_secret"


Unbounded modifiedDateTime polling is limited to the most recent 14 days. For
historical synchronization, provide a modifiedBefore timestamp no more than 14
days after modifiedDateTime. The upper bound is exclusive, so adjacent windows
can be processed without overlap:


curl -X GET "https://api.yourwatchtower.com/api/v2/incidents?modifiedDateTime=2024-01-01T00:00:00Z&modifiedBefore=2024-01-15T00:00:00Z&limit=1000" \
  -H "X-Client-Id: your_client_id" \
  -H "X-Client-Secret: your_client_secret"


Response Format


{
  "data": [
    {
      "incidentDateTime": "2024-06-15T14:30:00.000-07:00",
      "involved": 2,
      "creationDateTime": "2024-06-15T14:32:00.000-07:00",
      "modifiedDateTime": "2024-06-15T14:35:00.000-07:00",
      "category": "Water Rescue",
      "submittedBy": "John Smith",
      "callNumber": "SD24-001234",
      "lat": 32.7157,
      "lng": -117.1611,
      "location": "Mission Beach",
      "resource": ["Lifeguard Unit 1", "Rescue Boat 2"],
      "responders": ["John Smith", "Jane Doe"],
      "airTemp": 72,
      "tideHeight": 4.2,
      "waveHeight": 3,
      "waveInterval": 12,
      "waterTemp": 68,
      "agencyId": "agency_123",
      "agency": "San Diego Lifeguards",
      "notes": ["Initial response notes", "Follow-up information"],
      "involvedParties": ["Individual - 25 - Male - Local"]
    }
  ],
  "version": "2.0.0-beta.2",
  "pagination": {
    "totalCount": 1250,
    "hasNextPage": true,
    "hasPreviousPage": false,
    "currentPage": 1,
    "totalPages": 13,
    "limit": 100,
    "offset": 0
  }
}


Response Fields


Field

Type

Description

incidentDateTime

string

When the incident occurred (ISO 8601 in the agency timezone)

involved

number

Number of people involved

creationDateTime

string

When the record was created (ISO 8601 in the agency timezone)

modifiedDateTime

string

When the record was last modified (ISO 8601 in the agency timezone)

category

string

Incident category/type

submittedBy

string

Name of person who submitted the report

callNumber

string

Unique call/incident number

lat

number

Latitude coordinate (5 decimal places)

lng

number

Longitude coordinate (5 decimal places)

location

string

Location name/description

resource

array

Equipment/units used in response

responders

array

Personnel who responded

airTemp

number

Air temperature (°F or °C based on agency settings)

tideHeight

number

Tide height (feet or meters based on agency settings)

waveHeight

number

Wave height (feet or meters based on agency settings)

waveInterval

number

Wave interval (seconds)

waterTemp

number

Water temperature (°F or °C based on agency settings)

agencyId

string

Your agency ID

agency

string

Your agency name

notes

array

Incident notes (if includeNotes=true)

involvedParties

array

Involved party details (if includeInvolvedParties=true)


Note: Temperature and distance measurements are automatically returned in your agency's preferred units (Imperial or Metric) as configured in your account settings.


Timestamp Handling


Response timestamps include the agency's UTC offset and automatically reflect daylight saving time. For example, an America/Los_Angeles timestamp uses -08:00 during standard time and -07:00 during daylight time. A trailing Z is only used for UTC.


Timestamp filters are normalized to UTC before comparison. Include either Z or an explicit numeric offset in filter values. Offset-less values are interpreted as UTC for backward compatibility.


Pagination


The API supports two pagination methods:


Method 1: Offset-based


GET /api/v2/incidents?limit=100&offset=200


Method 2: Page-based


GET /api/v2/incidents?limit=100&page=3


The page parameter automatically calculates the offset: offset = (page - 1) * limit


Error Responses


Status Code

Description

400

Bad Request - Invalid parameters

401

Unauthorized - Invalid credentials

403

Forbidden - Access denied

500

Internal Server Error


Error Response Format


{
  "error": "Invalid client credentials"
}


Rate Limits


  • Maximum 1000 records per request
  • API credentials can be disabled if misused


Security Best Practices


  1. Store credentials securely - Never commit API credentials to version control
  2. Use environment variables - Store credentials in environment variables or secure configuration
  3. Rotate credentials regularly - Generate new credentials periodically
  4. Monitor usage - Check your API credential status in Account Settings
  5. Disable unused credentials - Disable credentials that are no longer needed


Code Examples


JavaScript/Node.js


const axios = require('axios')

const apiClient = axios.create({
baseURL: 'https://api.yourwatchtower.com/api',
headers: {
'X-Client-Id': process.env.WATCHTOWER_CLIENT_ID,
'X-Client-Secret': process.env.WATCHTOWER_CLIENT_SECRET
}
})

async function getIncidents(params = {}) {
try {
const response = await apiClient.get('/v2/incidents', { params })
return response.data
} catch (error) {
console.error('API Error:', error.response?.data || error.message)
throw error
}
}

// Initial load - get all data
async function initialLoad() {
const data = await getIncidents({
startDate: '2024-01-01T00:00:00Z',
endDate: '2024-12-31T23:59:59Z',
limit: 1000,
includeNotes: true
})

console.log(`Initial load: ${data.data.length} incidents`)

// Store the timestamp for delta loading
const lastSyncTime = new Date().toISOString()
return { data, lastSyncTime }
}

// Delta load - get only updated records
async function deltaLoad(lastSyncTime) {
const data = await getIncidents({
modifiedDateTime: lastSyncTime,
limit: 1000,
includeNotes: true
})

console.log(`Delta load: ${data.data.length} new/updated incidents`)
return data
}


Python


import requests
import os
from datetime import datetime

class WatchtowerAPI:
def __init__(self):
self.base_url = 'https://api.yourwatchtower.com/api'
self.headers = {
'X-Client-Id': os.getenv('WATCHTOWER_CLIENT_ID'),
'X-Client-Secret': os.getenv('WATCHTOWER_CLIENT_SECRET')
}

def get_incidents(self, **params):
response = requests.get(
f'{self.base_url}/v2/incidents',
headers=self.headers,
params=params
)
response.raise_for_status()
return response.json()

def delta_sync(self, last_sync_time):
"""Get incidents modified since last sync"""
return self.get_incidents(
modifiedDateTime=last_sync_time,
limit=1000
)

# Usage
api = WatchtowerAPI()

# Initial load
initial_data = api.get_incidents(
startDate='2024-01-01T00:00:00Z',
endDate='2024-12-31T23:59:59Z',
limit=1000,
includeNotes=True
)

print(f"Initial load: {len(initial_data['data'])} incidents")

# Store sync timestamp
last_sync = datetime.utcnow().isoformat() + 'Z'

# Later - delta load
delta_data = api.delta_sync(last_sync)
print(f"Delta load: {len(delta_data['data'])} new/updated incidents")


cURL


#!/bin/bash

CLIENT_ID="your_client_id"
CLIENT_SECRET="your_client_secret"

# Initial load
echo "Performing initial load..."
curl -X GET "https://api.yourwatchtower.com/api/v2/incidents?startDate=2024-01-01T00:00:00Z&limit=1000" \
  -H "X-Client-Id: $CLIENT_ID" \
  -H "X-Client-Secret: $CLIENT_SECRET" \
  -H "Accept: application/json"

# Delta load (replace timestamp with your last sync time)
echo "Performing delta load..."
curl -X GET "https://api.yourwatchtower.com/api/v2/incidents?modifiedDateTime=2024-01-15T10:30:00Z&modifiedBefore=2024-01-29T10:30:00Z&limit=1000" \
  -H "X-Client-Id: $CLIENT_ID" \
  -H "X-Client-Secret: $CLIENT_SECRET" \
  -H "Accept: application/json"


Rate Limiting


IP-based rate limiting is enforced through AWS WAF:


  • Public Incidents API: 100 requests per 5-minute window per IP address
  • Aggregation: Custom keys (IP address + HTTP method)
  • Action: Block requests exceeding limit
  • Response: HTTP 403 (Forbidden)


Support


For API support, contact:



Changelog


Version 2.0.0-beta.2


  • Added the optional modifiedBefore exclusive upper bound for historical delta synchronization
  • Limited unbounded modifiedDateTime lookbacks and bounded modification windows to 14 days


Version 2.0.0-beta.1


  • Added the versioned /api/v2/incidents endpoint while preserving /api/incidents behavior
  • Fixed response timestamps that previously represented agency-local time with an incorrect trailing Z
  • Response timestamps now include the agency's DST-aware UTC offset
  • Fixed ISO 8601 request filters to honor Z and explicit numeric offsets
  • Standardized the modifiedDateTime response field casing (previously modifieddatetime in ISO responses)


Version 1.0.0-beta.4


  • Added modifiedDateTime parameter for delta loading and incremental data synchronization
  • Supports both standalone delta loading and combined filtering with date ranges


Version 1.0.0-beta.3


  • Improved property naming for consistency and clarity (incidentTime -> incidentDateTime)


Version 1.0.0-beta.2


  • Time values now returned in ISO 8601 format
  • Includes modifiedDateTime field for accurate change tracking
  • Improved property naming for consistency and clarity
  • Consistent camelCasing on response object


Version 1.0.0-beta.1


  • Initial release with incident data access
  • Support for filtering by date range and category
  • Pagination support
  • Optional notes and involved parties data
  • Automatic unit conversion based on agency preferences

Updated on: 03/08/2026

Was this article helpful?

Share your feedback

Cancel

Thank you!