Authentication

All API endpoints (except /health) require a Bearer JWT obtained via Google OAuth. Include it in every request.

Authorization Header
Authorization: Bearer <jwt_token>
POST /auth/google No auth

Authenticate via Google OAuth. Creates a new user account if one doesn't exist. Returns a JWT for subsequent requests.

Request Body
{ "google_token": "<Google ID token>", "user_info": { "company": "Acme Corp", "position": "Analyst", "use_case": "Monitoring geopolitical events", "heard_about": "Twitter" } }
ParameterTypeDescription
google_token*stringGoogle OAuth ID token
user_infoobjectRequired for new users (must include company and position). Ignored for returning users.
Response 200
{ "jwt": "eyJhbGciOi...", "user": { "user_id": "uuid-string", "email": "user@example.com", "name": "Jane Doe", "role": "user", "is_new": false } }
400Missing google_token or missing user_info for new users
401Invalid Google token
POST /auth/refresh Auth required

Refresh a valid (non-expired) JWT with a new expiry.

Response 200
{ "jwt": "eyJhbGciOi..." }

Entity Reference Format

Entity references follow the pattern {type}___{Name_With_Underscores} (three underscores as separator).

per
Person
per___Donald_Trump
org
Organization
org___United_Nations
priv
Private company
priv___SpaceX
stock
Publicly traded company
stock___Apple
cnt
Country
cnt___United_States
govactor
Government actor / agency
govactor___Department_of_Defense
grp
Group
grp___NATO
oth
Other
oth___Bitcoin

Error Handling

400Bad request — missing or invalid parameters
401Unauthorized — missing, invalid, or expired JWT
403Forbidden — insufficient role (non-admin accessing admin endpoints)
404Not found — requested resource doesn't exist
500Server error — unexpected failure
Standard Error Format
{ "error": "Description of the error" }
Some endpoints return {"success": false, "error": "..."} instead. See per-endpoint documentation.

Events

GET /api/event/{eventId} Auth required

Retrieve full event data including summary, bookkeeping, and deduplicated actions.

Response 200
{ "summary": { "EventTitle": "Trade War Escalation", "EventDescription": "...", "EventBroadCategory": "Economic", "GeneralSentiment": 25, "MarketAttention": 70, "MarketDirect": true, "Actions": [{ "Action": "...", "Importance": 85 }], "Entities": [{ "EntityReference": "per___...", "EventImportance": 90 }] }, "bookkeeping": { ... }, "error": "None" }
The error field contains the literal string "None" on success (not JSON null).
GET /api/event/{eventId}/details Auth required

Get a summarized view: top 5 actions, top 5 entities, description, and market impact. Lighter than the full event endpoint.

Response 200
{ "success": true, "eventId": "event_123", "eventTitle": "Trade War Escalation", "sentiment": 25, "attention": 70, "topActions": [{ "action": "...", "importance": 85 }], "topEntities": [{ "entityRef": "per___...", "displayName": "..." }] }
404Event not found

Entities

GET /api/loadEntityPage/{entityRef} Auth required

Retrieve entity data including relationships, actions, and events. POST variant allows passing relationship display settings in the request body.

Response 200
{ "displayName": "Donald Trump", "entityRef": "per___Donald_Trump", "bookkeeping": { ... }, "relationships": [ ... ], "actions": [ ... ], "events": [ ... ] }
GET /api/loadEntitySecondaryData/{entityRef} Auth required

Retrieve secondary data (Wikipedia summary, market fundamentals). Call after the main entity page loads for faster initial render. fundamentals is only populated for entities with stock tickers.

Response 200
{ "wikipedia": { "summary": "...", "url": "https://..." }, "fundamentals": { "marketCap": 1500000000, "sector": "Technology" } }
GET /api/getEntityLivePricesData/{entityRef} Auth required

Get live price data: 60-day hourly candles and 1-year daily candles.

Response 200
{ "ticker": "AAPL", "hourly": [{ "timestamp": 1713600000, "open": 150.25, "close": 151.50 }], "daily": [{ "timestamp": 1713600000, "open": 150.25, "close": 153.75 }] }
POST /api/entities/batch Auth required

Get current data for multiple entities in a single request.

Request Body
{ "entityRefs": ["per___Donald_Trump", "stock___Apple"] }
Response 200
{ "success": true, "entities": { "per___Donald_Trump": { "activeEvents": 15, "found": true }, "stock___Apple": { "found": false } } }
POST /api/entity-search Auth required

Search entities using FTS5 full-text search across names, aliases, and tickers. Results capped at 50.

Request Body
{ "query": "trump", "types": ["per", "org"] }
ParameterTypeDescription
query*stringSearch term
typesstring[]Filter by entity type prefix. Omit to search all types.
Response 200
{ "success": true, "results": [{ "name": "per___Donald_Trump", "displayName": "Donald Trump", "score": 25.3, "ticker": "DJT" }] }

Relationships

POST /api/entity-relationships-filtered/{entityRef} Auth required

Get filtered and sorted relationships for an entity. Primary endpoint for the relationship browser.

Request Body
{ "maxNumberOfRelsToDisplay": 50, "sortingMetric": "relImportanceMetric", "filters": { "counterpartyTypes": ["per", "priv"], "direction": [">"], "minImportance": 0.5 } }
ParameterTypeDescription
maxNumberOfRelsToDisplayintMax relationships to return. Default: 50
sortingMetricstring"relImportanceMetric" or "lastUpdateTime"
filters.counterpartyTypesstring[]Filter by counterparty entity type prefixes
filters.directionstring[]">" (outgoing) and/or "<" (incoming)
filters.minImportancefloatMinimum importance threshold
filters.maxImportancefloatMaximum importance threshold
GET /api/relationship/{entityA}/{entityB} Auth required

Get comprehensive relationship data between two entities, including actions, processed attributes, and timeseries sentiment data.

Response 200
{ "entityADisplayName": "Donald Trump", "entityBDisplayName": "Joe Biden", "importance": 8.5, "actions": [{ "Action": "...", "Importance": 85 }], "timeseries": [{ "datetime": "2026-01-15T00:00:00.000Z", "sentiment": 0.3 }] }
timeseries is null if no actions exist. Pass ?debug=true for verbose timeseries output.

Paths

Find chains of relationships connecting two entities, optionally through required waypoints.

POST /api/entity-paths Auth required
Request Body
{ "entity_a": "per___Donald_Trump", "entity_b": "per___Hillary_Clinton", "max_steps": 4, "firstNPaths": 3, "settings": { "entitiesInBetween": ["cnt___United_States"], "filters": { ... } } }
ParameterTypeDescription
entity_a*stringStart entity reference
entity_b*stringEnd entity reference
max_stepsintMax path length (1–10). Default: 4
firstNPathsintMax paths to return. -1 for all (up to 100). Default: 1
settings.entitiesInBetweenstring[]Waypoints the path must pass through
settings.filtersobjectSame filter options as relationship filtering
Response 200 — paths found
{ "success": true, "found": true, "num_paths": 2, "paths": [{ "path_number": 1, "length": 3, "entities_display": ["Donald Trump", "United States", "Hillary Clinton"], "type": "path" }] }
type is "path" for multi-hop paths or "relationship" for direct connections. segments only populated when waypoints are specified.
400Missing entity_a/entity_b, or max_steps/firstNPaths out of range

StreetQuery

Execute complex graph or chain queries. Define conditional graph patterns to find matching sets of entities. Supports "graph" (nodes with roles + edges) and "chain" (linear entity-relationship steps) query types.

POST /api/streetquery Auth required
Request Body — graph example
{ "type": "graph", "nodes": { "ANCHOR": { "role": "anchor", "anchorRef": "stock___Amazon" }, "CEOS": { "role": "result", "filter": { "types": ["per"] } } }, "edges": [{ "from": "ANCHOR", "to": "CEOS", "type": "relationship", "relationshipFilter": { "names": ["ceo"] } }], "output": { "returnNodes": ["CEOS"] } }
Response 200
{ "success": true, "nodes": { "CEOS": [{ "ref": "per___Andy_Jassy", "displayName": "Andy Jassy" }] }, "stats": { "executionTimeMs": 45 } }
400No query provided or invalid query structure

Feeds

Feeds and search share the same filter structure. All filter fields are optional.

Feed Configuration Object
{ "query": "optional text query", "entityConditions": [{ "entity": "per___Donald_Trump", "importance": "any" }], "categories": ["Political", "Economic"], "minSize": 5, "dateRange": { "start": 1710000000, "end": 1713600000 }, "sentimentRange": { "min": -100, "max": 100 }, "attentionRange": { "min": 0, "max": 100 } }
GET /api/initialize Auth required

Load initial dashboard data. Returns saved feed configurations and current feed data.

Response 200
{ "success": true, "savedFeeds": { "My Feed": { ... } }, "feedDatas": { "My Feed": { "events": [ ... ], "count": 42 } } }
POST /api/update-feeds Auth required

Update saved feed configurations and return refreshed feed data.

Request Body
{ "savedFeeds": { "My Feed": { ...feed_config } } }
GET /api/refresh-feeds Auth required

Refresh feed data using current saved configurations. No changes to feed settings.

Saved Items

All saved item endpoints are per-user. Items are stored as key references (no data snapshots) and enriched with live data on retrieval. No pagination — all saved items are returned.

Events
GET /api/saved-events Auth required

Get saved events with enriched data. If an event no longer exists, data.missing will be true.

POST /api/saved-events/{event_id} Auth required

Save an event.

Body
{ "notes": "optional" }
DELETE /api/saved-events/{event_id} Auth required

Remove a saved event.

404Event not in saved items
Entities
GET /api/saved-entities Auth required

Get saved entities with enriched metadata, types, and activity metrics.

POST /api/saved-entities/{entity_name} Auth required

Save an entity.

Body
{ "notes": "optional" }
DELETE /api/saved-entities/{entity_name} Auth required

Remove a saved entity.

Relationships
GET /api/saved-relationships Auth required

Get saved relationships with enriched importance, processing status, and action count. Keys are canonically sorted: entityA:::entityB (alphabetical).

POST /api/saved-relationships Auth required

Save a relationship.

Body
{ "entityA": "...", "entityB": "...", "notes": "" }
400Missing entityA or entityB
DELETE /api/saved-relationships/{entityA}/{entityB} Auth required

Remove a saved relationship. Entities must be in canonical (alphabetical) order — use the key from GET /api/saved-relationships.

404Not found (or wrong entity order)
Paths
GET /api/saved-paths Auth required

Get saved paths with enriched display names and computed metrics.

POST /api/saved-paths Auth required

Save a path. Minimum 2 entities required.

Body
{ "entities": ["ref1", "ref2"], "notes": "" }
400Fewer than 2 entities
DELETE /api/saved-paths/{path_id} Auth required

Remove a saved path. The path_id is entity1:::entity2:::entity3.

404Path not in saved items

User Settings

GET /api/user/settings Auth required

Get user's general settings (theme, layout preferences, etc.).

PUT /api/user/settings Auth required

Update settings. Merges with existing — send only the fields you want to change.

Request Body
{ "theme": "dark", "relmapLayout": "force" }
GET /api/user/actions-log Auth required

Get your action log with pagination. Returns most-recent-first.

Query ParamTypeDescription
limitintMax entries. Default: 100
offsetintSkip entries. Default: 0

Messages & Feedback

In-app messaging between users and admins.

User Endpoints
GET /api/messages/my Auth required

Get your full message history (chronological).

POST /api/messages/send Auth required

Send a feedback message. At least one of content or attachment required.

Request Body
{ "content": "Message text", "page_url": "{\"path\": \"/feeds\"}", "attachment": "<base64_or_url>" }
400Empty message (no content and no attachment)
GET /api/messages/unread-count Auth required

Get count of unread admin replies.

POST /api/messages/mark-read Auth required

Mark all admin replies as read.

Admin Endpoints
GET /api/messages/conversations Admin only

List all user conversations. unread_count reflects user messages the admin hasn't read.

GET /api/messages/conversation/{user_id} Admin only

Get full message thread for a user. Auto-marks user messages as read.

POST /api/messages/reply/{user_id} Admin only

Reply to a user.

Body
{ "content": "reply text" }
400Empty message
GET /api/messages/stats Admin only

Get messaging analytics: totals, top pages (10), top users (10), daily volume (30 days).

Analytics

All analytics endpoints require admin role. Data is based on user action logs from the last 30 days. Results are cached for 60 seconds.

GET /api/analytics/overview Admin only

Dashboard summary: total users, total actions, actions today/this week, and 5 most recently active users.

GET /api/analytics/users Admin only

Per-user breakdown: total actions, actions today, last active timestamp, and top 3 endpoints per user.

GET /api/analytics/user/{user_id} Admin only

Single user drilldown: daily counts (30 days), endpoint breakdown by category, and last 50 actions.

404User not found in action logs
GET /api/analytics/features Admin only

Feature/endpoint usage across all users. Top 20 endpoints returned, grouped by category.

GET /api/analytics/objects Admin only

Most-viewed objects (entities, events, relationships) across all users. Top 30 per category. Pass ?user_id=... to filter to a specific user.

Health & Status

GET /health No auth

Health check.

Response 200
{ "status": "healthy", "service": "unified-backend" }
GET /api/data-status Auth required

Get current database status with live counts. last_update is "never" if no events exist.

Response 200
{ "last_update": "2026-04-15T10:30:00", "events_count": 15000, "entities_count": 8500 }