> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://knowledge.yourwatchtower.com/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# 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:

```http
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

```bash
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

```bash
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:

```bash
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

```json
{
  "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

```json
{
  "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

```javascript
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

```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

```bash
#!/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 `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
