conflictData / API_REFERENCE.md
Hardik Singh
Upgrade: High-Fidelity Geocoding & Precision UI
48486c6

ConflictIQ API Reference

This document describes the API routes currently implemented in the backend (/api/v1), including request parameters and response body shapes.

Base URLs

  • API base: /api/v1
  • Health (also exposed outside API prefix): /health

Authentication

  • No authentication/authorization is currently enforced in these routes.

Common Notes

  • All dates use YYYY-MM-DD.
  • Most timestamps are ISO-like strings and often end with Z.
  • Error responses from FastAPI typically look like:
    { "detail": "Error message" }
    

Core Event Object (conflict_events row)

Many endpoints return raw or near-raw conflict_events rows. Typical fields:

  • id (integer)
  • event_id (string)
  • source (string)
  • source_reliability (string)
  • event_time (string datetime)
  • event_date (string date)
  • year (integer)
  • week (integer)
  • country (string)
  • country_iso3 (string)
  • region, admin1, admin2, city (string/null)
  • lat, lon (number/null)
  • geo_precision (integer)
  • event_type, event_subtype, interaction_code (string/null)
  • actor1, actor1_type, actor2, actor2_type (string/null)
  • fatalities, fatalities_civilians (integer)
  • fatalities_confidence (string)
  • severity (string/null)
  • severity_score (number/null)
  • title, notes (string/null)
  • tags (string array/null)
  • source_url (string/null)
  • conflict_name (string/null)
  • conflict_id (integer/null)
  • category (string)
  • ingested_at (string datetime)
  • geom (internal PostGIS object, excluded in some endpoints)

Health Endpoints

GET /health

GET /api/v1/health

Request parameters: none

Response body:

{
  "status": "OK",
  "events_total": 1234,
  "database_connected": true,
  "redis_connected": true
}

Conflicts API (/api/v1/conflicts)

GET /api/v1/conflicts

Query parameters:

  • country (string, optional)
  • category (string, optional)
  • from_date (date, optional, default = today - 7 days)
  • to_date (date, optional, default = today)
  • event_type (string, optional)
  • severity (string, optional)
  • min_fatalities (integer, optional, default 0)
  • tags (string, optional, comma-separated, example: urban-combat,artillery)
  • limit (integer, optional, default 100, max 500)
  • offset (integer, optional, default 0)

Response body:

{
  "status": 200,
  "success": true,
  "count": 2,
  "data": [{ "...event fields..." : "..." }],
  "meta": {
    "from_cache": false,
    "page": 1,
    "per_page": 100
  }
}

GET /api/v1/conflicts/recent

Query parameters:

  • days (integer, optional, default 7)
  • limit (integer, optional, default 100)

Response body:

{
  "status": 200,
  "success": true,
  "count": 2,
  "data": [{ "...event fields..." : "..." }],
  "meta": { "from_cache": false }
}

GET /api/v1/conflicts/ongoing

Query parameters:

  • limit (integer, optional, default 50)

Response body:

{
  "status": 200,
  "success": true,
  "count": 2,
  "data": [{ "...event fields without geom..." : "..." }]
}

GET /api/v1/conflicts/historical

Query parameters:

  • days_ago (integer, optional, default 2)
  • limit (integer, optional, default 100)

Response body:

{
  "status": 200,
  "success": true,
  "count": 2,
  "data": [{ "...event fields without geom..." : "..." }]
}

GET /api/v1/conflicts/near

Query parameters:

  • lat (number, required)
  • lon (number, required)
  • radius_km (integer, optional, default 50)
  • days (integer, optional, default 7)
  • limit (integer, optional, default 100)

Response body:

{
  "status": 200,
  "success": true,
  "count": 2,
  "data": [{ "...event fields without geom..." : "..." }],
  "meta": { "from_cache": false }
}

GET /api/v1/conflicts/country/{iso3}

Path parameters:

  • iso3 (string, required)

Query parameters:

  • days (integer, optional, default 30)
  • limit (integer, optional, default 100)

Response body:

{
  "status": 200,
  "success": true,
  "count": 2,
  "data": [{ "...event fields without geom..." : "..." }],
  "meta": { "from_cache": false }
}

GET /api/v1/conflicts/{event_id}

Path parameters:

  • event_id (string, required)

Response body:

{
  "status": 200,
  "success": true,
  "data": { "...single event fields without geom..." : "..." },
  "meta": { "from_cache": false }
}

404 response:

{ "detail": "Event not found" }

GET /api/v1/conflicts/clusters

Query parameters:

  • precision (number, optional, default 1.0, min 0.1, max 5.0)
  • days (integer, optional, default 7)

Response body:

{
  "status": 200,
  "count": 2,
  "data": [
    {
      "lon": 36.8219,
      "lat": -1.2921,
      "count": 7,
      "main_category": "MILITARY",
      "main_severity": "HIGH"
    }
  ]
}

Stats API

GET /api/v1/stats

GET /api/v1/stats/stats

Query parameters:

  • country (string, optional)
  • days (integer, optional, default 30)

Response body:

{
  "country": "Global",
  "period_days": 30,
  "total_events": 120,
  "by_type": { "Airstrike / Artillery": 20, "Unknown": 5 },
  "by_severity": { "HIGH": 10, "UNKNOWN": 3 },
  "total_fatalities": 450,
  "civilian_fatalities": 120,
  "events_last_24h": 14,
  "trend": "STABLE"
}

GET /api/v1/active-conflicts

Request parameters: none

Response body:

[
  {
    "conflict_id": 1,
    "name": "Russo-Ukrainian War",
    "countries": ["UKR", "RUS"],
    "region": "Europe",
    "start_date": "2022-02-24",
    "status": "ACTIVE",
    "intensity": "WAR",
    "total_events": 2000,
    "last_event_at": "2026-04-20T09:25:00Z"
  }
]

Intelligence API (/api/v1/intel)

GET /api/v1/intel/theaters

Request parameters: none

Response body:

[
  {
    "conflict_id": 1,
    "name": "Example Theater",
    "intensity": "WAR",
    "center_lat": 48.5,
    "center_lon": 37.9,
    "max_severity": 9.2,
    "total_fatalities": 300,
    "dominant_actor": "Actor Name",
    "primary_weapon": "unknown",
    "spread_km": 220.5,
    "total_events": 124,
    "stability_rating": 35.4
  }
]

GET /api/v1/intel/sitrep

Request parameters: none

Response body:

{
  "summary": "Global Intelligence Alert: ...",
  "intensity": "HIGH",
  "stats": {
    "total_events": 50,
    "total_fatalities": 210,
    "top_country": "UKR",
    "top_category": "MILITARY",
    "most_active_actor": "Actor Name"
  }
}

If no recent events:

{
  "summary": "Stable. No major conflict events reported in the last 24h.",
  "intensity": "LOW"
}

GET /api/v1/intel/forecast

Request parameters: none

Response body:

{
  "forecast": "AI-generated strategic forecast text...",
  "risk_level": "CRITICAL",
  "timestamp": "2026-04-20"
}

GET /api/v1/intel/actors

Request parameters: none

Response body:

[
  {
    "actor1": "Actor Name",
    "involvement_count": 17,
    "fatal_impact": 93
  }
]

GET /api/v1/intel/trends

Request parameters: none

Response body:

[
  {
    "country_iso3": "UKR",
    "current_count": 21,
    "previous_count": 8,
    "surge_percentage": 162.5
  }
]

GET /api/v1/intel/hotspots

Request parameters: none

Response body:

[
  {
    "lon": 37.61,
    "lat": 48.01,
    "event_count": 12,
    "country_iso3": "UKR"
  }
]

GET /api/v1/intel/monitor

Request parameters: none

Response body:

  • Array of conflict event objects (SELECT * FROM conflict_events ... LIMIT 15).

GET /api/v1/intel/frontlines

Request parameters: none

Response body:

[
  {
    "lon": 37.6,
    "lat": 47.9,
    "event_count": 5,
    "country": "Ukraine",
    "country_iso3": "UKR",
    "highest_severity": 9.1,
    "primary_engagement": "Armed clash"
  }
]

500 response for intel endpoints:

{ "detail": "Exception message" }

Intelligence Hub API (/api/v1/intel/articles)

GET /api/v1/intel/articles

Query parameters:

  • limit (integer, optional, default 20)
  • offset (integer, optional, default 0)

Response body:

[
  {
    "id": 42,
    "title": "Daily Theater Summary",
    "author": "Ops Desk",
    "tags": ["ukraine", "airstrike"],
    "created_at": "2026-04-20T06:10:00"
  }
]

GET /api/v1/intel/articles/search

Query parameters:

  • q (string, required, min length 2)

Response body:

[
  {
    "id": 42,
    "title": "Daily Theater Summary",
    "author": "Ops Desk",
    "tags": ["ukraine"],
    "created_at": "2026-04-20T06:10:00",
    "rank": 0.425
  }
]

GET /api/v1/intel/articles/{article_id}

Path parameters:

  • article_id (integer, required)

Response body:

  • Full intel_articles row as JSON object:
    • id, title, content, author, tags, created_at, updated_at, search_vector

404 response:

{ "detail": "Article not found" }

POST /api/v1/intel/articles

Request parameters (implemented as function params; currently handled as query/form-style fields):

  • title (string, required)
  • content (string, required)
  • author (string, required)
  • tags (string array, optional, default [])

Response body:

{
  "status": "published",
  "id": 43,
  "created_at": "2026-04-20T08:30:00"
}

Data API (/api/v1/data)

GET /api/v1/data/events

Query parameters:

  • country (string, optional, ISO3)
  • actor (string, optional, matches actor1 or actor2 using ILIKE)
  • start_date (date, optional)
  • end_date (date, optional)
  • limit (integer, optional, default 100)
  • offset (integer, optional, default 0)

Response body:

  • Array of conflict event objects (raw DB rows).

GET /api/v1/data/acled

Query parameters:

  • country (string, optional)
  • limit (integer, optional, default 100)

Response body:

[
  {
    "event_id_cnty": "CIQ-20260420-UKR-00001",
    "event_date": "2026-04-20",
    "year": 2026,
    "event_type": "Airstrike / Artillery",
    "actor1": "Actor A",
    "actor2": "Actor B",
    "country": "Ukraine",
    "location": "Dnipro",
    "latitude": 48.4647,
    "longitude": 35.0462,
    "source": "GDELT",
    "notes": "Text...",
    "fatalities": 8,
    "severity": 8.7
  }
]

GET /api/v1/data/export/csv

Query parameters:

  • country (string, optional)

Response:

  • text/csv streamed file download.
  • Header includes:
    • Content-Disposition: attachment; filename=conflictiq_data_<YYYY-MM-DD>.csv

GET /api/v1/data/geojson

Query parameters:

  • limit (integer, optional, default 500)

Response body:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Point", "coordinates": [36.82, -1.29] },
      "properties": {
        "event_id": "CIQ-...",
        "title": "Event title",
        "event_type": "Armed clash",
        "fatalities": 3
      }
    }
  ]
}

AI Analyst API (/api/v1/ai)

GET /api/v1/ai/analyze

Query parameters:

  • context (string, optional)

Response:

  • Content type: Server-Sent Events (SSE) stream
  • Stream emits token chunks as:
    data: <partial text token>
    

Error behavior:

  • On failure, still returns an SSE stream with an error message token:
    data: [COMMUNICATION LINK ERROR: ...]
    

WebSocket API

WS /api/v1/ws

Client behavior:

  • Connect with WebSocket to /api/v1/ws.
  • Server accepts the socket and keeps it open.
  • Any text the client sends is received (currently not processed into replies).

Server push behavior:

  • On every new conflict_events insert (via PostgreSQL NOTIFY), server broadcasts an event payload to all connected clients.
  • Payload is JSON text of the inserted row plus:
    • priority: true if category is MILITARY or TERRORIST
    • priority: false otherwise

Example pushed message:

{
  "id": 1001,
  "event_id": "CIQ-20260420-UKR-00001",
  "category": "MILITARY",
  "title": "Artillery strike near frontline",
  "event_time": "2026-04-20T09:45:00",
  "priority": true
}

Implementation Caveats (Current Code)

  • Route order issue: /api/v1/conflicts/{event_id} is declared before /api/v1/conflicts/clusters, so requests to /conflicts/clusters may be captured as event_id="clusters" depending on router matching behavior.
  • GET /api/v1/intel/theaters uses mode() ... ORDER BY weapon, but weapon is not present in conflict_events schema in db/init.sql, which can cause runtime SQL errors unless schema differs in production.
  • POST /api/v1/intel/articles currently uses plain function parameters (not an explicit JSON body model), so clients should send these fields as query/form parameters unless route code is updated.