API Reference
The Ergen REST API provides programmatic access to events, entities, relationships, and paths. All requests and responses use JSON.
Authentication
Every endpoint (except /health and the auth routes themselves) requires a Bearer JWT. Ergen uses passwordless email one-time-password (OTP) authentication — there is no password and no Google sign-in. Access is invite-based.
- Submit an access request (
POST /api/access-request). - An admin approves the account.
- Request a login code (
POST /auth/request-otp). - Verify it for a JWT (
POST /auth/verify-otp). - Refresh the JWT before it expires (
POST /auth/refresh).
Request access to Ergen. Creates a pending account that an admin must approve before login is possible. This is the only sign-up path.
| Parameter | Type | Description |
|---|---|---|
| name* | string | Full name (max 300 chars) |
| email* | string | Work email (max 300 chars) |
| company* | string | Company (max 300 chars) |
| position* | string | Role / title (max 300 chars) |
| use_case | string | Optional. Intended use (max 4000 chars) |
| heard_about | string | Optional. How you heard about us (max 4000 chars) |
already_pending (awaiting approval) or already_approved (account active — log in instead)Email a one-time login code to an approved account. The code expires after expires_in seconds; a new code can be requested again after resend_in seconds.
invalid_email — a valid email is requiredpending — account is awaiting admin approvalnot_found — no account; submit an access request firstcooldown — requested again too soon (see retry_after)send_failed — the email could not be deliveredVerify a login code and receive a JWT plus the user profile. Put the returned jwt in the Authorization header on every subsequent request.
invalid — wrong or missing code (may include attempts_left)pending — account is awaiting admin approvalnot_found — no account; submit an access request firstexpired — code expired; request a new onetoo_many_attempts — too many wrong codesExchange a valid (non-expired) JWT for a fresh one. Works for pending accounts too, so a client can keep a session warm while awaiting approval.
Entity Reference Format
Entity references follow the pattern {type}___{Name_With_Underscores} (three underscores as separator).
Error Handling
Retry-After header){"success": false, "error": "..."} instead. See per-endpoint documentation.Events
Retrieve full event data including summary, bookkeeping, and deduplicated actions.
| Query Param | Type | Description |
|---|---|---|
| include_archived | bool | Default: false. Include archived event data (only takes effect for callers with archive access — tier 2+/admin). |
error field contains the literal string "None" on success (not JSON null). There is no success key on this endpoint.Get a summarized view: top 5 actions, top 5 entities, description, and market impact. Lighter than the full event endpoint.
| Query Param | Type | Description |
|---|---|---|
| unprocessedEventsIncluded | bool | Default: false. If the event has no processed summary, fall back to placeholder title/description from its centroid article. |
Find events semantically similar to a given event, ranked by embedding similarity.
| Query Param | Type | Description |
|---|---|---|
| limit | int | Max results. Default: 20 |
| min_score | float | Minimum similarity (0–1). Default: 0.4 |
Get the deduplicated actions for an event, sorted by importance.
| Query Param | Type | Description |
|---|---|---|
| limit | int | Max actions. Default: 50 |
| include_archived | bool | Include actions from archived sources. Default: true — for callers with archive access (tier 2+/admin), archived actions are returned unless explicitly disabled. |
Fetch multiple events in one request. The events object is keyed by event id; ids that don't resolve are listed in missing.
Get recent events that mention an entity, most-recent first.
| Query Param | Type | Description |
|---|---|---|
| limit | int | Max events. Default: 50 |
| include_inactive | bool | Include deactivated (merged) events. Default: false |
| include_archived | bool | Include archived events. Default: false |
Free-text semantic search over events using embedding similarity. Lighter than /api/search — no feed filters, just a query.
Fetch the raw article text for one or more article ids. The articles object is keyed by article id.
Get the actions that directly connect two entities.
Semantic search across actions (atomic interactions) rather than whole events.
Search the historical (archived) event store. Requires a tier-2 or higher account.
Entities
Retrieve entity data including relationships and actions. POST variant allows passing relationship display settings in the request body. Note several keys carry a literal .json suffix.
Same as the GET variant, but accepts custom relationship display settings in the body (same shape as /api/entity-relationships-filtered). Use it to control how the entity's relationships are filtered and sorted on load.
Retrieve secondary data (Wikipedia summary and market data). Call after the main entity page loads for a faster initial render. market is only populated for entities with a stock ticker.
Get live price data for an entity with a stock ticker: 60-day hourly candles and 1-year daily candles. Both arrays are empty for entities without a ticker.
Get current data for multiple entities in a single request.
Search entities using FTS5 full-text search across names, aliases, and tickers. Page size (limit) capped at 100.
| Parameter | Type | Description |
|---|---|---|
| query* | string | Search term |
| types | string[] | Filter by entity type prefix. Omit to search all types. |
| Query Param | Type | Description |
|---|---|---|
| limit | int | Page size. Default: 20 |
| offset | int | Skip results for paging. Default: 0 |
Get the actions an entity participates in, across all events, sorted by importance.
| Query Param | Type | Description |
|---|---|---|
| role | string | "both" (default), "source", or "target" |
| limit | int | Max actions. Default: 50 |
| include_stagnated | bool | Include stagnated actions. Default: true |
| include_archived | bool | Include archived sources. Default: false. Requires archive access (tier 2+/admin); ignored (treated as false) for lower tiers. |
Co-mention metrics for two entities: how often they appear together in event clusters and the combined importance of those mentions.
{"success": false, "error": "entity_a and entity_b are required"} when either is missing/blankRelationships
Get filtered and sorted relationships for an entity. Primary endpoint for the relationship browser.
| Parameter | Type | Description |
|---|---|---|
| limit | int | Max relationships to return. Default: 50 (range 1–500) |
| sort_by | string | "gate" (default), "n_co_events", "evidence", or "recency" |
| offset | int | Skip results for paging. Default: 0 |
| include_both_orientations | bool | Used only when filters.direction is absent: true → both, false → outgoing only |
| filters.direction | string[] | "outgoing" and/or "incoming" |
| filters.processed_only | bool | Only LLM-processed relationships |
| filters.min_co_events | int | Minimum shared-event count |
| filters.min_gate | float | Minimum gate score |
| filters.min_evidence | float | Minimum evidence score |
| filters.pair_types | string[] | Filter by pair type (e.g. per_cnt) |
| filters.counterparty_types | string[] | Filter by counterparty entity type prefixes |
| Query Param | Type | Description |
|---|---|---|
| limit | int | Page size. Overrides the body's limit if present. |
| offset | int | Skip results for paging. Default: 0 |
| sort_by | string | Sort key. Overrides the body's sort_by if present. |
limit/offset formatGet comprehensive relationship data between two entities, including actions, processed attributes, and timeseries sentiment data.
timeseries is null if no actions exist. Pass ?debug=true for verbose timeseries output.Find relationships involving a single entity (on either side), filtered and sorted. Powers the search page's relationship tab.
filters wrapper) and use snake_case; the key is entity_ref, not entityRef.Get the qualitative-attribute schema for a relationship pair-type (e.g. the set of processed attributes valid for a given pair of entity types). Useful for building relationship filters.
pair_type (response lists the valid values)Paths
Find chains of relationships connecting two entities, optionally through required waypoints.
| Parameter | Type | Description |
|---|---|---|
| entity_a* | string | Start entity reference |
| entity_b* | string | End entity reference |
| max_steps | int | Max path length (1–10). Default: 4 |
| firstNPaths | int | Max paths to return. -1 for all (up to 100). Default: 3 |
| settings.entitiesInBetween | string[] | Waypoints the path must pass through |
| settings.filters | object | Same filter options as relationship filtering |
type is "path" for multi-hop paths or "relationship" for direct connections. segments only populated when waypoints are specified.entity_a/entity_b, or max_steps/firstNPaths out of rangeStreetQuery
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.
{"error","window","used","cap","resets_at"}Feeds
Feeds and search share the same filter structure. All filter fields are optional.
Load initial dashboard data. Returns saved feed configurations and current feed data.
| Query Param | Type | Description |
|---|---|---|
| per_feed_limit | int | Max events per feed in the initial payload |
| unprocessedEventsIncluded | bool | Include events without a generated summary. Default: false |
Replace saved feed configurations and return refreshed feed data.
| Parameter | Type | Description |
|---|---|---|
| savedFeeds* | object | The full set of feed configs to save |
| per_feed_limit | int | Max events returned per feed. Default: 20 (clamped 1–200) |
| Query Param | Type | Description |
|---|---|---|
| unprocessedEventsIncluded | bool | Include events without a generated summary. Default: false |
{"savedFeeds": {}}) clears all feeds. Sending no body at all returns 500 and leaves your saved feeds untouched.Refresh feed data using current saved configurations (no changes to feed settings). Pass feed_name to refresh a single feed, with per_feed_limit/offset for paging through its events.
| Query Param | Type | Description |
|---|---|---|
| feed_name | string | Refresh only this feed. Omit to refresh all. |
| per_feed_limit | int | Max events per feed. Default: 20 |
| offset | int | Skip events within the feed for paging. Default: 0 |
| unprocessedEventsIncluded | bool | Include events without a generated summary. Default: false |
feed_name set){"success": false, "error": "Feed not found"} when feed_name isn't one of your saved feedsSearch
Search for events using the feed configuration object. Results are returned most-recently-updated first. When a text query is provided, each event gets a similarityScore (a title string-match ratio, not a semantic-embedding score) but ordering stays by recency. Capped at 100 events.
| Query Param | Type | Description |
|---|---|---|
| limit | int | Page size. Default: 20 |
| offset | int | Skip results for paging. Default: 0 |
| unprocessedEventsIncluded | bool | Include events without a generated summary. Default: false |
?limit=abc) silently fall back to the default rather than erroring.Agent & Chat
The agent is a tool-using AI assistant over your Ergen data. Chat is a streaming (SSE) endpoint and is token-metered — usage is charged per turn against your tier's window rather than a flat call quota. Conversations are persisted per user.
Send a message and stream the assistant's reply as Server-Sent Events. Pass conv_id to continue an existing conversation, or omit it to start a new one (the first event returns the new id).
message is an object ({content, mentions}), not a string. mentions is an optional array. Alternatively send a legacy messages array of {role, content, mentions} when message is omitted.{"error": "No messages provided"} when neither message nor a non-empty messages array is sent{"error","window","used","cap","resets_at"} with a Retry-After header (admins are exempt)List the models selectable for agent chat and the default.
List your conversations (pinned first, then most-recently updated).
Create or update (upsert) a conversation. Omit id to create a new one.
{"error": "No messages"} when messages is empty/absentGet a single conversation including its full message list.
Auto-generate a short title from the first exchange.
{"error": "Not found"} — conversation doesn't existRename a conversation.
{"error": "No title"} when title is blank{"error": "Not found"} — conversation doesn't existToggle the pinned state. Returns the new value.
{"error": "Not found"} — conversation doesn't existDelete a conversation.
Routines
Routines are scheduled agent prompts — they run on a trigger (once, hourly, daily, weekdays, weekly, or a custom interval) and record each execution as a run. They can also be triggered on demand, synchronously or as an SSE stream.
List your routines.
Create a routine. Returns the created object.
| trigger.type | Required fields |
|---|---|
| once | trigger_at (ISO datetime) |
| hourly | trigger_minute |
| daily / weekdays | trigger_time |
| weekly | trigger_time, trigger_weekday |
| custom | interval_minutes |
name/prompt or invalid triggerGet one routine plus its recent runs.
Update a routine. Send only the fields you want to change.
model_id or trigger type{"error": "Not found"} — routine doesn't existDelete a routine.
{"error": "Not found"} — routine doesn't existEnable/disable a routine (flips enabled). Returns the updated object.
{"error": "Not found"} — routine doesn't existRun a routine immediately and wait for the result (synchronous).
Run a routine immediately and stream progress as Server-Sent Events.
{"error": "Not found"} — routine doesn't exist (returned before the stream begins)Get a routine's run history, most-recent first.
| Query Param | Type | Description |
|---|---|---|
| limit | int | Max runs. Default: 50 (clamped to 200) |
{"error": "Not found"} — routine doesn't existSaved 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.
Get saved events with enriched data. If an event no longer exists, data.missing will be true.
| Query Param | Type | Description |
|---|---|---|
| unprocessedEventsIncluded | bool | Include placeholder/unprocessed saved events. Default: false |
Save an event.
Remove a saved event.
Get saved entities with enriched metadata, types, and activity metrics. If an entity no longer exists, data.missing will be true.
Save an entity.
Remove a saved entity.
Get saved relationships with enriched gate score, processing status, and action count. Keys are canonically sorted: entityA:::entityB (alphabetical). Note the per-item fields use snake_case.
Save a relationship.
entityA or entityBRemove a saved relationship. Entities must be in canonical (alphabetical) order — use the key from GET /api/saved-relationships.
Get saved paths with enriched display names and computed metrics. Per-item fields use snake_case.
Save a path. Minimum 2 entities required.
Remove a saved path. The path_id is entity1:::entity2:::entity3.
User Settings
Get user's general settings (theme, layout preferences, etc.). settings is a free-form preferences object — empty {} when none saved.
Update settings. Merges with existing — send only the fields you want to change.
Get your action log with pagination. Returns most-recent-first.
| Query Param | Type | Description |
|---|---|---|
| limit | int | Max entries. Default: 100 |
| offset | int | Skip entries. Default: 0 |
Messages & Feedback
In-app messaging between users and admins.
Get your full message history (chronological).
Send a feedback message. At least one of content or attachment required.
Get count of unread admin replies.
Mark all admin replies as read.
List all user conversations. unread_count reflects user messages the admin hasn't read.
Get full message thread for a user. Auto-marks user messages as read.
Reply to a user.
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.
Dashboard summary: total users, total actions, actions today/this week, and 5 most recently active users.
Per-user breakdown: total actions, actions today, last active timestamp, and top 3 endpoints per user.
Single user drilldown: daily counts (30 days), endpoint breakdown by category, and last 50 actions.
Feature/endpoint usage across all users. Top 20 endpoints returned, grouped by category.
Most-viewed objects (entities, events, relationships) across all users. Top 30 per category. Pass ?user_id=... to filter to a specific user.
User Management
Admin-only endpoints for reviewing accounts and approving access requests by setting a user's tier.
List all users. Pass ?pending=true to show only accounts awaiting approval.
| Query Param | Type | Description |
|---|---|---|
| pending | bool | 1/true/yes shows only pending accounts |
Set a user's tier (and optionally role). Promoting a pending account out of the pending tier approves it.
Inspect another user's tier and current usage windows (see GET /api/usage for the shape).
Health & Status
Health check.
Get current database status with live counts. last_update is "never" if no events exist.
Aggregate database statistics.
Your account's tier and current usage windows. unlimited is true for admins (no caps); for metered tiers, session and weekly report the cap, amount used, and reset time.