# Analytics Source: https://docs.linqalpha.com/api-reference/basic/analytics-sse POST /v1/analytics/sse Generate an agentic analytics response via Server-Sent Events. This endpoint leverages multi-step reasoning with tool use to deliver deeper, more comprehensive answers compared to the standard search & generate endpoints. The response stream includes message echo, thinking processes, tool calls with results, and the final answer. **Note:** Unlike `/v1/search` or `/v2/chat/sse`, this endpoint does not accept separate filter parameters (e.g., `stock_ids`, `tickers`, `external_types`, `upload_period`). Instead, include all filtering context directly in the `query` field. For example: `"Analyze AAPL earnings trend from Q1 2024 to Q4 2025"`. ### Event Flow ``` conversation -> conversation_id for this session message -> echo of user query status: start -> stream begins +-- AGENTIC LOOP (repeats) ----------------------------+ | tool_use_block -> 1-N parallel tool calls (batch)| | tool_result_block -> results for each call | | status: keep_alive -> heartbeat (if >15s gap) | | think (x N) -> reasoning tokens | +------------------------------------------------------+ think (x N) -> extended reasoning / draft answer -> single chunk final answer status: finish -> stream ends ``` # Analytics V2 Source: https://docs.linqalpha.com/api-reference/basic/analytics-sse-v2 POST /v2/analytics/sse Agentic analytics with structured source selection, hard/soft filters, and identity fields. - **`search_types`**: Choose sources — `rms` (internal docs), `external` (transcripts, filings), `structured` - **`filters.rms.hard_filters` / `soft_filters`**: Hard = exclude non-matching (AND between fields, OR within arrays). Soft = boost relevance only. - `organization_id` and `user_id`: Optional (required only for platform API keys) ## Event Flow ``` conversation ← conversation_id message ← echo of user query status: start ← stream begins +-- AGENTIC LOOP (repeats) ---------+ | tool_use_block (x1-3) | | tool_result_block (x1-3) | | keepalive (~15s) | | think (xN) | +------------------------------------+ answer ← full answer with [1][2] citations status: finish ← stream ends ``` ## Retrieving References After the stream finishes, use `conversation_id` to get citation sources: ``` GET /v2/analytics/conversations/{conversation_id}/references ``` Returns normalized references with enriched metadata. See [Analytics V2 References](/api-reference/basic/analytics_references) for details. ## Referencing Vault documents To chat over documents you uploaded through the Vault endpoints, pass their `rms_document_id` (returned by [Vault — Confirm Upload](/api-reference/vault/confirm)) in `search_types.rms[].document_ids`, and **set `source` to `"vault"`**: ```json theme={null} { "organization_id": "", "query": "Summarize the Q3 figures in the uploaded report.", "search_types": { "rms": [ { "workspace": "personal", "source": "vault", "document_ids": [""] } ] } } ``` `source: "vault"` is **required** for Vault documents. If you omit it, the document is searched generically — it is not retrieved for direct file citation, the answer's `[N]` markers won't resolve, and `GET /v2/analytics/conversations/{id}/references` returns an empty list (the answer may still look complete, so this fails silently). * `document_ids` are **`rms_document_id`** values from `confirm` (poll [Document Status](/api-reference/vault/status) until `Synced` first) — not the presign `document_id`. * `workspace` must match the workspace the document was uploaded into (the `workspace` sent to `presigned_url` / `confirm`). ## Selecting a model Pass `workspace_model` to run a request on a specific model: ```json theme={null} { "organization_id": "", "query": "Summarize BNP Paribas' latest credit profile.", "workspace_model": "claude-opus-4-7" } ``` Omit it and the model configured for your organization is used — the default for almost all requests. Reach for it when a particular query needs more (or less) capability than your default. The model that actually ran is reported on the `workspace_model` SSE event, so you can always confirm which one served the request: ``` data: {"event_name": "workspace_model", "data": {"workspace_model": "claude-opus-4-7[1m]"}} ``` The reported id carries a `[1m]` suffix marking the 1M-context runtime the model ran under. It is appended to every Claude model, so the reported value will not string-match a bare id you sent. Both forms are accepted on the way in (`claude-opus-4-7` and `claude-opus-4-7[1m]` are equivalent), so you can send back whatever you read. To compare, strip the suffix on either side. If your organization has an enforced model policy, it takes precedence over `workspace_model` and your requested value is ignored. This is not an error — the request succeeds on the policy's model. Read the `workspace_model` SSE event to see what ran. An unrecognized or malformed model id is rejected with `400` before the stream opens, so a typo fails fast rather than silently running on a different model. The permitted set is a fixed list, not every Claude model that exists. Currently `claude-opus-4-7`, `claude-opus-4-8` and `claude-opus-5` (with or without the `[1m]` suffix). Requesting anything else returns `400` with `workspace_model is not permitted in this environment`. Ask us if you need a model that is not on the list. # Analytics V2 Judge Source: https://docs.linqalpha.com/api-reference/basic/analytics_judge POST /v2/analytics/messages/{chat_message_id}/judge Runs a source-grounding judge over one Analytics answer. You supply the answer's `chat_message_id` plus your own judging `prompt` (and an optional `response_schema`); we resolve the original prompt, the generated answer, and every reference the answer cited, run a tool-less GPT-4.1 over them, and return a verdict shaped by your schema. **Usage flow:** 1. Call `POST /v2/analytics/sse`; while consuming the stream, capture the `chat_message_id` event. 2. POST that id here with your `prompt` — your prompt is the entire judging instruction; we add no criteria of our own — and an optional `response_schema`. 3. The verdict scores how well each claim is grounded in the sources the answer actually cited (a number that contradicts its source, or is absent from all sources, is flagged as not grounded). Tenant-scoped: you can only judge answers generated by your own organization; others return 404. ## What it does The judge takes an answer you already generated and returns a structured verdict on how well each claim is backed by the sources that answer cited. For every cited source it reads the verbatim quoted text plus its metadata (document, type, tickers, dates, publisher, and FactSet fields), then checks each claim against that source. A figure that contradicts its source, or that appears in no cited source, is flagged. The check is deterministic (`temperature` 0); your `prompt` is the entire judging instruction and your `response_schema` defines the verdict shape, so we add no criteria of our own. ## Getting the `chat_message_id` As you consume the [Analytics V2 SSE stream](/api-reference/basic/analytics-sse-v2) (`POST /v2/analytics/sse`), the id arrives as its own event: ``` data: {"event_name": "chat_message_id", "data": {"chat_message_id": "5b37f46c-f5de-4b8a-af09-5058dd4779b3"}} ``` Capture the event whose `event_name == "chat_message_id"` and keep `data.chat_message_id`. ## Example ```python theme={null} import requests resp = requests.post( "https://api.linqalpha.com/v2/analytics/messages/5b37f46c-f5de-4b8a-af09-5058dd4779b3/judge", headers={"X-API-KEY": "", "Content-Type": "application/json"}, json={ # Your judge prompt, used verbatim as the judge's instruction. "prompt": ( "For each section of the answer, return: (1) a score from 0 to 10 reflecting how well its " "claims are grounded in the cited sources [N], (2) a concise summary, and (3) a short report " "covering Strengths, Weaknesses, and any Contradicted or unverifiable facts. Any figure or " "rating absent from the cited sources must appear under Contradicted or unverifiable." ), # Optional. Any valid JSON Schema (dynamic-key maps supported); omit for the default verdict shape. "response_schema": { "type": "object", "properties": { "section_scores": {"type": "object", "additionalProperties": {"type": "number"}}, "section_content_summaries": {"type": "object", "additionalProperties": {"type": "string"}}, "report": {"type": "string"}, }, "required": ["section_scores", "section_content_summaries", "report"], }, }, timeout=160, ) resp.raise_for_status() payload = resp.json()["payload"] print(payload["verdict"], payload["judge_model"], payload["references_evaluated"]) ``` You can only judge answers generated by your own organization; judging another org's answer returns `404`. The call is a single long-running LLM request, so allow a generous client timeout (\~160s). # Analytics V2 References Source: https://docs.linqalpha.com/api-reference/basic/analytics_references GET /v2/analytics/conversations/{conversation_id}/references Retrieves the list of evidence references (citations) used in an analytics conversation, in normalized format with enriched metadata. > **Note:** This endpoint returns references from the Analytics V2 SSE endpoint. Some source types (e.g., `rms`) require a separate onboarding process. For more information, please contact us at support@linqalpha.com. **Usage Flow:** 1. Call the Analytics SSE V2 endpoint (`POST /v2/analytics/sse`) 2. From the SSE stream, find the `conversation` event → extract `conversation_id` 3. After the stream finishes, call this endpoint with the `conversation_id` 4. The response contains all references with citation index, source document info, and metadata **How to view original documents:** - **Via Viewer:** `https://chat.linqalpha.com/rms/viewer?conversation_id={conversation_id}&citation_idx={citation_idx}` **Note:** This endpoint returns references in the same normalized format as the v1 references API, with enriched metadata fields (s3_file_key, external_url, calendar_date, fiscal_year, etc.). Use this endpoint when consuming the v2 analytics SSE stream. # Batch Presigned URLs Source: https://docs.linqalpha.com/api-reference/basic/batch_presigned_urls GET /v1/documents/batch_presigned_urls Generates temporary presigned URLs for multiple documents in a single request. Filter documents by ticker, document type, and date ranges. Returns paginated results with presigned URLs and document metadata. This is useful when you need to download multiple source documents at once, rather than calling the single-document presigned URL endpoint repeatedly. Generate temporary presigned URLs for multiple documents in a single request. This is more efficient than calling the single-document endpoint repeatedly. ## Quick Start ```bash Basic Usage theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v1/documents/batch_presigned_urls?tickers=AAPL&per_page=20' \ --header 'X-API-KEY: ' ``` ```bash Filter by Date Range theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v1/documents/batch_presigned_urls?tickers=AAPL&calendar_period[start_time][year]=2025&calendar_period[start_time][month]=1&calendar_period[end_time][year]=2025&calendar_period[end_time][month]=12' \ --header 'X-API-KEY: ' ``` ```bash Multiple Tickers and Document Types theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v1/documents/batch_presigned_urls?tickers=AAPL&tickers=MSFT&doc_sub_type=10-K&doc_sub_type=10-Q&per_page=50' \ --header 'X-API-KEY: ' ``` Presigned URLs expire after approximately 1 hour. Generate new URLs when needed. # Batch Presigned URLs V2 Source: https://docs.linqalpha.com/api-reference/basic/batch_presigned_urls_v2 GET /v2/documents/presigned_urls Generates temporary presigned URLs for multiple documents. Supports both external documents and RMS documents via the `search_type` parameter. **Two modes:** - **By document IDs** (RMS only): Pass `document_ids` to get presigned URLs for specific documents from search/analytics references. - **By filters**: Pass `tickers`, `doc_type`, etc. to browse and get presigned URLs. For platform API keys, pass `organization_id` to specify which org to query. Generate temporary presigned URLs for multiple documents in a single request. Supports both external documents (filings, transcripts, news) and RMS documents, controlled by the `search_type` parameter. ## Search Type | search\_type | Source | Supported filters | | -------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `external` (default) | External documents (filings, transcripts, IR slides, news) | `tickers`, `stock_ids`, `doc_type`, `doc_sub_type`, `fiscal_period`, `calendar_period` | | `rms` | RMS documents | `tickers`, `stock_ids`, `doc_type`, `doc_sub_type`, `fiscal_period`, `calendar_period`, `document_ids` | `document_ids` is RMS only — external does not support batch by IDs. The valid values for `doc_type` and `doc_sub_type` differ by search\_type and by organization. Refer to your organization's specific documentation for available values. ## Quick Start ```bash External documents by ticker (default) theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v2/documents/presigned_urls?tickers=AAPL&doc_type=earnings_call&per_page=20' \ --header 'X-API-KEY: ' ``` ```bash RMS documents by ticker theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v2/documents/presigned_urls?search_type=rms&organization_id=&tickers=MSFT&per_page=20' \ --header 'X-API-KEY: ' ``` ```bash RMS documents by IDs (from references) theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v2/documents/presigned_urls?search_type=rms&organization_id=&document_ids=&document_ids=' \ --header 'X-API-KEY: ' ``` ## Minimum Filter Requirements * **external**: at least one of `tickers` or `stock_ids` * **rms**: at least one of `tickers`, `stock_ids`, or `document_ids` For platform API keys that access multiple organizations, pass `organization_id` to specify which org to query. If omitted, defaults to the API key's primary organization. Presigned URLs expire after approximately 1 hour. Generate new URLs when needed. # Chat Message Source: https://docs.linqalpha.com/api-reference/basic/chat_message GET /v1/chat_messages/{chat_message_id} Retrieves a specific chat message by its ID. **How to find chat_message_id:** Look for the event with `event_name` set to `search_results` in the Chat API response data. For detailed instructions, see [search_results event response](https://docs.linqalpha.com/api-reference/basic/chat_v2#option-6). # Chat Source: https://docs.linqalpha.com/api-reference/basic/chat_v2 POST /v2/chat/sse Generate a response from the LinqAlpha engine. LinqAlpha uses different types of language models and data sources to generate an answer. The response is provided as a server-sent events (SSE) stream. **Note**: For this search chat endpoint, the stock_id (BBG_ID) can be obtained by referring to the [Map Tickers API](https://docs.linqalpha.com/api-reference/basic/map_tickers) and using the `stock_id` value from its response. # Customer Connectors Source: https://docs.linqalpha.com/api-reference/basic/connectors GET /v2/connectors Lists customer MCP connectors for the authenticated organization. Query parameters are used to filter the results. Customer connectors let an organization register and manage its own MCP connector definitions. Requests are authenticated with your API key and scoped to your organization. Connector credentials are write-only. Do not expect credential values to be returned by list or detail endpoints. # Conversation Feedback Source: https://docs.linqalpha.com/api-reference/basic/conversation_feedback POST /v1/conversations/{conversation_id}/feedback Submits feedback for a specific conversation. Use this endpoint to collect user ratings and comments about the quality of AI responses. Feedback helps improve the service and can be used for quality monitoring. **Rating Options:** - `up`: Positive feedback indicating a helpful response - `down`: Negative feedback indicating an unsatisfactory response **Optional Fields:** - `message_id`: Target specific message within the conversation - `comment`: Additional text feedback from the user # Delete Conversation Feedback Source: https://docs.linqalpha.com/api-reference/basic/delete_conversation_feedback DELETE /v1/conversations/{conversation_id}/feedback Deletes feedback for a specific conversation. If `message_id` is not provided, the feedback from the last message in the conversation will be deleted. # Map Tickers Source: https://docs.linqalpha.com/api-reference/basic/map_tickers GET /v1/map_tickers Accepts a list of ticker strings via query parameters and returns their corresponding stock IDs (BBG IDs). # LinqAlpha MCP Source: https://docs.linqalpha.com/api-reference/basic/mcp POST /v1/mcp LinqAlpha MCP gives AI assistants direct access to institutional-grade financial data for fundamental research. Through the Model Context Protocol (MCP), your AI can query company fundamentals, earnings estimates, stock prices, economic indicators, SEC filings, and earnings transcripts — all from a single endpoint. Supports JSON-RPC 2.0 protocol. Available methods: - `initialize` — Initialize MCP session - `ping` — Health check - `tools/list` — List available financial data tools - `tools/call` — Execute a financial data tool **Setup (Claude Desktop):** ```json { "mcpServers": { "linqalpha": { "url": "https://api.linqalpha.com/v1/mcp", "headers": { "x-api-key": "" } } } } ``` ## Overview LinqAlpha MCP gives AI assistants direct access to institutional-grade financial data for fundamental research. Through the [Model Context Protocol](https://modelcontextprotocol.io), your AI (Claude, Cursor, etc.) can query company fundamentals, earnings estimates, stock prices, economic indicators, SEC filings, and earnings transcripts — all from a single endpoint. ## Quick Start If you already have an existing API key, you must request a new key with MCP permissions enabled. Existing keys issued before the MCP endpoint release do not have MCP access. Contact [support@linqalpha.com](mailto:support@linqalpha.com) to request a new key. Add the following to your MCP client configuration: ```json Claude Desktop theme={null} { "mcpServers": { "linqalpha": { "url": "https://api.linqalpha.com/v1/mcp", "headers": { "x-api-key": "" } } } } ``` ```bash Claude Code theme={null} claude mcp add linqalpha \ --transport http \ --url https://api.linqalpha.com/v1/mcp \ --header "x-api-key: " ``` ```json Cursor theme={null} { "mcpServers": { "linqalpha": { "url": "https://api.linqalpha.com/v1/mcp", "headers": { "x-api-key": "" } } } } ``` ## Use via Completion API You can use LinqAlpha MCP tools directly from any LLM completion API (OpenAI, Anthropic, etc.) by making standard HTTP requests to our MCP endpoint. This lets you integrate LinqAlpha's financial data tools into your own AI workflows and applications. ### Step 1: List Available Tools First, fetch the tool definitions to get their names and input schemas: ```python Python theme={null} import requests MCP_URL = "https://api.linqalpha.com/v1/mcp" HEADERS = { "Content-Type": "application/json", "x-api-key": "" } # List all available tools response = requests.post(MCP_URL, headers=HEADERS, json={ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }) tools = response.json()["result"]["tools"] ``` ### Step 2: Convert to OpenAI Tool Format Transform MCP tool definitions into the format expected by OpenAI's API: ```python Python theme={null} def mcp_to_openai_tools(mcp_tools): """Convert MCP tool definitions to OpenAI function calling format.""" openai_tools = [] for tool in mcp_tools: openai_tools.append({ "type": "function", "function": { "name": tool["name"], "description": tool["description"], "parameters": tool["inputSchema"] } }) return openai_tools openai_tools = mcp_to_openai_tools(tools) ``` ### Step 3: Chat with Tool Calling Use the converted tools in your OpenAI completion request, then execute any tool calls against LinqAlpha MCP: ```python Python theme={null} from openai import OpenAI import json client = OpenAI() messages = [ {"role": "user", "content": "What was NVIDIA's revenue for the last 4 quarters?"} ] # 1. Send request with tools response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=openai_tools, ) message = response.choices[0].message # 2. If the model calls a tool, execute it via LinqAlpha MCP if message.tool_calls: messages.append(message) for tool_call in message.tool_calls: # Execute tool call against LinqAlpha MCP mcp_response = requests.post(MCP_URL, headers=HEADERS, json={ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": tool_call.function.name, "arguments": json.loads(tool_call.function.arguments) } }) tool_result = mcp_response.json()["result"]["content"][0]["text"] messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": tool_result }) # 3. Get final response with tool results final_response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=openai_tools, ) print(final_response.choices[0].message.content) ``` This pattern works with any LLM that supports function/tool calling — including Anthropic Claude API, Google Gemini, and open-source models. Just adapt the tool format conversion for your provider. ## Available Tools LinqAlpha exposes **20 financial data tools** organized by category. ### Fundamentals & Estimates | Tool | Description | | ------------------------- | ------------------------------------------------------------------------------ | | `fundamentals_data_query` | Query financial datasets including fundamentals, estimates, and ownership data | | `fundamentals_data_docs` | Browse financial data schema documentation and available fields | | `stock_prices` | Get historical and real-time stock price data | ### Economic Data | Tool | Description | | ------------------------- | ------------------------------------------------------------------------------- | | `economic_data_query` | Query macroeconomic data (GDP, CPI, employment, Treasury yields, VIX, and more) | | `economic_indicators` | List available economic indicators grouped by category | | `economic_indicator_data` | Get economic indicator time series data (GDP, CPI, unemployment) | | `economic_calendar` | Get upcoming economic data release schedule | ### Market Data | Tool | Description | | --------------------- | ----------------------------------------------------------------------------- | | `forex_rates` | Get foreign exchange currency pair rates and history | | `commodity_prices` | Get commodity market data (energy, metals, grains, softs) | | `treasury_rates` | Get US Treasury yield curve data across all maturities (1M-30Y) | | `market_symbols` | List available market symbols for forex, commodities, and economic indicators | | `market_risk_premium` | Get country-level market risk premium for CAPM calculations | ### Equity & Search | Tool | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `equity_database_query` | Query equity market database (stocks, events, documents) | | `transcript_search` | Full-text search across earnings call transcripts and SEC filings. Requires a Manticore SQL query in the `sql` parameter (not natural language). Call `read_guide('data/manticore')` for the SQL dialect and schema | ### Research & Citations | Tool | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cite_filing_source` | Create a citation for a filing/transcript excerpt found via `transcript_search`. Requires `chat_session_id` (from `create_research_session`) and `chunk_id` (the `id` field of a `transcript_search` result). This tool does not search — run `transcript_search` first | | `cite_web_source` | Create a web-based research citation | | `create_research_session` | Create a new research session for citation tracking | | `get_citations` | Retrieve all citations created in the current research session | | `web_search` | Search the web with date range filtering for recent financial news and analysis | ### Platform | Tool | Description | | ------------------ | ------------------------------------------------------ | | `my_platform_data` | Query your organization's LinqAlpha platform data | | `list_guides` | List available financial data guides and documentation | | `read_guide` | Read a specific financial data guide | ## Protocol This endpoint implements [JSON-RPC 2.0](https://www.jsonrpc.org/specification) over HTTP, following the MCP specification. ### Supported Methods | Method | Description | | --------------------------- | ------------------------------------------------------ | | `initialize` | Initialize MCP session and receive server capabilities | | `ping` | Health check | | `notifications/initialized` | Client notification after initialization | | `tools/list` | List all available tools with their input schemas | | `tools/call` | Execute a tool with the given arguments | ### Example: List Tools ```json Request theme={null} { "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} } ``` ```json Response theme={null} { "jsonrpc": "2.0", "id": 1, "result": { "tools": [ { "name": "stock_prices", "description": "Get historical and real-time stock price data", "inputSchema": { ... } } ] } } ``` ### Example: Call a Tool ```json Request theme={null} { "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "fundamentals_data_query", "arguments": { "sql": "SELECT ticker, revenue, net_income FROM financials WHERE ticker = 'AAPL' ORDER BY date DESC LIMIT 4" } } } ``` ```json Response theme={null} { "jsonrpc": "2.0", "id": 2, "result": { "content": [ { "type": "text", "text": "Query returned 4 rows..." } ] } } ``` ## Rate Limits * **60 requests per minute** per user * Exceeding the limit returns a JSON-RPC error with code `-32000` ## Error Codes | Code | Meaning | | -------- | ---------------------------------------- | | `-32600` | Invalid JSON-RPC request | | `-32601` | Method not found | | `-32602` | Invalid params (e.g., unknown tool name) | | `-32603` | Internal server error | | `-32000` | Rate limit exceeded | # Presigned URL Source: https://docs.linqalpha.com/api-reference/basic/presigned_url GET /v1/documents/{document_id}/presigned_url Generates a temporary presigned URL for secure access to the original document file. Provide a `document_id` (obtained from the References API) to receive a time-limited (10 min) URL that allows you to download or view the source document directly. This is useful when you need to access the actual file that was referenced in the search results. # Presigned URL V2 Source: https://docs.linqalpha.com/api-reference/basic/presigned_url_v2 GET /v2/documents/{document_id}/presigned_url Generates a temporary presigned URL for secure access to a document file. Supports both external documents (filings, transcripts, news) and RMS documents (Third Bridge transcripts, internal notes, EDS content). Use `search_type=rms` to access RMS documents. Default is `external` (same behavior as V1). For platform API keys, pass `organization_id` to specify which org's documents to query. If omitted, defaults to the API key's primary organization. Generate a temporary download URL for a single document. Supports both external documents (filings, transcripts) and RMS documents (internal notes, Third Bridge transcripts). ## Search Type Use the `search_type` parameter to specify which document source to query: | search\_type | Source | Document types | | -------------------- | ------------------ | ------------------------------------------------------------------ | | `external` (default) | External documents | SEC filings, earnings transcripts, IR slides, news | | `rms` | RMS documents | User-uploaded notes, emails, Third Bridge transcripts, EDS content | ## Quick Start ```bash External document (default) theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v2/documents/767d83c9-9990-460d-8725-06d9f1b699ce/presigned_url' \ --header 'X-API-KEY: ' ``` ```bash RMS document theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v2/documents/88ca5d94f625ac981f8a4b93422073b7/presigned_url?search_type=rms&organization_id=b898130a-d85e-4610-939d-c415231df8b7' \ --header 'X-API-KEY: ' ``` For platform API keys that access multiple organizations, pass `organization_id` to specify which org's documents to query. If omitted, defaults to the API key's primary organization. Download URLs expire after approximately 1 hour. Generate new URLs when needed. # References Source: https://docs.linqalpha.com/api-reference/basic/references GET /v1/references Retrieves evidence references for a specific chat message. Each reference corresponds to an exact chunk used in the final answer generated by the LinqAlpha engine. These chunks represent the precise portions of documents (e.g., filings, transcripts, or news) that were retrieved and cited by the model to construct the final response.. **How to find chat_message_id:** Look for the event with `event_name` set to `search_results` in the Chat API response data. For detailed instructions, see [search_results event response](https://docs.linqalpha.com/api-reference/basic/chat_v2#option-6). **How to view original documents:** 1. **Via Viewer:** `https://chat.linqalpha.com/rms/viewer?chat_message_id={chat_message_id}&citation_idx={citation_idx}` 2. **Direct Download:** If the reference contains a `document_id`, you can access the original document directly through the [presigned_url endpoint](https://docs.linqalpha.com/api-reference/basic/presigned_url). # References (v2) Source: https://docs.linqalpha.com/api-reference/basic/references_v2 GET /v2/conversations/{conversation_id}/references Retrieves the list of evidence references (citations) used in a conversation. The [Analytics SSE](/api-reference/basic/analytics-sse) endpoint streams the generated answer but **does not include references in the response**. Use this endpoint after the stream completes to fetch the full list of references that were cited in the answer. **Usage Flow:** 1. Call the Analytics SSE endpoint (`POST /v1/analytics/sse`) 2. From the SSE stream, find the `conversation` event → extract `conversation_id` 3. After the stream finishes, call this endpoint with the `conversation_id` 4. The response contains all references with citation index, source document info, and metadata **How to view original documents:** - **Via Viewer:** `https://chat.linqalpha.com/rms/viewer?conversation_id={conversation_id}&citation_idx={citation_idx}` # Search Source: https://docs.linqalpha.com/api-reference/basic/search POST /v1/search LinqAlpha uses multiple data sources and retrieval methods to find the most relevant documents or information based on a query. Unlike the generate endpoint, this API only performs the search step and returns matched results without generating a response. **How to view original documents:** 1. **Via Viewer:** `https://chat.linqalpha.com/rms/viewer?chat_message_id={chat_message_id}&citation_idx={citation_idx}` 2. **Direct Download:** If the reference contains a `document_id`, you can access the original document directly through the [presigned_url endpoint](https://docs.linqalpha.com/api-reference/basic/presigned_url). # Stop Stream Source: https://docs.linqalpha.com/api-reference/basic/stop_stream POST /v1/stop_stream Pause the RMS (Research Management System) chat stream. # TTSQL Source: https://docs.linqalpha.com/api-reference/basic/ttsql POST /v1/ttsql Converts natural language query to SQL, executes it, and returns results in markdown format **Earnings-call schedule fields.** These fields are returned **only for organizations entitled to earnings-call schedule retrieval** and are omitted entirely for all others. For an entitled organization, every successful response includes both `earnings_schedule` (array) and `earnings_schedule_truncated` (bool) — they are `[]` and `false` when the query isn't about earnings-call dates. For a schedule query (e.g. "When does Apple next report?"), `rdb_result` carries the same schedule as markdown text and `earnings_schedule` is its structured form (the two are always returned together, not one instead of the other). Upcoming dates that are still vendor projections (not yet confirmed) are flagged `is_estimated: true`. # Create Briefing Schedule Source: https://docs.linqalpha.com/api-reference/briefing/create POST /v1/briefings Create a new briefing with topic, tickers, and schedule in a single call. Create a new briefing schedule with topic, tickers, and delivery timing in a single API call. The briefing will be automatically generated and delivered via email at the scheduled time. ## Stock Selection You can specify stocks in two ways: * **`stock_ids`** (recommended) — Internal stock UUIDs. Use the [Map Tickers](/api-reference/basic/map_tickers) endpoint to convert ticker symbols to stock IDs first. * **`tickers`** — Ticker symbols (e.g. `["AAPL", "MSFT"]`). Automatically resolved to stock IDs via the ticker mapping service. If resolution fails, the request will return an error. If both `stock_ids` and `tickers` are provided, `stock_ids` takes priority. ## Scheduled Time Format The `scheduled_time` field must be in 12-hour AM/PM format: * `"9:00 AM"`, `"3:30 PM"`, `"12:00 PM"` ## Timezone Must be a valid IANA timezone identifier. Common examples: | Region | Timezone | | ---------- | --------------------- | | Korea | `Asia/Seoul` | | Hong Kong | `Asia/Hong_Kong` | | China | `Asia/Shanghai` | | Singapore | `Asia/Singapore` | | Taiwan | `Asia/Taipei` | | Japan | `Asia/Tokyo` | | India | `Asia/Calcutta` | | Indonesia | `Asia/Jakarta` | | Malaysia | `Asia/Kuala_Lumpur` | | Australia | `Australia/Sydney` | | UK | `Europe/London` | | US East | `America/New_York` | | US West | `America/Los_Angeles` | | US Central | `America/Chicago` | | UTC | `UTC` | ## Language Supported values: `English`, `Spanish`, `French`, `German`, `Italian`, `Portuguese`, `Dutch`, `Hindi`, `Japanese`, `Chinese`, `Finnish`, `Korean`, `Polish`, `Russian`, `Turkish`, `Ukrainian`, `Vietnamese`. Pass `null` for auto-detection. ## Authentication For platform API keys, you can optionally pass `organization_id`, `user_id`, `user_email`, and `user_name` in the request body to specify which user the briefing is created for. # Delete Briefing Schedule Source: https://docs.linqalpha.com/api-reference/briefing/delete DELETE /v1/briefings/{id} Delete an existing briefing schedule. Permanently delete an existing briefing schedule and its associated template. This action is irreversible. All future deliveries will be cancelled. Past delivery history will no longer be accessible via the deliveries endpoint. To temporarily pause a briefing without deleting it, use [Update Briefing](/api-reference/briefing/update) with `is_active: false` instead. # List Deliveries Source: https://docs.linqalpha.com/api-reference/briefing/deliveries GET /v1/briefings/{id}/deliveries Retrieve delivery history for a specific briefing. Retrieve the delivery history for a specific briefing schedule. Results are paginated and sorted by most recent first. Each delivery includes its status, email title, and timestamps. To get the full briefing content, use the [Get Delivery Detail](/api-reference/briefing/delivery-detail) endpoint. ## Pagination | Parameter | Default | Max | Description | | ---------- | ------- | --- | -------------- | | `page` | 1 | — | Page number | | `per_page` | 10 | 50 | Items per page | ## Delivery Status | Status | Description | | ---------- | ----------------------------------------------------- | | `pending` | Scheduled but not yet generated | | `sent` | Successfully generated and delivered | | `failed` | Generation or delivery failed | | `resynced` | Re-delivered after a content resync | | `skipped` | Intentional non-send (e.g. no content for this cycle) | # Get Delivery Detail Source: https://docs.linqalpha.com/api-reference/briefing/delivery-detail GET /v1/briefings/deliveries/{delivery_id} Retrieve full delivery detail including briefing content. Also available at /v1/briefings/{id}/deliveries/{delivery_id}. Retrieve the full detail of a specific delivery, including the generated briefing content. The content is Markdown for most briefings, but may be HTML for certain briefing types. Use the delivery ID to retrieve citation sources via [Get Delivery References](/api-reference/briefing/delivery-references). # Get Delivery References Source: https://docs.linqalpha.com/api-reference/briefing/delivery-references GET /v1/briefings/deliveries/{delivery_id}/references Retrieves the full list of citation references (sources) used to generate a briefing delivery. Briefing email content includes inline citations `[N]` that point into this list via `citation_idx`. Use this endpoint to build custom UIs, export source lists, or link readers back to original documents. **Usage Flow:** 1. Get a delivery via [List Deliveries](/api-reference/briefing/deliveries) or [Get Delivery Detail](/api-reference/briefing/delivery-detail) — note the `chat_session_id` 2. Verify delivery status is `sent` 3. Call this endpoint with `delivery_id` 4. For each reference, dispatch on `search_type` to build the viewer URL (see below) **How to view original documents** (switch on `search_type`): - `external` / `news` — the reference already links to the source. Use `external_url` directly (Factset viewer URL for news, publisher URL for external). - `tfs` (transcripts & filings) — open in the LinqAlpha document viewer: `https://chat.linqalpha.com/documents/{document_id}`. To download the raw file, pass `document_id` to the [Presigned URL](/api-reference/basic/presigned_url) endpoint. - `rms` (internal research, RMS-connected organizations) — open in the RMS viewer: `https://chat.linqalpha.com/rms/viewer?conversation_id={chat_session_id}&citation_idx={citation_idx}`. `chat_session_id` comes from the delivery detail response. **Error codes** (returned in response body with HTTP 200): - `BRIEFING_DELIVERY_NOT_FOUND` — `delivery_id` does not exist or does not belong to your organization - `BRIEFING_DELIVERY_REFERENCES_NOT_AVAILABLE` — delivery exists but has no associated session (still pending or generation failed) - `GET_SESSION_REFERENCES_FAIL` — upstream fetch failed Retrieves the citation references (sources) used to generate a briefing delivery. Briefing email content embeds inline citations `[N]` that point into this list via `citation_idx`. References become available once the delivery status is `sent`. Call [Get Delivery Detail](/api-reference/briefing/delivery-detail) first to confirm status and to grab the `chat_session_id` (required for building RMS viewer URLs). # Refresh External Content PDF Upload URL Source: https://docs.linqalpha.com/api-reference/briefing/external-content-refresh-upload-url POST /v1/external_content/refresh_upload_url Issues a 600-second presigned PUT URL for an existing active PDF source without changing articles, extracted content, metadata, timestamps, or attachment history. The API key must belong to the target organization or its platform. Upload with Content-Type: application/pdf. Missing or invalid sources return an error; never retry using content ingestion. Successful URL issuance does not confirm PDF upload or customer delivery. Error messages use fixed public descriptions and never include upstream response bodies. Recover a missing PDF attachment for an existing external content source using its organization, source type, and content date. Pass `source_type` exactly as registered. Refresh does not trim or otherwise normalize this identity. Both ingestion and refresh require a valid calendar `content_date` in `YYYY-MM-DD` format, with a year from 0001 to 9999. Impossible dates and year 0000 return HTTP 400 at the Public API validation boundary. The endpoint preserves the source's articles, extracted text, metadata, timestamps, and attachment history. It returns a presigned PUT URL for the existing stored PDF key, valid for 600 seconds. Upload the PDF with `Content-Type: application/pdf`. The source must already exist, be active, and have a PDF key. The API key must belong to the target organization or its platform. A missing source or failed refresh returns an error; clients must not retry through the content ingestion endpoint because ingestion replaces article content. Authentication, authorization, missing-source, and validation failures return HTTP 401, 403, 404, and 422 respectively. Upstream server failures, network errors, and timeouts return HTTP 503 and can be retried with bounded backoff. An unavailable upstream refresh route also returns HTTP 404. Error messages use fixed public descriptions and do not include upstream response bodies. A successful refresh confirms that an upload URL was issued. Verify the subsequent PUT succeeds before marking the PDF uploaded; this endpoint does not confirm customer delivery. # List Briefing Schedules Source: https://docs.linqalpha.com/api-reference/briefing/list GET /v1/briefings Retrieve all briefing schedules for the authenticated user. Retrieve all briefing schedules for the authenticated user. Each briefing includes its topic, tickers, schedule configuration, and next delivery time. Use this to display an overview of all active and inactive briefings. ## Response Returns a flat array of briefing objects. The `tickers` field contains resolved ticker symbols and `next_delivery_at` shows when the next briefing will be generated. # Preview Briefing On-Demand Source: https://docs.linqalpha.com/api-reference/briefing/preview POST /v1/briefings/preview Run a briefing on-demand from an ad-hoc prompt as a fire-and-forget job: enqueue the run and get a pending delivery handle immediately, then poll `GET /v1/briefings/deliveries/{delivery_id}` for the result. Run a briefing on-demand from an ad-hoc prompt, without waiting for it to finish. This endpoint is **fire-and-forget**: it enqueues the run and returns immediately with a pending delivery handle. You then fetch the finished briefing by polling the [delivery endpoints](/api-reference/briefing/delivery-detail) with the returned `delivery_id`. The output comes from the same engine behind your scheduled briefings, so it matches their depth, formatting, and citations — letting you preview a briefing before committing to a schedule. ## Stock Selection Scope the run to specific stocks in either of two ways (omit both to cover the full market): * **`stock_ids`** (recommended) — Internal stock UUIDs. Use the [Map Tickers](/api-reference/basic/map_tickers) endpoint to convert ticker symbols to stock IDs first. * **`tickers`** — Ticker symbols (e.g. `["AAPL", "MSFT"]`). Automatically resolved to stock IDs via the ticker mapping service. A symbol that cannot be resolved does not fail the request: it is forwarded upstream, retried through a second resolver there, and dropped from the scope if it still cannot be matched. If both `stock_ids` and `tickers` are provided, `stock_ids` takes priority. ## Polling for the result The `202` response returns `{ delivery_id, status }` with `status: "pending"` — the briefing is not ready yet. Poll `GET /v1/briefings/deliveries/{delivery_id}` until its `status` is terminal, then read the content from that response. `status` is the same field returned by [Get Briefing Delivery](/api-reference/briefing/delivery-detail). ## `previous_delivery_id` Pass the `delivery_id` of an earlier delivery to carry formatting and continuity from it into this run. The delivery must belong to your organization; otherwise the request returns `BRIEFING_NOT_FOUND` (see below). ## Errors Domain errors are returned as **HTTP `200`** with an `error.code` (and `payload: null`), consistent with the other briefing JSON endpoints. Input-validation failures are the exception — they return HTTP `400`. | Condition | HTTP status | `error.code` | | ---------------------------------------------- | ----------- | ------------------------ | | `query` missing or empty | `400` | `INVALID_REQUEST_BODY` | | `previous_delivery_id` is not a valid UUID | `400` | `INVALID_REQUEST_BODY` | | `previous_delivery_id` not owned by the caller | `200` | `BRIEFING_NOT_FOUND` | | Upstream / generation failure | `200` | `BRIEFING_GENERATE_FAIL` | ## Authentication For platform API keys, optionally pass `user_id` (the customer\_id), `user_email`, and `user_name` in the request body to select which user the briefing runs as. Ordinary organization-bound keys resolve the user from the key itself and ignore these fields. # Update Briefing Schedule Source: https://docs.linqalpha.com/api-reference/briefing/update PATCH /v1/briefings/{id} Update an existing briefing. All fields are optional. Update an existing briefing schedule. All fields are optional — only include the fields you want to change. ## Stock Selection Same as [Create Briefing](/api-reference/briefing/create): provide `stock_ids` (preferred) or `tickers`. Use the [Map Tickers](/api-reference/basic/map_tickers) endpoint to convert ticker symbols to stock IDs. ## Scheduled Time Format Must be in 12-hour AM/PM format: `"9:00 AM"`, `"3:30 PM"`, `"12:00 PM"`. ## Partial Update Only fields included in the request body will be updated. For example, to change only the schedule time: ```json theme={null} { "scheduled_time": "8:00 AM" } ``` To deactivate a briefing without deleting it: ```json theme={null} { "is_active": false } ``` Omitting a field leaves it unchanged. Sending `null` does **not** clear a field — for example, a linked watchlist group cannot currently be unlinked via this endpoint, and `language: null` does not reset an already-set language. ## Field Reference | Field | Type | Description | | ------------------------ | -------------- | ------------------------------------------------------------ | | `topic` | string | Briefing instructions | | `stock_ids` | string\[] | Internal stock UUIDs (preferred) | | `tickers` | string\[] | Ticker symbols (auto-resolved) | | `language` | string \| null | Output language (`null` = auto-detect; see note above) | | `scheduled_time` | string | `h:mm AM/PM` format | | `timezone` | string | IANA timezone (e.g. `America/New_York`) | | `frequency` | string | `daily`, `weekly`, or `monthly` | | `scheduled_day_of_week` | integer | 0 (Sun) – 6 (Sat), for weekly | | `scheduled_day_of_month` | integer | 1–31, for monthly | | `title` | string | Custom briefing title | | `source_type` | string | `tfs_only`, `rms_only`, or `both` | | `attach_pdf` | boolean | Attach PDF to delivery email | | `is_active` | boolean | Enable/disable the schedule | | `rms_sources` | object\[] | RMS source configs (`source`, `workspace`) | | `watchlist_group_id` | string | Watchlist group ID to link (unlink via `null` not supported) | | `use_custom_email_title` | boolean | Use title as fixed email subject | | `user_id` | string | Platform (EDS) keys only: target user's `customer_id` | | `user_email` | string | Platform (EDS) keys only: target user's email | | `user_name` | string | Platform (EDS) keys only: display name (used when creating) | # Execute Agent Judge Source: https://docs.linqalpha.com/api-reference/evaluations/create POST /v2/judge/agent Records the submission and queues it. Returns immediately; the evaluation itself runs asynchronously and is retrieved with `GET /v2/judge/agent/{evaluation_id}`. Runs the full agent-server decompose pipeline and returns a written assessment. For deterministic numeric scoring, use `POST /v2/judge/llm` instead. ## What it does Submits a question and an answer your own agent or LLM produced, and returns an `evaluation_id` immediately. The judge reads the answer's claims, checks the material ones against primary sources, and writes an expert-style assessment. That takes **minutes, not seconds**, so this endpoint does not return the assessment. `202` means the submission is recorded and will be evaluated — never that it has been. ``` POST /v2/judge/agent -> 202 { evaluation_id, status: "pending" } | | the judge runs on our side v GET /v2/judge/agent/{evaluation_id} -> the assessment, once it settles ``` `status` is `pending` for a new submission, but read it rather than assuming it. An `Idempotency-Key` retry returns the evaluation that key already names — which may have finished in the meantime — so it can come back `completed`, `excluded` or `failed`. The value set is the same one [Get Agent Judge Evaluation](/api-reference/evaluations/get) returns. Retrieve the result with [Get Agent Judge Evaluation](/api-reference/evaluations/get). ## Example ```python theme={null} import requests resp = requests.post( "https://api.linqalpha.com/v2/judge/agent", headers={ "X-API-KEY": "", "Content-Type": "application/json", # Optional but recommended: makes a retry safe. "Idempotency-Key": "run-2026-08-18-0001", }, json={ "query": "How did NVIDIA's data center segment perform in FY2025?", "answer": ( "Data center revenue reached $115.2B in FY2025, up 142% year over year, " "driven by Hopper shipments to hyperscalers." ), # The inclusive time range used by the selected search profile. `start_time` # is optional; omit it when only an upper cutoff is needed. "time_window": { "start_time": "2025-01-01T00:00:00Z", "end_time": "2026-08-18T05:32:11Z", }, # Optional. `external` is the default; use `rms` for RMS-only answers and # `all` when the answer combines RMS and external sources. "search_type": "external", # Structured references only. `metadata` is an optional free-form JSON object. "references": [ { "title": "NVIDIA FY2025 Q4 CFO Commentary", "content": "Data center revenue was $115.2 billion, up 142% from a year ago.", # Optional locator for the source the excerpt came from. "url": "https://investor.nvidia.com/financial-info/financial-reports-and-filings/", "metadata": { "published_at": "2025-02-26T16:00:00-08:00", "source_type": "earnings_release", }, }, ], }, ) evaluation_id = resp.json()["payload"]["evaluation_id"] ``` ## The fields These are the fields this endpoint reads; anything else in the body is ignored. **Built for organization-bound API keys.** The organization and user are taken from your key. The evaluation is filed under that organization, and RMS verification uses that user's document visibility. Platform-wide keys are not a supported configuration for this endpoint — it has not been designed or tested against them, and the behaviour you get is whatever the shared authentication layer does rather than something this endpoint guarantees. If you hold a platform key, talk to your LinqAlpha contact before integrating. | Field | Required | Notes | | ------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `query` | Yes | The question the answer responds to. Must contain non-whitespace text. | | `answer` | Yes | The answer to evaluate. Must contain non-whitespace text. | | `time_window` | Yes | Inclusive search window. `end_time` is required and `start_time` is optional. Both use RFC 3339 timestamps **with a timezone offset**. `start_time` must be earlier than or equal to `end_time`; a future `end_time` is rejected. | | `references` | No | Defaults to `[]`. Each element is `{ "title"?, "content", "url"?, "metadata"? }`. `content` is required; the rest are optional. `url` must be HTTP(S). `metadata` accepts any JSON object, including nested objects and arrays. A bare URL string and undeclared reference-level fields are not accepted. | | `search_type` | No | Verifier source scope: `rms`, `external`, or `all`. Defaults to `external`; use `all` when the answer combines RMS and external sources. | `start_time` and `end_time` are inclusive bounds applied to the selected `search_type`. When `search_type` is `all`, the same window applies to both RMS and external search. Omit `start_time` to search everything up to and including `end_time`. **The offset is required, and that is deliberate.** `2026-08-19T14:32:11` without one is ambiguous, and reading it as UTC would move a Seoul timestamp nine hours. The result of that is not an error you would see: it is a plausible assessment judged against the wrong instant. Send `Z` or your own offset — both name the same instant and both are accepted. `start_time` must be earlier than or equal to `end_time`. A future `end_time` is rejected; a few minutes of clock skew is tolerated. Each element is `{ "title"?, "content", "url"?, "metadata"? }`. `content` is the verbatim excerpt the judge source-grounds against — a link alone has nothing for it to check, so bare URL strings are refused. The reference object only accepts these four fields. `url` is an optional HTTP(S) locator for the original source. It points at where the excerpt came from; it does not replace `content`, and neither the URL nor its domain is treated as proof that the excerpt or its attribution is correct. `metadata` is a free-form JSON object. Its keys and nested structure are not prescribed, so it can carry a source date such as `published_at`, identifiers, tags, nested objects, arrays, numbers, booleans, and null values. The `metadata` value itself must be an object. Omit `references` entirely and the answer is still fact-checked independently. ## Idempotency Send an `Idempotency-Key` header to make retries safe. Within your organization: * Same key, same body → the **original** `evaluation_id`, no second judge run. * Same key, different body → `409 Conflict`. Use it whenever a network error leaves you unsure whether a submission landed. Without it, a retry starts a second run and you are billed for both. A retry still answers `202`, but the `status` it carries is the **original evaluation's current status** — not necessarily `pending`. If that evaluation already finished, you get `completed`, `excluded` or `failed` straight from the retry and there is nothing left to poll. ## Limits | | Max | | ---------------------- | ------------------------- | | `query` | 10,000 UTF-16 code units | | `answer` | 200,000 UTF-16 code units | | `references` | 200 items | | Whole body, serialized | 1,000,000 bytes (UTF-8) | | Whole body, tokenized | \~100,000 tokens | Oversized submissions are rejected with `400` at submission time — nothing is queued and nothing is billed, so a request that is too large costs only the round trip. Two of these are easy to trip without noticing. **The character counts are UTF-16 code units**, which is what `"…".length` returns in JavaScript. Characters outside the Basic Multilingual Plane — emoji, some rarer CJK — count as two. If your text is plain prose the distinction never comes up. **The token cap is separate from the byte cap**, and applies to the request as a whole. Dense CJK text can pass 200,000 code units and still exceed 100,000 tokens, so a long Korean or Japanese answer may be refused while a longer English one is not. These may be raised as we see real usage. A raise never breaks a client that was within the old figure, so code against them as minimums. If you are running close to one, tell us rather than splitting a submission. # Execute LLM Judge Source: https://docs.linqalpha.com/api-reference/evaluations/create_llm POST /v2/judge/llm Records the submission and queues it. Returns immediately; the judgement itself runs asynchronously and is retrieved with `GET /v2/judge/llm/{evaluation_id}`. Unlike `POST /v2/judge/agent` (Agent Judge), which returns a written assessment, this endpoint produces a **structured verdict** — dimension scores plus reasoning — on a fixed rubric (Factuality / Completeness / Relevance / Grounding, each 1-5) with a server-computed `overall_score`. Every run uses the same rubric so runs are comparable across callers and time. ## What it does Submits an answer plus your judge prompt and the sources you want it graded against, and returns an `evaluation_id` immediately. The judge verifies the sources within the selected source scope, then a deterministic LLM grades the answer on a **fixed rubric** — Factuality / Completeness / Relevance / Grounding, each 1–5 — and returns a **structured verdict** (dimension scores plus a short reasoning, with a server-computed `overall_score` mean). Unlike [Execute Agent Judge](/api-reference/evaluations/create), which returns a **written assessment** against primary sources, this endpoint returns **numeric scores you can compare across runs**. The rubric is the same for every run so scores are comparable across callers and across time. That still takes **minutes, not seconds**, so this endpoint does not return the verdict. `202` means the submission is recorded and will be evaluated — never that it has been. ``` POST /v2/judge/llm -> 202 { evaluation_id, status: "pending" } | | the judge runs on our side v GET /v2/judge/llm/{evaluation_id} -> the verdict, once it settles ``` `status` is `pending` for a new submission, but read it rather than assuming it. An `Idempotency-Key` retry returns the evaluation that key already names — which may have finished in the meantime — so it can come back `completed` or `failed`. The value set is the same one [Get LLM Judge Evaluation](/api-reference/evaluations/get_llm) returns. Retrieve the result with [Get LLM Judge Evaluation](/api-reference/evaluations/get_llm). ## Example ```python theme={null} import requests resp = requests.post( "https://api.linqalpha.com/v2/judge/llm", headers={ "X-API-KEY": "", "Content-Type": "application/json", # Optional but recommended: makes a retry safe. "Idempotency-Key": "run-2026-08-25-0001", }, json={ # OPTIONAL. Your framing for the judge — delivered verbatim as the system message # when supplied. The scoring dimensions themselves are fixed by the endpoint; use # `prompt` to steer emphasis (e.g. "penalise unhedged numeric claims") rather than # to change the rubric structure. Omit the field entirely to fall back to Linq's # default judge prompt. "prompt": ( "You are grading whether the ANSWER is supported by the SOURCES and by the " "verification verdicts. Score each dimension in the fixed rubric strictly, " "and justify the scores in a short reasoning." ), "query": "How did Apple perform in Q3 FY2024?", "answer": ( "Apple reported record Services revenue in Q3 FY2024 while iPhone revenue " "slipped year-over-year." ), # Inclusive search range. `start_time` is optional; `end_time` is required. "time_window": { "start_time": "2024-07-01T00:00:00Z", "end_time": "2026-08-19T05:32:11Z", }, # Optional. `external` is the default; use `rms` for RMS-only answers and # `all` when the answer combines RMS and external sources. "search_type": "external", # Structured references match the agent-judge payload shape. `content` is what # the final judge source-grounds against. "references": [ { "title": "Apple Q3 FY2024 Press Release", "content": ( "Apple today announced financial results for its fiscal 2024 third " "quarter. Services revenue reached an all-time high." ), # Optional locator for the source the excerpt came from. "url": "https://www.apple.com/newsroom/2024/08/apple-reports-third-quarter-results/", "metadata": { "published_at": "2024-08-01T13:30:00Z", "source_type": "press_release", }, }, { "title": "Apple Investor Relations", "content": ( "iPhone revenue was down slightly year-over-year while Services set " "a new record." ), }, ], }, ) evaluation_id = resp.json()["payload"]["evaluation_id"] ``` ## The fields These are the fields this endpoint reads; anything else in the body is rejected. **Built for organization-bound API keys.** The organization and user are taken from your key. The evaluation is filed under that organization, and RMS verification uses that user's document visibility. Platform-wide keys are not a supported configuration for this endpoint — it has not been designed or tested against them, and the behaviour you get is whatever the shared authentication layer does rather than something this endpoint guarantees. If you hold a platform key, talk to your LinqAlpha contact before integrating. | Field | Required | Notes | | ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `prompt` | No | Your framing for the judge. Delivered verbatim as the system message when supplied — steers emphasis; does **not** change the scoring dimensions. Omit the field to fall back to Linq's default judge prompt. If supplied it must contain non-whitespace text; an empty string is rejected. | | `query` | Yes | The original user prompt that produced the answer. Lets the judge check whether the answer followed the instructions. | | `answer` | Yes | The text being judged. Must contain non-whitespace text. | | `time_window` | Yes | Inclusive search window. `end_time` is required and `start_time` is optional. Both use RFC 3339 timestamps **with a timezone offset**. `start_time` must be earlier than or equal to `end_time`; a future `end_time` is rejected. | | `references` | No | Defaults to `[]`. Max 50 items. Each element is `{ "title"?, "content", "url"?, "metadata"? }`. `content` is required; the rest are optional. `url` must be HTTP(S). `metadata` accepts any JSON object, including nested objects and arrays. A bare URL string and undeclared reference-level fields are not accepted. | | `search_type` | No | Verifier source scope: `rms`, `external`, or `all`. Defaults to `external`; use `all` when the answer combines RMS and external sources. | **Fixed rubric.** Every LLM Judge run is scored on the same four dimensions (Factuality / Completeness / Relevance / Grounding, each 1–5), with a server-computed `overall_score` (arithmetic mean). Previously supported `scoring_rubric` and `response_schema` fields were removed so that scores from any two runs are directly comparable; sending either now returns a `400`. `start_time` and `end_time` are inclusive bounds applied to the selected `search_type`. When `search_type` is `all`, the same window applies to both RMS and external search. Omit `start_time` to search everything up to and including `end_time`. **The offset is required, and that is deliberate.** `2026-08-19T14:32:11` without one is ambiguous, and reading it as UTC would move a Seoul timestamp nine hours. The result of that is not an error you would see: it is a plausible verdict judged against the wrong instant. Send `Z` or your own offset — both name the same instant and both are accepted. `start_time` must be earlier than or equal to `end_time`. A future `end_time` is rejected; a few minutes of clock skew is tolerated. Each element is `{ "title"?, "content", "url"?, "metadata"? }`. The reference object only accepts these four fields; any other undeclared field is rejected with `400`. `content` is the passage itself. The verifier checks the answer against that text, so a link alone would give it nothing to read; paste the passage you are relying on. `url` is an optional HTTP(S) locator for that passage's source. It is a pointer used to inspect the original, not evidence — neither the URL nor its domain is trusted as proof that the excerpt, publisher, or date is correct. `metadata` is an optional free-form JSON object. It can contain source dates such as `published_at`, identifiers, tags, and arbitrary nested JSON without a predefined field list. The `metadata` value itself must be an object. ## How the judgement is produced The verifier runs each reference against Linq's tool set for the selected `search_type`, producing a per-reference verdict (`supported` / `contradicted` / `unresolved`) with evidence. When `references: []` this step is skipped and no cost is incurred here. A deterministic model (`temperature=0`, `seed=7`) reads your `prompt`, the query, answer, references, and verification verdicts, and returns integer scores for each of the four fixed dimensions plus a short reasoning. The server then computes `overall_score` as the arithmetic mean of the four scores. **Partial verification is preserved, not hidden.** If a verifier chunk failed, only the surviving verdicts reach the judge, and the response reports `verified_reference_count` **less than** `input_reference_count` so you can tell — see [Get LLM Judge Evaluation](/api-reference/evaluations/get_llm). ## Idempotency Send an `Idempotency-Key` header to make retries safe. Within your organization: * Same key, same body → the **original** `evaluation_id`, no second judge run. * Same key, different body → `409 Conflict`. Use it whenever a network error leaves you unsure whether a submission landed. Without it, a retry starts a second run and you are billed for both. Idempotency is scoped per endpoint. Using the **same key** on [Execute Agent Judge](/api-reference/evaluations/create) and this endpoint returns two distinct evaluations — the two endpoints do not share the key space. A retry still answers `202`, but the `status` it carries is the **original evaluation's current status** — not necessarily `pending`. If that evaluation already finished, you get `completed` or `failed` straight from the retry and there is nothing left to poll. ## Limits | | Max | | ---------------------- | ------------------------- | | `prompt` | 50,000 UTF-16 code units | | `answer` | 200,000 UTF-16 code units | | `query` | 10,000 UTF-16 code units | | `references` | 50 items | | Whole body, serialized | 1,000,000 bytes (UTF-8) | | Whole body, tokenized | \~100,000 tokens | Oversized submissions are rejected with `400` at submission time — nothing is queued and nothing is billed, so a request that is too large costs only the round trip. Two of these are easy to trip without noticing. **The character counts are UTF-16 code units**, which is what `"…".length` returns in JavaScript. Characters outside the Basic Multilingual Plane — emoji, some rarer CJK — count as two. If your text is plain prose the distinction never comes up. **The token cap is separate from the byte cap**, and applies to the request as a whole. Dense CJK text can pass 200,000 code units and still exceed 100,000 tokens, so a long Korean or Japanese answer may be refused while a longer English one is not. These may be raised as we see real usage. A raise never breaks a client that was within the old figure, so code against them as minimums. If you are running close to one, tell us rather than splitting a submission. # Get Agent Judge Evaluation Source: https://docs.linqalpha.com/api-reference/evaluations/get GET /v2/judge/agent/{evaluation_id} Returns HTTP 200 in every state, including while still running. Branch on `status`, not on the status code. ## What it does Returns the current state of one Agent Judge run, using the `evaluation_id` from [Execute Agent Judge](/api-reference/evaluations/create). The response shape is **identical in every state** — three fields, always present — so a client reads `status` and never has to branch on the body's shape. This returns HTTP `200` while the judge is still running. Branch on the `status` field, not on the status code. ## Statuses | `status` | Meaning | `evaluation` | Retry? | | ----------- | ------------------------------------------------------------------------ | ----------------------------------------------------- | ---------------------- | | `pending` | Queued or running. | `null` | Keep polling | | `completed` | Finished. | The assessment | No | | `excluded` | **Not an error.** The submission could not be judged fairly — see below. | One of a small whitelisted set of public-safe reasons | After fixing the input | | `failed` | Something broke on our side. | `null` | Yes | A `GET` immediately after a `POST` returns `pending`. That is expected, not an error. ### `excluded` is a verdict, not a failure Some submissions cannot be judged fairly, and saying so is more honest than inventing a score. A question that depends on context you did not send, or an answer whose task cannot be reconstructed from the fields you sent, is `excluded` rather than marked wrong. On `excluded`, `evaluation` carries one of a small whitelisted set of public-safe reasons the server maps untrusted model text into — for example, *"The request depends on information that was not provided, so it cannot be evaluated."* Use it to decide what to change before resubmitting. The distinction between `excluded` (fix your input and resubmit) and `failed` (server-side, safe to retry) is carried by `status`; on `failed`, no reason text ships on the wire. ## Polling Poll no more than **once every 10 seconds**. Typical runs settle in a few minutes; a sensible client gives up after around 30 minutes and treats the run as failed. ```python theme={null} import time import requests url = f"https://api.linqalpha.com/v2/judge/agent/{evaluation_id}" headers = {"X-API-KEY": ""} deadline = time.time() + 30 * 60 # give up after ~30 minutes while time.time() < deadline: payload = requests.get(url, headers=headers).json()["payload"] if payload["status"] == "completed": print(payload["evaluation"]) break if payload["status"] == "excluded": # Not an error: the submission could not be judged fairly. # `evaluation` carries the public-safe reason -- use it to decide what to # change (query / answer / references) before resubmitting. print("excluded:", payload["evaluation"]) break if payload["status"] == "failed": # Server-side, safe to retry. print("failed — retry the same submission") break time.sleep(10) # still pending ``` ## Response ```json theme={null} { "error": null, "payload": { "evaluation_id": "2fde560a-e7eb-45e0-8cd3-04e57c50d1d3", "status": "completed", "evaluation": "The answer's central figure is well supported..." } } ``` `evaluation` is non-null on `completed` (the written assessment) and on `excluded` (a public-safe reason from a small whitelisted set); `pending` and `failed` return `"evaluation": null`. Per-claim factual results, the evidence behind them, source-conflict details, and internal scoring are used to produce the assessment but are not part of this response in any state. ## Isolation Judge runs are scoped to the organization that submitted them. An `evaluation_id` belonging to another organization returns `404`, exactly as an id that does not exist — the two are indistinguishable by design. That organization comes from your API key, so an organization-bound key reads exactly the evaluations it submitted. Platform-wide keys are not a supported configuration here either — see [Execute Agent Judge](/api-reference/evaluations/create). A malformed `evaluation_id` is rejected with `400` before any lookup. # Get LLM Judge Evaluation Source: https://docs.linqalpha.com/api-reference/evaluations/get_llm GET /v2/judge/llm/{evaluation_id} Returns HTTP 200 in every state, including while still running. Branch on `status`, not on the status code. The response shape is identical across statuses — the same seven fields are always present. ## What it does Returns the current state of one LLM Judge run, using the `evaluation_id` from [Execute LLM Judge](/api-reference/evaluations/create_llm). The response shape is **identical in every state** — the same six fields are always present — so a client reads `status` and never has to branch on the body's shape. This returns HTTP `200` while the judge is still running. Branch on the `status` field, not on the status code. ## Statuses | `status` | Meaning | `verdict` | `judge_model` | `input_reference_count` / `verified_reference_count` | Retry? | | ----------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------- | ---------------------------------------------------- | ------------ | | `pending` | Queued or running. | `null` | `null` | `null` | Keep polling | | `completed` | Finished. | Fixed-rubric verdict — see below | The model that produced it | Both non-null | No | | `failed` | Something broke on our side. | Same shape as `completed`, but `scores` / `overall_score` are `null` and `reasoning` carries a customer-safe sentence | `null` | `null` | Yes | A `GET` immediately after a `POST` returns `pending`. That is expected, not an error. Failure detection: `verdict !== null && verdict.overall_score === null` is the failure marker. `reasoning` on such a verdict is the customer-safe sentence that used to be surfaced as a top-level `reason` field — that field was removed to keep the shape uniform. Unlike [Get Agent Judge Evaluation](/api-reference/evaluations/get), this endpoint does **not** currently return `excluded`. The LLM Judge has no exclusion pre-filter — the same status enum is exposed for consistency, but only `pending` / `completed` / `failed` are produced today. ## Polling Poll no more than **once every 10 seconds**. Typical runs settle in a few minutes; a sensible client gives up after around 30 minutes and treats the run as failed. ```python theme={null} import time import requests url = f"https://api.linqalpha.com/v2/judge/llm/{evaluation_id}" headers = {"X-API-KEY": ""} deadline = time.time() + 30 * 60 # give up after ~30 minutes while time.time() < deadline: payload = requests.get(url, headers=headers).json()["payload"] if payload["status"] == "completed": print(payload["verdict"]) # Coverage signal — see the note below. print(f"verified {payload['verified_reference_count']}/{payload['input_reference_count']}") break if payload["status"] == "failed": # `verdict.overall_score is None` is the failure marker; `reasoning` is the # customer-safe reason that used to live under a separate `reason` field. print("failed:", payload["verdict"]["reasoning"]) # safe to retry break time.sleep(10) # still pending ``` ## Response `verdict` uses one shape on every non-null run — four dimensions on a 1–5 scale, plus `reasoning` and a server-computed `overall_score`. `completed` populates all three; `failed` uses the same shape with `scores` / `overall_score` set to `null` and the customer-safe reason on `reasoning`. Every successful LLM Judge run therefore returns scores directly comparable across callers and time, and a caller reads one field to tell success from failure. ### `completed` ```json theme={null} { "error": null, "payload": { "evaluation_id": "2fde560a-e7eb-45e0-8cd3-04e57c50d1d3", "status": "completed", "verdict": { "scores": { "Factuality": 4, "Completeness": 4, "Relevance": 5, "Grounding": 5 }, "reasoning": "The answer's core claims align with the supplied sources...", "overall_score": 4.5 }, "judge_model": "gpt-5.4-mini", "input_reference_count": 2, "verified_reference_count": 2 } } ``` ### `failed` ```json theme={null} { "error": null, "payload": { "evaluation_id": "2fde560a-e7eb-45e0-8cd3-04e57c50d1d3", "status": "failed", "verdict": { "scores": null, "reasoning": "The evaluation could not be completed.", "overall_score": null }, "judge_model": null, "input_reference_count": null, "verified_reference_count": null } } ``` ## The response fields | Field | Type | Notes | | -------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------- | | `evaluation_id` | UUID | The id from `POST /v2/judge/llm`. | | `status` | enum | `pending` / `completed` / `failed`. | | `verdict` | object \| null | Non-null on `completed` **and** `failed`. See the two response examples above. | | `judge_model` | string \| null | The model that produced the verdict. Non-null only on `completed`. | | `input_reference_count` | int \| null | How many references you submitted. Non-null only on `completed`. | | `verified_reference_count` | int \| null | How many references the verifier actually produced a verdict for. Non-null only on `completed`. See below. | On a clean run the two are equal. When they differ (`verified_reference_count < input_reference_count`), a chunk of the verifier failed and only the surviving verdicts reached the judge — the verdict is still valid, but partly graded on incomplete verification. Downstream you may want to weigh those runs differently or resubmit. On non-`completed` statuses both are `null`. Submitting `references: []` is a valid request; the verifier is skipped and the judge grades on `prompt` / `query` / `answer` only. In this case `input_reference_count` and `verified_reference_count` are both `0`, not `null`. Per-reference verification verdicts, the evidence behind them, and internal cost/latency accounting are used to produce the verdict but are not part of this response. ## Isolation Judge runs are scoped to the organization that submitted them. An `evaluation_id` belonging to another organization returns `404`, exactly as an id that does not exist — the two are indistinguishable by design. The two endpoints — this one and [Get Agent Judge Evaluation](/api-reference/evaluations/get) — read from **disjoint** id spaces. An id from `POST /v2/judge/agent` returns `404` here, and vice versa. Idempotency keys are scoped the same way, so the same key on the two endpoints yields two separate evaluations. A malformed `evaluation_id` is rejected with `400` before any lookup. # Create Organization Source: https://docs.linqalpha.com/api-reference/rms/create_organization POST /v1/organizations Creates a new organization with the specified parameters. **Note**: This feature is available to a limited set of users only. Access requires a separate onboarding process, which will be provided upon request. For more information, please contact us at [support@linqalpha.com](mailto:support@linqalpha.com) # Create Source Source: https://docs.linqalpha.com/api-reference/rms/create_source POST /v1/sources Registers a previously-uploaded file as a document source under a source batch and queues it for asynchronous parsing. Once `status: "success"`, the source is searchable in chat/search conversations that reference its `source_batch_id`. **Important Notes:** - **Prerequisite:** A source batch must be created first using `POST /v1/source_batches` - **The file must already be in S3** before calling this endpoint. Two ways to get it there: - **Recommended:** call `POST /v1/sources/upload_url` to obtain a presigned PUT URL, upload the file directly via that URL, then pass the returned `file_key` here. No client-side AWS setup required. - **Alternative:** if you already have your own S3 staging bucket integrated with us, upload there and pass the resulting `file_key`. - **Processing:** Sources are queued for asynchronous processing. Use `GET /v1/sources/{source_id}` to poll the processing status before using in conversations. - **File Requirements:** - Supported formats: PDF, DOCX, XLSX, DOC, TXT, PPTX - Maximum size: 20MB per file - Maximum files per batch: 10 files - **Usage in Conversations:** - Reference sources via `source_batch_id` in chat/search API requests - All sources in a batch are accessible when the batch is referenced - Sources remain available for reuse within conversations that reference their batch **Workflow (presigned-URL path):** 1. `POST /v1/source_batches` → `source_batch_id` 2. `POST /v1/sources/upload_url` → `upload_url`, `file_key`, `content_type` 3. PUT the file body to `upload_url` with header `Content-Type: ` 4. `POST /v1/sources` (this endpoint) with `file_key` from step 2 5. Poll `GET /v1/sources/{source_id}` until `status: "success"` 6. Reference `source_batch_id` in chat/search requests # Create Source Batch Source: https://docs.linqalpha.com/api-reference/rms/create_source_batch POST /v1/source_batches Creates a new source batch container for organizing and managing related document sources. A source batch acts as a logical grouping that must be created before uploading any sources. The returned `source_batch_id` is required when creating sources and can be used to reference all contained sources in chat or search requests. **Workflow:** 1. Create a source batch (this endpoint) 2. Upload sources to the batch using the returned `source_batch_id` 3. Use the `source_batch_id` in chat/search API requests to access all sources in the batch # RMS Chat Source: https://docs.linqalpha.com/api-reference/rms/rms_chat POST /v1/rms_chat Generate a response using the RMS (Research Management System) with streaming server-sent events. This endpoint provides enhanced search capabilities with organization-specific data and customizable document types. **Note**: This feature is available to a limited set of users only. Access requires a separate onboarding process, which will be provided upon request. For more information, please contact us at [support@linqalpha.com](mailto:support@linqalpha.com) # RMS Deep Research Source: https://docs.linqalpha.com/api-reference/rms/rms_deep_research POST /v1/rms_deep_research Generate a response using the RMS (Research Management System) with deep research capabilities. This endpoint provides enhanced search capabilities with organization-specific data and customizable document types. **Note**:This feature is available to a limited set of users only. Access requires a separate onboarding process, which will be provided upon request. For more information, please contact us at [support@linqalpha.com](mailto:support@linqalpha.com) # Source Source: https://docs.linqalpha.com/api-reference/rms/source GET /v1/sources/{source_id} Retrieves the current processing status of an uploaded source. Use this endpoint to poll source readiness before including it in chat or search requests. **Status Values:** - `processing`: Source is being parsed and indexed (typically takes 10-60 seconds depending on file size) - `success`: Source is ready for use in conversations - `failed`: Processing failed (check file format, size, or content validity) **Best Practice:** Poll this endpoint with exponential backoff until status is `success` before initiating chat/search requests. # Get Upload URL Source: https://docs.linqalpha.com/api-reference/rms/upload_url POST /v1/sources/upload_url Returns a presigned S3 PUT URL for direct file upload. Lets clients upload files without managing AWS credentials or staging buckets — the server signs a single-use URL bound to a server-generated S3 object key, the client PUTs the file body to that URL, then registers the upload via `POST /v1/sources` with the returned `file_key`. **Why this exists:** `POST /v1/sources` requires a `file_key` for an object that already exists in our backing storage. Customers without their own staging bucket use this endpoint to obtain an upload URL pointed at the canonical destination directly, eliminating any client-side AWS setup. **Important — Content-Type binding:** The presigned URL is signed with `Content-Type` baked into the signature. The client **MUST** send a matching `Content-Type` header on the PUT request, otherwise S3 rejects with HTTP 403 (`SignatureDoesNotMatch`). The expected value is returned in the `content_type` field of this response — pass it back as the `Content-Type` header on your PUT. **Workflow:** 1. Create a source batch via `POST /v1/source_batches` (returns `source_batch_id`) 2. Call this endpoint with `name`, `source_type`, `source_batch_id` (returns `upload_url`, `file_key`, `content_type`, `expires_in`) 3. PUT the file to `upload_url` with `Content-Type: ` header 4. Register the source via `POST /v1/sources` with `file_key` from step 2 5. Poll `GET /v1/sources/{source_id}` until `status: "success"` 6. Reference `source_batch_id` in chat/search requests **Constraints:** - Supported `source_type`: `pdf`, `docx`, `xlsx`, `pptx`, `doc`, `txt` - Maximum file size enforced at registration step: 20MB - Maximum 10 active sources (status: `processing` or `success`) per source_batch — `failed`/`deleted` sources do not count toward the limit (enforced when generating the URL) - URL expires after 10 minutes (`expires_in: 600`) - File is validated at registration time (`POST /v1/sources`); a wrong-content upload will be accepted by S3 but rejected when registering # Container Sync Status Source: https://docs.linqalpha.com/api-reference/status/containers GET /v1/status/containers Returns per-container sync status with direct children IDs. At least one filter (path, name, or container_ids) is required. # Document Sync Status Source: https://docs.linqalpha.com/api-reference/status/documents GET /v1/status/documents Returns per-document sync status. At least one filter (path, name, or document_ids) is required. # Organization Sync Status Source: https://docs.linqalpha.com/api-reference/status/sync GET /v1/status/sync Returns an overview of the sync status for your organization, including searchable-aware document counts and recent sync job history. # Vault — Confirm Upload Source: https://docs.linqalpha.com/api-reference/vault/confirm POST /v2/vault/confirm Step 3 of the Vault upload flow. Registers the uploaded file in RMS and triggers async ingestion (parse → chunk → embed → index), returning immediately with the rms_document_id. Poll GET /v2/vault/status for processing status / readiness. Step 3 of the Vault upload flow. Registers the uploaded file in RMS and triggers async ingestion (parse → chunk → embed → index). The file **must already be uploaded** (via the presigned URL) before calling this. `confirm` is **enqueue-and-return**: it responds immediately with the `rms_document_id`. Processing runs in the background — poll **`GET /v2/vault/status`** with the returned `rms_document_id` until `status` is `Synced`. (Document status lives on `/vault/status`, not on this response.) ```bash theme={null} curl --request POST \ --url 'https://api.linqalpha.com/v2/vault/confirm' \ --header 'X-API-KEY: ' \ --header 'Content-Type: application/json' \ --data '{"document_id":"3a5b47d5-...","file_key":"development/data-original/.pdf","file_name":"Q4_report.pdf","content_type":"application/pdf"}' ``` `rms_document_id` is populated for normal file uploads. It is `null` only when the upload deduplicates into an existing document (`status: "already_exists"`) — in that case the document already exists and no new processing is enqueued. ## `content_type` by file type Pass the canonical MIME type for the file's type. **If the `content_type` does not match the file, the document is not processed** — always use the exact value from the table below. The **same `content_type` must be used at all three steps** — `presigned_url`, the upload `PUT` (`Content-Type` header), and `confirm`. The upload URL is signed with it, so a mismatch is rejected with HTTP `403`. | File type | Extension | `content_type` | | ------------------- | --------- | --------------------------------------------------------------------------- | | PDF | `.pdf` | `application/pdf` | | Word | `.docx` | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | | Word (legacy) | `.doc` | `application/msword` | | Excel | `.xlsx` | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | | Excel (macro) | `.xlsm` | `application/vnd.ms-excel.sheet.macroenabled.12` | | Excel (binary) | `.xlsb` | `application/vnd.ms-excel.sheet.binary.macroenabled.12` | | Excel (legacy) | `.xls` | `application/vnd.ms-excel` | | PowerPoint | `.pptx` | `application/vnd.openxmlformats-officedocument.presentationml.presentation` | | PowerPoint (legacy) | `.ppt` | `application/vnd.ms-powerpoint` | | Text | `.txt` | `text/plain` | | CSV | `.csv` | `text/csv` | | Audio | `.mp3` | `audio/mpeg` | | Audio | `.m4a` | `audio/x-m4a` | | Audio | `.wav` | `audio/wav` | | Video | `.mp4` | `video/mp4` | | HWP | `.hwp` | `application/vnd.hancom.hwp` | | HWPX | `.hwpx` | `application/vnd.hancom.hwpx` | The server resolves the file type from the **`file_name` extension first**, falling back to `content_type` only when the name has no extension — so always send a `file_name` with the correct extension. Legacy Office (`.xls/.xlsm/.xlsb/.doc/.ppt`), audio, and HWP/HWPX are auto-converted to a renderable parse target during ingestion. ## `document_id` vs `rms_document_id` These are **two different identifiers** created at different steps. Mixing them up is the most common integration error. | ID | Created by | What it's for | | ----------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `document_id` | `presigned_url` (an upload handle) | Carry it from `presigned_url` → `confirm`. Used only to tie the two calls together. Downstream endpoints do **not** use it. | | `rms_document_id` | `confirm` (the RMS document row's id) | The real document identifier. Pass it to **`GET /v2/vault/status`** (`rms_document_ids`) and to the **analytics SSE** search payload (`search_types.rms[].document_ids`). | ``` presigned_url → document_id (handle: presign → confirm only) confirm → rms_document_id (real id) status → rms_document_ids = [rms_document_id] analytics SSE → search_types.rms[].document_ids = [rms_document_id] ``` For status polling and the analytics SSE, always use **`rms_document_id`** — never the presign `document_id`. When `rms_document_id` is `null` (dedup), there is no new id to poll; the existing document is already processed. # Vault — Get Upload URL Source: https://docs.linqalpha.com/api-reference/vault/presigned_url POST /v2/vault/presigned_url Step 1 of the Vault upload flow. Returns a single-use presigned upload (PUT) URL. Upload the file directly to that URL, then register it via POST /v2/vault/confirm. Step 1 of the Vault upload flow. Returns a single-use presigned upload (PUT) URL. ## Flow 1. **`POST /v2/vault/presigned_url`** → `{ presigned_url, file_key, document_id }` 2. `PUT` the file to `presigned_url` with a matching `Content-Type` header 3. **`POST /v2/vault/confirm`** → registers the document and triggers processing 4. **`GET /v2/vault/status`** → poll until `status` is `Synced` (for the slow tail) ```bash theme={null} curl --request POST \ --url 'https://api.linqalpha.com/v2/vault/presigned_url' \ --header 'X-API-KEY: ' \ --header 'Content-Type: application/json' \ --data '{"file_name":"Q4_report.pdf","content_type":"application/pdf","workspace":"personal"}' ``` The presigned URL is single-use and expires after \~10 minutes. The `PUT` request's `Content-Type` must match the `content_type` you sent here. # Vault — Document Status Source: https://docs.linqalpha.com/api-reference/vault/status GET /v2/vault/status Returns the processing status for one or more vault documents. Poll after confirm until status is `Synced` (complete and searchable). Returns the processing status for one or more vault documents. Poll this after `confirm` until `status` is `Synced` (complete and searchable), or to re-check documents later. ## Status values | `status` | Meaning | | ---------------- | ---------------------------------------------- | | `Synced` | Complete and **searchable** — poll until this. | | `Syncing...` | Still processing. | | `Syncing Failed` | Processing failed. | For link-swapped formats (audio, legacy Office), the status stays `Syncing...` until the converted/transcript artifact is ready — so **`Synced` always means usable**, with no separate readiness flag. Each entry also returns `fail_code` — a deterministic terminal-failure code (e.g. `PASSWORD_PROTECTED`, `FILE_SIZE_EXCEEDED`, `CONVERSION_FAILED`) so you can branch on the reason instead of parsing the `status` text. It is `null` unless the document terminally failed. ```bash theme={null} curl --request GET \ --url 'https://api.linqalpha.com/v2/vault/status?rms_document_ids=eeb72343-...,3a0e9e69-...' \ --header 'X-API-KEY: ' ``` `rms_document_ids` is a comma-separated list of **`rms_document_id`** values (the `rms_document_id` returned by `confirm`), max 100. **Use `rms_document_id`, not the presign `document_id`.** The two are different identifiers — see [Confirm Upload](/api-reference/vault/confirm) for their roles. Passing the presign `document_id` here returns no match. # Quickstart Source: https://docs.linqalpha.com/quickstart Linq API - Light Theme Linq API - Dark Theme ## Getting Started Kickstart your integration with the Linq Alpha API by setting up your development environment. Follow our guides to authenticate, send requests, and handle streaming responses (including Server-Sent Events). Set up your API keys, configure endpoints, and prepare your local environment. Experiment with endpoints, test your requests, and view live responses. ## Authentication Linq API uses API key authentication. You must include your API key in the header of every request. For example: ```http theme={null} X-API-KEY: YOUR_API_KEY ```