API Examples
API Examples
Section titled “API Examples”This page provides copy-and-paste code samples for the most common API operations. Each example is shown in curl, JavaScript (using fetch), and Python (using requests).
Replace https://your-instance.overwatch.com with the base URL of your Overwatch deployment throughout.
Authenticating
Section titled “Authenticating”Obtain an access token by sending the user’s email and password to the login endpoint. The response includes an access_token (valid for 1 hour) and a refresh_token (valid for 7 days).
curl -X POST "https://your-instance.overwatch.com/api/v1/auth/login" \ -H "Content-Type: application/json" \ -d '{ "email": "engineer@example.com", "password": "your-password" }'Response:
{ "access_token": "eyJhbGciOiJIUzI1NiIs...", "refresh_token": "eyJhbGciOiJIUzI1NiIs...", "token_type": "bearer", "expires_in": 3600, "user": { "id": "user-uuid", "email": "engineer@example.com", "name": "Jane Doe", "role": "engineer" }}Store the access_token and use it in subsequent requests:
export TOKEN="eyJhbGciOiJIUzI1NiIs..."const BASE_URL = "https://your-instance.overwatch.com/api/v1";
async function login(email, password) { const response = await fetch(`${BASE_URL}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), });
if (!response.ok) { const error = await response.json(); throw new Error(error.detail || "Login failed"); }
const data = await response.json(); // data.access_token — use in Authorization header // data.refresh_token — store securely for token refresh return data;}
// Usageconst session = await login("engineer@example.com", "your-password");const token = session.access_token;import requests
BASE_URL = "https://your-instance.overwatch.com/api/v1"
def login(email: str, password: str) -> dict: response = requests.post( f"{BASE_URL}/auth/login", json={"email": email, "password": password}, ) response.raise_for_status() return response.json()
# Usagesession = login("engineer@example.com", "your-password")token = session["access_token"]
# Create a reusable session with the tokenapi = requests.Session()api.headers.update({"Authorization": f"Bearer {token}"})Listing Incidents
Section titled “Listing Incidents”Retrieve a paginated list of incidents. You can filter by status, severity, and search terms. Results are sorted by creation date (newest first) by default.
# List active high-severity incidents (first 25)curl -X GET "https://your-instance.overwatch.com/api/v1/incidents?status=active&severity=high&limit=25" \ -H "Authorization: Bearer $TOKEN"Response:
{ "data": [ { "id": "inc-uuid-001", "title": "Payment service latency spike", "severity": "high", "status": "active", "created_at": "2026-02-23T08:15:00Z", "assignee": "Jane Doe" }, { "id": "inc-uuid-002", "title": "Database connection pool exhaustion", "severity": "high", "status": "active", "created_at": "2026-02-23T07:42:00Z", "assignee": null } ], "meta": { "total": 2, "limit": 25, "offset": 0 }}async function listIncidents(token, { status, severity, limit = 25 } = {}) { const params = new URLSearchParams(); if (status) params.set("status", status); if (severity) params.set("severity", severity); params.set("limit", String(limit));
const response = await fetch( `${BASE_URL}/incidents?${params.toString()}`, { headers: { Authorization: `Bearer ${token}` }, } );
if (!response.ok) { const error = await response.json(); throw new Error(error.detail || "Failed to list incidents"); }
return response.json();}
// Usageconst result = await listIncidents(token, { status: "active", severity: "high",});
console.log(`Found ${result.meta.total} incidents`);for (const incident of result.data) { console.log(`[${incident.severity}] ${incident.title}`);}def list_incidents( session: requests.Session, status: str = None, severity: str = None, limit: int = 25,) -> dict: params = {"limit": limit} if status: params["status"] = status if severity: params["severity"] = severity
response = session.get(f"{BASE_URL}/incidents", params=params) response.raise_for_status() return response.json()
# Usageresult = list_incidents(api, status="active", severity="high")
print(f"Found {result['meta']['total']} incidents")for incident in result["data"]: print(f"[{incident['severity']}] {incident['title']}")Creating an Incident
Section titled “Creating an Incident”Create a new incident by providing a title, severity, and description. The API returns the newly created incident with its generated ID and timestamps.
curl -X POST "https://your-instance.overwatch.com/api/v1/incidents" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Redis cluster failover in us-east-1", "severity": "critical", "description": "Primary Redis node became unreachable at 14:32 UTC. Sentinel promoted a replica, but several services experienced cache misses and elevated latency during the failover window." }'Response:
{ "id": "inc-uuid-003", "title": "Redis cluster failover in us-east-1", "severity": "critical", "status": "active", "description": "Primary Redis node became unreachable at 14:32 UTC...", "created_at": "2026-02-23T14:35:00Z", "created_by": "user-uuid"}async function createIncident(token, { title, severity, description }) { const response = await fetch(`${BASE_URL}/incidents`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ title, severity, description }), });
if (!response.ok) { const error = await response.json(); throw new Error(error.detail || "Failed to create incident"); }
return response.json();}
// Usageconst incident = await createIncident(token, { title: "Redis cluster failover in us-east-1", severity: "critical", description: "Primary Redis node became unreachable at 14:32 UTC. " + "Sentinel promoted a replica, but several services experienced " + "cache misses and elevated latency during the failover window.",});
console.log(`Created incident ${incident.id}`);def create_incident( session: requests.Session, title: str, severity: str, description: str,) -> dict: response = session.post( f"{BASE_URL}/incidents", json={ "title": title, "severity": severity, "description": description, }, ) response.raise_for_status() return response.json()
# Usageincident = create_incident( api, title="Redis cluster failover in us-east-1", severity="critical", description=( "Primary Redis node became unreachable at 14:32 UTC. " "Sentinel promoted a replica, but several services experienced " "cache misses and elevated latency during the failover window." ),)
print(f"Created incident {incident['id']}")Searching Incidents
Section titled “Searching Incidents”Use the semantic search endpoint to find incidents by meaning, not just keywords. The search engine converts your query into a vector embedding and returns ranked results based on conceptual similarity.
curl -X POST "https://your-instance.overwatch.com/api/v1/search/query" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "query": "database connection pool exhaustion causing timeouts", "limit": 10 }'Response:
{ "results": [ { "id": "inc-uuid-002", "title": "Database connection pool exhaustion", "type": "incident", "score": 0.94, "snippet": "PostgreSQL max_connections limit reached, causing new connection requests to time out..." }, { "id": "inc-uuid-017", "title": "Aurora read replica connection saturation", "type": "incident", "score": 0.87, "snippet": "Read replica hit 500 connection ceiling during traffic spike..." }, { "id": "proc-uuid-005", "title": "Restart connection pooler (PgBouncer)", "type": "procedure", "score": 0.82, "snippet": "Steps to safely restart PgBouncer without dropping active queries..." } ], "meta": { "total": 3, "query_time_ms": 142 }}async function searchIncidents(token, query, limit = 10) { const response = await fetch(`${BASE_URL}/search/query`, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ query, limit }), });
if (!response.ok) { const error = await response.json(); throw new Error(error.detail || "Search failed"); }
return response.json();}
// Usageconst results = await searchIncidents( token, "database connection pool exhaustion causing timeouts");
for (const result of results.results) { console.log(`[${result.score.toFixed(2)}] ${result.title} (${result.type})`);}def search_incidents( session: requests.Session, query: str, limit: int = 10,) -> dict: response = session.post( f"{BASE_URL}/search/query", json={"query": query, "limit": limit}, ) response.raise_for_status() return response.json()
# Usageresults = search_incidents( api, query="database connection pool exhaustion causing timeouts",)
for result in results["results"]: print(f"[{result['score']:.2f}] {result['title']} ({result['type']})")Tip: Semantic search returns results across multiple entity types — incidents, procedures, and knowledge base articles. Use the
typefield in each result to distinguish between them.
Using an API Key
Section titled “Using an API Key”If you are building a service integration or automation script, use an API key instead of JWT tokens. The X-API-Key header replaces the Authorization header.
curl -X GET "https://your-instance.overwatch.com/api/v1/incidents?status=active" \ -H "X-API-Key: overwatch_ak_1a2b3c4d5e6f7890"const API_KEY = "overwatch_ak_1a2b3c4d5e6f7890";
async function listActiveIncidents() { const response = await fetch( `${BASE_URL}/incidents?status=active`, { headers: { "X-API-Key": API_KEY }, } );
if (!response.ok) { const error = await response.json(); throw new Error(error.detail || "Request failed"); }
return response.json();}API_KEY = "overwatch_ak_1a2b3c4d5e6f7890"
api_key_session = requests.Session()api_key_session.headers.update({"X-API-Key": API_KEY})
response = api_key_session.get(f"{BASE_URL}/incidents", params={"status": "active"})response.raise_for_status()incidents = response.json()Note: API keys are long-lived and do not expire automatically (unless you set an
expires_atduring creation). See the Authentication guide for details on creating and managing API keys.
Error Handling
Section titled “Error Handling”All API errors return a JSON body with a detail field. Here is a pattern for consistent error handling across your application.
class OverwatchAPIError extends Error { constructor(statusCode, detail) { super(detail); this.statusCode = statusCode; this.name = "OverwatchAPIError"; }}
async function apiRequest(method, path, { token, body } = {}) { const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json", };
const response = await fetch(`${BASE_URL}${path}`, { method, headers, body: body ? JSON.stringify(body) : undefined, });
if (!response.ok) { const error = await response.json().catch(() => ({})); if (response.status === 401) { // Token expired — trigger refresh or redirect to login console.warn("Token expired, refreshing..."); } throw new OverwatchAPIError( response.status, error.detail || `Request failed with status ${response.status}` ); }
return response.json();}from requests.exceptions import HTTPError
def api_request(session: requests.Session, method: str, path: str, **kwargs) -> dict: response = session.request(method, f"{BASE_URL}{path}", **kwargs)
try: response.raise_for_status() except HTTPError: error_body = response.json() if response.content else {} detail = error_body.get("detail", f"Request failed with status {response.status_code}")
if response.status_code == 401: # Token expired — trigger refresh or re-authenticate print("Token expired, refreshing...")
raise Exception(f"[{response.status_code}] {detail}")
return response.json()# Check the HTTP status code in scriptsHTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ -X GET "https://your-instance.overwatch.com/api/v1/incidents" \ -H "Authorization: Bearer $TOKEN")
if [ "$HTTP_CODE" -eq 401 ]; then echo "Token expired. Re-authenticate and retry."elif [ "$HTTP_CODE" -ne 200 ]; then echo "Request failed with status $HTTP_CODE"fiNext Steps
Section titled “Next Steps”- Authentication — Detailed guide to JWT and API key authentication.
- API Reference — Full endpoint listing.
- Webhooks — Receiving and sending webhook events.