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/apiAPI Versions
Endpoint | Status | Response Version | Timestamp Behavior |
|---|---|---|---|
| Recommended |
| ISO 8601 with the agency's DST-aware UTC offset |
| Legacy compatibility |
| 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
- Log into your Watchtower account
- Navigate to Account Settings
- Go to the Security section
- Click Generate New API Credentials
- Provide a descriptive name (e.g., "Mobile App Integration")
- 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_hereEndpoints
GET /v2/incidents
Retrieves incident data with optional filtering and pagination.
Request Headers
Header | Required | Description |
|---|---|---|
| Yes | Your API client ID |
| Yes | Your API client secret |
Query Parameters
Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| string | No | Filter incidents from this instant (ISO 8601; include | |
| string | No | Filter incidents to this instant (ISO 8601; include | |
| string | No | Filter by incident category (partial match) | |
| boolean | No | false | Include incident notes in response |
| boolean | No | false | Include involved party information |
| string | No | Filter records modified since this instant (ISO 8601; include | |
| string | No | Exclusive upper bound for | |
| integer | No | 1000 | Number of records per page (max 1000) |
| integer | No | 0 | Number of records to skip |
| 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 |
|---|---|---|
| string | When the incident occurred (ISO 8601 in the agency timezone) |
| number | Number of people involved |
| string | When the record was created (ISO 8601 in the agency timezone) |
| string | When the record was last modified (ISO 8601 in the agency timezone) |
| string | Incident category/type |
| string | Name of person who submitted the report |
| string | Unique call/incident number |
| number | Latitude coordinate (5 decimal places) |
| number | Longitude coordinate (5 decimal places) |
| string | Location name/description |
| array | Equipment/units used in response |
| array | Personnel who responded |
| number | Air temperature (°F or °C based on agency settings) |
| number | Tide height (feet or meters based on agency settings) |
| number | Wave height (feet or meters based on agency settings) |
| number | Wave interval (seconds) |
| number | Water temperature (°F or °C based on agency settings) |
| string | Your agency ID |
| string | Your agency name |
| array | Incident notes (if |
| array | Involved party details (if |
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=200Method 2: Page-based
GET /api/v2/incidents?limit=100&page=3The 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
- Store credentials securely - Never commit API credentials to version control
- Use environment variables - Store credentials in environment variables or secure configuration
- Rotate credentials regularly - Generate new credentials periodically
- Monitor usage - Check your API credential status in Account Settings
- 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:
- Email: support@yourwatchtower.com
- Phone: 714-316-9734
- Documentation: https://knowledge.yourwatchtower.com/en/
Changelog
Version 2.0.0-beta.2
- Added the optional
modifiedBeforeexclusive upper bound for historical delta synchronization - Limited unbounded
modifiedDateTimelookbacks and bounded modification windows to 14 days
Version 2.0.0-beta.1
- Added the versioned
/api/v2/incidentsendpoint while preserving/api/incidentsbehavior - 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
Zand explicit numeric offsets - Standardized the
modifiedDateTimeresponse field casing (previouslymodifieddatetimein ISO responses)
Version 1.0.0-beta.4
- Added
modifiedDateTimeparameter 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
Thank you!