Skip to main content
POST
/
v1
/
rag
/
orchestrate
Orchestrator agent — multi-step financial research (streaming)
curl --request POST \
  --url https://api.ivory.finance/v1/rag/orchestrate \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <api-key>' \
  --data '
{
  "query": "<string>",
  "company_name": "<string>",
  "cik": "<string>",
  "accession_number": "<string>",
  "transcript_doc_id": "<string>",
  "exhibit_doc_id": "<string>",
  "llm_provider": "bedrock",
  "llm_model": "deepseek.v3.2",
  "session_id": "<string>",
  "client_id": "<string>",
  "document_ids": [
    "<string>"
  ],
  "config": {
    "llm_provider": "bedrock",
    "llm_model": "deepseek.v3.2",
    "search_provider": "google",
    "search_api_key": "<string>",
    "search_custom_url": "<string>",
    "db_connection_string": "<string>"
  },
  "chat_id": "<string>",
  "conversation_history": [
    {
      "role": "user",
      "content": "<string>"
    }
  ]
}
'
{
  "detail": [
    {
      "loc": [
        "<string>"
      ],
      "msg": "<string>",
      "type": "<string>",
      "input": "<unknown>",
      "ctx": {}
    }
  ]
}

Documentation Index

Fetch the complete documentation index at: https://docs.ivory.finance/llms.txt

Use this file to discover all available pages before exploring further.

How it works

The orchestrator sits above the individual tool-calling agent. Instead of a flat tool loop, it adds an explicit planning phase that structures the research before execution begins.
1

Phase 0 — Planning

The orchestrator calls a structured plan tool to decompose the query into an ordered task list. This plan is emitted as a plan SSE event before any research begins — giving your UI an immediate preview of the research strategy.
{
  "type": "plan",
  "steps": [
    { "agent": "news_search",           "task": "Find Apple Q4 2025 earnings date" },
    { "agent": "research_sec_filing",   "task": "Retrieve Apple 10-K risk factors" },
    { "agent": "analyze_filing_risks",  "task": "Extract Loughran-McDonald risk signals" },
    { "agent": "generate_chart",        "task": "Visualise risk category breakdown" }
  ]
}
2

Phase 1 — Tool loop (up to 5 turns)

The orchestrator selects from the full tool suite. Each macro-tool may run multiple internal operations — for example, research_sec_filing internally runs search_filings followed by retrieve_from_filing in a single orchestrator step.Every tool call emits agent_step SSE events (running → done) so your UI can show live progress.
3

Phase 2 — Streaming answer

Sources, charts, and the final cited Markdown answer are streamed in order: sourceschart × N → token × N → conversation_statedone.

Source credibility hierarchy

The orchestrator applies a tiered trust model to all sources. When sources conflict, higher-tier sources win.
TierLabelSource
1SEC Periodic Filing (Audited)research_sec_filing — 10-K, 10-Q, 20-F, 40-F
2Official Company (Unaudited)research_sec_filing — 8-K, 6-K; search_transcripts; search_exhibits; NGX/JSE filings
3Expert Referenceweb_search with service: wikipedia or service: arxiv
4Financial News Medianews_search all services; web_search reuters
5General Web / User Documentweb_search google/yahoo; retrieve_from_document; read_editor_document
6Social Mediaweb_search reddit/x — noise, never stated as fact
When a Tier 1 filing contradicts a Tier 4 news article, the filing wins. The LLM will explicitly note when only Tier 5–6 sources are available for a claim.

Tools reference

Internally runs search_filings to locate relevant filings, then retrieve_from_filing on the best matches. Returns both a filing discovery list and semantic chunk excerpts — one orchestrator step instead of two.
{
  "query": "AWS cloud segment revenue Q4 2025",
  "cik": "0001018724",
  "form_types": ["10-K"],
  "after_date": "2026-01-01",
  "max_filings": 2
}
ParameterTypeDescription
querystring ✓Semantic search query for retrieving content
cikstring10-digit SEC CIK (zero-padded). Provide for faster, precise lookup.
company_namestringCompany name (fuzzy match). Use when CIK is unknown.
form_typesstring[]Filter to specific forms: 10-K, 10-Q, 8-K, 6-K, 20-F, DEF 14A, etc.
after_datestringISO date lower bound (YYYY-MM-DD)
before_datestringISO date upper bound (YYYY-MM-DD)
max_filingsintegerCap on filings to retrieve (default: 2)
Well-known CIKs: Amazon=0001018724, Microsoft=0000789019, Tesla=0001318605, Alphabet=0001652044, Meta=0001326801, NVIDIA=0001045810, Apple=0000320193, Netflix=0001065280.Source tier: 1 (10-K/10-Q/20-F/40-F) or 2 (8-K/6-K)
Search for recent financial news: earnings dates, analyst reactions, press releases, M&A activity. Use before research_sec_filing to confirm the right reporting period.
{ "query": "Apple Q4 2025 earnings date", "service": "reuters" }
ServiceSource tier
google (default)4
reuters4
yahoo4
Execute a read-only query against the configured database. Useful for structured lookups: earnings calendar, CIK-to-ticker mappings, portfolio holdings, or proprietary financial data.
{
  "sql": "SELECT ticker, report_date, eps_actual FROM public.earnings_calendar WHERE ticker = 'AAPL' ORDER BY report_date DESC LIMIT 4",
  "params": null
}
MongoDB:
{ "collection": "earnings", "filter": { "ticker": "AAPL" }, "sort": { "report_date": -1 }, "limit": 4 }
Configure a database connection per-request via config.db_connection_string. See Agent Configuration for supported schemes.
Runs the Loughran-McDonald financial risk lexicon across all indexed chunks for a filing. Returns keyword frequencies, density (per-thousand-words), and a composite risk score (0–10) across six categories.
{ "accession_number": "0000320193-25-000001", "section": "risk factors" }
CategoryExample keywords
Litigationlawsuit, class action, settlement, arbitration, subpoena
Liquiditygoing concern, covenant, default, working capital, refinancing
Regulatoryfine, penalty, sanction, enforcement, corrective action
Marketvolatility, recession, tariff, macroeconomic, pricing pressure
Operationalsupply chain, cybersecurity breach, outage, recall, shortage
Governancerestatement, material weakness, internal control, fraud, related party
Always follow with generate_chart to visualise the results.
Generates an interactive Plotly chart rendered automatically in the UI. Emitted as a chart SSE event — no frontend code required beyond calling Plotly.newPlot().See Visualization for the full chart type reference.
{
  "chart_type": "dual_axis",
  "title": "Litigation Risk vs Revenue — $AAPL",
  "spec": {
    "x": ["FY2023", "FY2024", "FY2025"],
    "bar_series": [{ "name": "Litigation Mentions", "data": [134, 151, 167] }],
    "line_series": [{ "name": "Revenue ($B)", "data": [383, 391, 400] }],
    "y1_label": "Revenue ($B)", "y2_label": "Litigation Mentions",
    "ticker": "AAPL"
  }
}
Execute a Cypher query against the Ivory FalkorDB knowledge graph. Use for relationship queries: insider trading networks, corporate ownership chains, co-filing relationships, earnings call speaker graphs.
{
  "cypher": "MATCH (c:Company {ticker: 'AAPL'})-[:HELD_CALL]->(ec:EarningsCall) RETURN ec.call_date, ec.fiscal_quarter ORDER BY ec.call_date DESC LIMIT 5",
  "params": {}
}
Graph schema — Key node types: Company, Person, Filing, EarningsCall, Auditor, SectorKey edges: HELD_CALL, SPOKE_AT {role, section}, ANALYZED_AT {firm}, TRADED {transaction_code, shares, price_per_share, total_value}
Finds sector peers based on embedding similarity and returns a financial ratio comparison table.
{ "cik": "0001045810", "min_similarity": 40 }
ParameterDefaultDescription
cikCIK of the focal company
min_similarity40Minimum similarity score (0–100) to include a peer
Returns a structured earnings brief with QoQ/YoY comparisons and key quotes from the earnings call transcript.
{
  "cik": "0001045810",
  "ticker": "NVDA",
  "fiscal_year": 2026,
  "quarter": 4
}
Omit fiscal_year and quarter to get the most recent quarter.
Exports structured financial data to a downloadable Excel workbook.
{ "export_type": "financials", "cik": "0001045810" }
export_typeContent
financialsIncome statement, balance sheet, cash flow
compsPeer comparison table
revenue_segmentsRevenue breakdown by segment and period
dcf_inputsDCF model inputs
Hybrid BM25 + KNN search over indexed earnings call transcripts. Returns speaker-labelled chunks with fiscal period and call date context.
{
  "query": "data center revenue GPU supply constraints",
  "ticker": "NVDA",
  "after_date": "2025-01-01",
  "limit": 5
}
ParameterDescription
querySemantic search query
tickerFilter by ticker symbol
company_nameFilter by company name (fuzzy match)
after_date / before_dateDate range bounds (YYYY-MM-DD)
limitMax chunks to return (default 5)
Source tier: 2 — Official Company (earnings call, unaudited)
Hybrid BM25 + KNN search over press releases, investor presentations, and SEC exhibits (EX-99.x).
{
  "query": "Q4 2025 revenue guidance raised",
  "ticker": "AAPL",
  "exhibit_type": "EX-99.1",
  "limit": 5
}
ParameterDescription
exhibit_typeFilter by exhibit type (e.g. EX-99.1, EX-99.2)
Source tier: 2 — Official Company (press release/investor exhibit)
KNN search over a user-uploaded document scoped by doc_id. The document must have been uploaded and indexed via client_id + document_ids in the request.
{
  "doc_id": "my-upload-id-123",
  "query": "revenue projection methodology"
}
Requires client_id and document_ids to be set in the API request body. Returns [D1], [D2], … citations.Source tier: 5 — User Document (unverified)
Full-text search over UK Companies House regulatory filings.
{
  "query": "annual report revenue 2025",
  "company_name": "Barclays",
  "form_type": "AA",
  "limit": 5
}
ParameterDescription
company_nameUK company name (fuzzy match)
crnCompanies House Registration Number
form_typeFiling type (e.g. AA=Annual accounts, CS01=Confirmation statement)
Source tier: 2 — UK Regulatory Filing
Full-text search over UK Financial Conduct Authority regulatory filings and disclosures.
{
  "query": "capital requirements liquidity ratio",
  "company_name": "HSBC",
  "limit": 5
}
Source tier: 2 — UK FCA Regulatory Filing
Full-text search over Nigerian Exchange Group (NGX) and SEC regulatory filings.
{
  "query": "annual report profit 2025",
  "company_name": "Dangote Cement",
  "form_type": "Annual Report",
  "limit": 5
}
Source tier: 2 — NGX/SEC Regulatory Filing
Read the full content and metadata of an Ivory Intelligence Editor document. Returns the complete document text extracted from the Tiptap AST.
{ "doc_id": "doc_01JK4..." }
Access rules:
  • The user’s own documents (any visibility) — always accessible
  • Public documents by other users — accessible (read-only)
  • Private/workspace documents by other users — blocked
Source tier: 5 — User Editor Document (unverified)Use when the user says “analyse my document”, “summarise the report I wrote”, or references content they have in the editor.
Semantic (KNN) search across Ivory Editor documents with visibility-aware scoping.
{ "query": "Apple risk analysis Q4 2025", "scope": "all", "limit": 5 }
scopeSearches
privateOnly the requesting user’s own documents
publicAll publicly shared documents (any user)
all (default)Both private and public
Users control discoverability by setting their document visibility to public.Source tier: 5 — User Editor Document (unverified)
Insert or replace content in an Ivory Editor document. The write is tagged updated_by_type='agent' for full audit traceability.
{
  "doc_id": "doc_01JK4...",
  "operation": "append",
  "blocks": [
    {
      "type": "heading",
      "attrs": { "level": 2 },
      "content": [{ "type": "text", "text": "Risk Summary" }]
    },
    {
      "type": "paragraph",
      "content": [{ "type": "text", "text": "Litigation risk increased 24% YoY [S1][S2]." }]
    }
  ]
}
operationBehaviour
append (default)Add blocks to the END of the document
prependAdd blocks to the BEGINNING
replace_allReplace the entire document body (use with caution)
Only call this when the user explicitly asks the agent to write into their document. Never write without clear user intent. Only the document owner can be written to by the agent.
Creates a sec_citation smart block from a filing and appends it to an editor document. Combines KNN search within the filing + block formatting + document write in one step.
{
  "accession_number": "0000320193-25-000001",
  "query": "services revenue growth cloud",
  "doc_id": "doc_01JK4...",
  "chunk_limit": 5
}
Use when the user asks to “add a citation”, “insert a filing excerpt”, or “embed SEC data” into their document. If doc_id is provided the block is automatically appended.See Smart Blocks — sec_citation for the full block schema.
Fetches live financial metrics and appends them as a market_data smart block to an editor document.
{
  "ticker": "AAPL",
  "metrics": ["revenue_ttm_usd_millions", "net_margin", "eps_ttm"],
  "doc_id": "doc_01JK4..."
}
Requires cik or ticker. See Smart Blocks — market_data for the full block schema.
Generates a Plotly chart and appends it as an ivory_chart smart block to an editor document. Identical spec format to generate_chart — use this when inserting into a document vs. generating for the answer.
{
  "chart_type": "bar",
  "title": "AAPL Revenue by Segment",
  "spec": { "x": ["Services", "iPhone", "Mac"], "series": [{ "name": "Revenue ($B)", "data": [96.2, 201.0, 29.1] }] },
  "doc_id": "doc_01JK4..."
}
See Visualization for chart spec documentation.

Conversation history

The orchestrator supports stateless tenant-owned chat persistence. Pass prior turns on every request — the server never stores end-user messages.
FieldTypeDescription
chat_idstring | nullOpaque ID echoed back in conversation_state. Route the updated history to the right record.
conversation_historyConversationTurn[]Prior user/assistant turns injected before the current query. Supersedes session_id when both are provided.
A conversation_state event is emitted just before done, containing all user-facing turns including the current exchange. Persist this for the next request.
Request with history
{
  "query": "Now show me the liquidity risk trend too",
  "chat_id": "chat_user-42_abc123",
  "conversation_history": [
    { "role": "user",      "content": "Show me Apple's litigation risk trend over 3 annual filings" },
    { "role": "assistant", "content": "Apple's litigation risk increased from 134 (FY2023) to 167 (FY2025) [S1][S2][S3]. See [Chart 1]." }
  ]
}

SSE event stream

The response is a text/event-stream. Each line: data: <JSON>.
EventWhenKey fields
planAfter planning phasesteps: [{agent, task}]
agent_step runningTool calledtool, args, status: "running"
agent_step doneTool result readytool, status: "done", summary
sourcesBefore first tokensources[], web_sources[], doc_sources[]
chartPer generated chartchart_id, title, chart_type, plotly: {data, layout}
tokenPer output tokentoken
conversation_stateBefore donechat_id, messages[]
doneEnd of stream
errorOn failuredetail

Citation scheme

BracketSource typeComes from
[S1], [S2], …SEC filing chunksresearch_sec_filingsources[]
[W1], [W2], …Web search resultsweb_searchweb_sources[]
[N1], [N2], …News resultsnews_searchweb_sources[]
[D1], [D2], …User-uploaded document chunksretrieve_from_documentdoc_sources[]
[Chart 1], …Generated Plotly chartsgenerate_chartchart events
Transcript and exhibit results inform the answer inline (not separately cited — the speaker, date, and fiscal period are embedded in the text).

Example: Multi-source research

curl -X POST https://api.ivory.finance/v1/rag/orchestrate \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "query": "Show me Apple'\''s litigation risk trend over the past 3 annual filings and correlate with revenue",
    "config": {
      "llm_provider": "openai",
      "llm_model": "gpt-4o"
    }
  }'
Expected SSE stream:
data: {"type":"plan","steps":[
  {"agent":"research_sec_filing","task":"Find Apple 10-K filings FY2023–2025"},
  {"agent":"analyze_filing_risks","task":"Extract litigation risk signals from each filing"},
  {"agent":"generate_chart","task":"Dual-axis: litigation count vs revenue over time"}
]}

data: {"type":"agent_step","tool":"research_sec_filing","args":{"cik":"0000320193","form_types":["10-K"],"after_date":"2023-01-01","max_filings":3},"status":"running"}
data: {"type":"agent_step","tool":"research_sec_filing","status":"done","summary":"Found 3 10-K filings for APPLE INC\n[S1] — Risk Factors (2025)..."}

data: {"type":"agent_step","tool":"analyze_filing_risks","args":{"accession_number":"0000320193-25-000001"},"status":"running"}
data: {"type":"agent_step","tool":"analyze_filing_risks","status":"done","summary":"Risk Score: 7.8/10 | Highest: Litigation (167 mentions, 3.4‰)"}

data: {"type":"agent_step","tool":"generate_chart","args":{"chart_type":"dual_axis","title":"Litigation Risk vs Revenue — $AAPL","spec":{...}},"status":"running"}
data: {"type":"agent_step","tool":"generate_chart","status":"done","summary":"Chart generated (chart_id: chart_1)"}

data: {"type":"sources","sources":[...],"web_sources":[],"doc_sources":[]}
data: {"type":"chart","chart_id":"chart_1","title":"Litigation Risk vs Revenue — $AAPL","chart_type":"dual_axis","plotly":{"data":[...],"layout":{...}}}
data: {"type":"token","token":"## Apple Litigation Risk Trend\n\n"}
data: {"type":"token","token":"Apple's litigation risk increased from **134** (FY2023) to **167** (FY2025) [S1][S2][S3]. See [Chart 1].\n\n"}
data: {"type":"done"}

Consuming the SSE stream (JavaScript)

const response = await fetch('https://api.ivory.finance/v1/rag/orchestrate', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json',
    'Accept': 'text/event-stream',
  },
  body: JSON.stringify({ query: 'Apple risk trend 2023–2025' }),
})

const reader = response.body.getReader()
const decoder = new TextDecoder()
let buffer = ''

while (true) {
  const { value, done } = await reader.read()
  if (done) break

  buffer += decoder.decode(value, { stream: true })
  const lines = buffer.split('\n\n')
  buffer = lines.pop()   // incomplete last chunk

  for (const line of lines) {
    if (!line.startsWith('data: ')) continue
    const event = JSON.parse(line.slice(6))

    switch (event.type) {
      case 'plan':
        showResearchPlan(event.steps)
        break
      case 'agent_step':
        updateProgressIndicator(event)
        break
      case 'sources':
        // event.sources    — SEC chunks
        // event.web_sources — web + news results
        // event.doc_sources — user-uploaded doc chunks
        setSources(event.sources, event.web_sources, event.doc_sources)
        break
      case 'chart':
        Plotly.newPlot(`chart-${event.chart_id}`, event.plotly.data, event.plotly.layout)
        break
      case 'token':
        appendToAnswer(event.token)
        break
      case 'conversation_state':
        await saveConversationHistory(event.chat_id, event.messages)
        break
      case 'done':
        finalizeAnswer()
        break
      case 'error':
        showError(event.detail)
        break
    }
  }
}

Authorizations

X-API-Key
string
header
required

Body

application/json
query
string
required
company_name
string | null
cik
string | null
accession_number
string | null
transcript_doc_id
string | null
exhibit_doc_id
string | null
llm_provider
string
default:bedrock
llm_model
string
default:deepseek.v3.2
session_id
string | null
client_id
string | null
document_ids
string[] | null
config
AgentConfig · object

Optional per-request agent configuration. All fields fall back to server-side environment variables when not provided.

LLM llm_provider: "openai" (default) | "anthropic" | "deepseek" llm_model: e.g. "gpt-4o", "claude-opus-4-6", "deepseek-chat" (DeepSeek V3), "deepseek-reasoner" (R1)

Web / news search search_provider: "google" (default) | "bing" | "duckduckgo" | "custom" search_api_key: API key for Bing or custom provider (not needed for Google/DuckDuckGo) search_custom_url: Base URL for "custom" provider (e.g. "https://search.myco.com")

Database db_connection_string: Connection string for the query_database tool. Supported schemes: postgresql://, mysql://, mariadb://, mongodb://, oracle://, redshift://, snowflake://, databricks://, bigquery:// If omitted, the server DATABASE_URL env var is used.

chat_id
string | null
conversation_history
ConversationTurn · object[] | null

Response

Successful Response