Developer docs

DFX Intelligence · developer docs

Agent research and continuation

How the one-call research tool plans, how deep it reads, how to page and continue an investigation, and how to monitor what changed.

Short answer

Ask research one question in plain words. It plans the reads, answers only from cited claims, says what it could not see, and pages the rest.

What research takes

ArgumentWhat it does
questionThe question in plain language, as an analyst would ask it.
entityOptional. A name or a DFX id (dfx:<graph>:<id>). Without it, entities are read from the question.
workflowOptional hint: credit_by_sponsor, advisor_movement or allocator_to_company. Without it, the workflow is read from the question's words, and plan.workflow_basis says which words decided it.
depthidentify (ids only), screen (the latest date), investigate (default: every date and section the question needs), validate (adds cross-checks and contradictions), monitor (changes since since, or 90 days).
as_of, sinceOptional ISO dates: read the answer as of a date; start a monitoring window.
continuepage.continuation from a previous answer: returns its next page.
detailcompact (under 2 KB) or research (default, the page in full).

What comes back

FieldMeaning
answer.text, answer.basisA direct answer built only from cited rows; basis names the rows it rests on.
answer_stateWhether the question was answered in full or in part (for example OK or PARTIAL).
identitiesThe entity the question resolved to, or near misses under candidates when it did not.
facts, relationships, eventsThe rows, each with claim_type, a cite block and its own date.
claimsOne entry per checkable statement, keyed to its row (see the evidence schema).
planThe workflow and operation chosen, why, and each read with its state.
uncertainty, contradictions, coverage_limits, do_not_concludeWhat the data cannot support, where sources disagree, what the tape does not cover, and inferences the agent should not draw.
withheldWhat this access tier left out, counted, with where the public record prints it when it does.
nextSuggested follow-up calls with their arguments, all on the same profile.
pageIndex, pages, rows on this page and the continuation token.
usageReads used against the depth's budget, latency and bytes. No language model runs inside research (llm_calls 0).

Continue an investigation

Pages carry whole claims: a claim travels with its row and every claim it was derived from, and the answer's basis is always on page 1. Keep the token and ask for the next page only when the agent needs it.

research and continue (Python)

# Python 3.8+, standard library only. One research call, then its next page.
import json
import urllib.request

MCP = "https://exchange-production-9123.up.railway.app/mcp/core?c=docs"


def call(tool, arguments, rpc_id=1):
    body = json.dumps({
        "jsonrpc": "2.0", "id": rpc_id, "method": "tools/call",
        "params": {"name": tool, "arguments": arguments},
    }).encode()
    req = urllib.request.Request(MCP, data=body, headers={
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
        "User-Agent": "my-research-agent/1.0",
    })
    with urllib.request.urlopen(req, timeout=60) as resp:
        result = json.load(resp)["result"]
    return result["structuredContent"]


page1 = call("research", {
    "question": "Which BDCs lend to Auctane, and which of them are current lenders?",
})
print(page1["answer_state"], "|", page1["answer"]["text"][:160])
for row in page1["relationships"]:
    print(" ", row["type"], "|", row["subject"]["name"], "| current:", row.get("current"), "|", row["claim_type"], "|", row["cite"]["url"])
print("page", page1["page"]["index"], "of", page1["page"]["pages"])

if page1["page"].get("continuation"):
    page2 = call("research", {"continue": page1["page"]["continuation"]}, rpc_id=2)
    for row in page2["relationships"]:
        print(" ", row["type"], "|", row["subject"]["name"], "| current:", row.get("current"), "|", row["claim_type"], "|", row["cite"]["url"])
    print("page", page2["page"]["index"], "of", page2["page"]["pages"])

What production returned at . Standard output in full.

OK | At 2026-06-30, 29 BDCs held positions in Auctane Inc on their own schedules of investments; the largest: Blackstone Private Credit Fund ($889.4M of debt at fair
  BDC_SCHEDULE_HOLDING | Blackstone Private Credit Fund | current: True | VERIFIED_FACT | https://www.sec.gov/Archives/edgar/data/1803498/000180349826000048/bcred-20260630.htm
  BDC_SCHEDULE_HOLDING | Blackstone Secured Lending Fund | current: True | VERIFIED_FACT | https://www.sec.gov/Archives/edgar/data/1736035/000173603526000016/bxsl-20260630.htm
  BDC_SCHEDULE_HOLDING | ARES CAPITAL CORP | current: True | VERIFIED_FACT | https://www.sec.gov/Archives/edgar/data/1287750/000162828026050307/arcc-20260630.htm
  BDC_SCHEDULE_HOLDING | Goldman Sachs Private Credit Corp. | current: True | VERIFIED_FACT | https://www.sec.gov/Archives/edgar/data/1920145/000119312526340582/ck0001920145-20260630.htm
page 1 of 3
  BDC_SCHEDULE_HOLDING | ARES STRATEGIC INCOME FUND | current: True | VERIFIED_FACT | https://www.sec.gov/Archives/edgar/data/1918712/000162828026054923/asif-20260630.htm
  SPONSOR_OF_BORROWER | Thoma Bravo | current: True | REPORTED | https://www.thomabravo.com/press-releases/wwex-group-and-auctane-complete-merger-creating-leading-logistics-provider-shipstation-global
page 2 of 3

Saved investigations live on the watch tools (watch_create, watch_list, watch_changes, watch_reevaluate, watch_cancel). They save an account's own objectives and checkpoints, so they need an account; a keyless call to them is refused and nothing is saved.

Monitor: what changed since a checkpoint

depth: "monitor" with since returns the dated changes on one subject in one call. Each event is labelled by novelty: NEW_EVENT happened inside the window, NEWLY_OBSERVED_HISTORICAL was first read inside it but happened before, SCHEDULED_FUTURE has not happened yet.

research, depth monitor

curl -s "https://exchange-production-9123.up.railway.app/mcp/core?c=docs" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"research",
       "arguments":{"question":"What changed in Auctane lender group since April?",
                    "entity":"dfx:pc:d071f992-5b4e-dffb-9f2c-22cb2204622b",
                    "depth":"monitor","since":"2026-04-01","detail":"compact"}}}'

What production returned at . result.structuredContent in full, re-indented.

{
 "dfx": {
  "capability": "research",
  "layer": "dfx_intelligence",
  "version": "1.0.0",
  "answered_at": "2026-10-11T20:48:20.682543+00:00"
 },
 "ok": true,
 "state": "OK",
 "schema": "dfx.research/2",
 "generated_at": "2026-10-11T20:48:20.682543+00:00",
 "answer_state": "OK",
 "detail": "compact",
 "identities": {
  "state": "RESOLVED",
  "subject": {
   "dfx_id": "dfx:pc:d071f992-5b4e-dffb-9f2c-22cb2204622b",
   "name": "Auctane Inc",
   "type": "borrower"
  }
 },
 "answer": {
  "text": "5 changes for Auctane Inc on DFX's tape since 2026-04-01.",
  "basis": [
   "v1",
   "v2",
   "v3",
   "v4",
   "v5"
  ]
 },
 "claims": [
  {
   "claim_id": "clm_12952d56dd54c78131c19b4e",
   "row": "v1",
   "predicate": "EVENT_MATURITY_EXTENDED",
   "claim_type": "REPORTED_FACT"
  },
  {
   "claim_id": "clm_979da0244a3e2273fd4a73ef",
   "row": "v2",
   "predicate": "EVENT_NEW_LENDER",
   "claim_type": "REPORTED_FACT"
  },
  {
   "claim_id": "clm_724fa17bb73be34df32498ff",
   "row": "v3",
   "predicate": "EVENT_NEW_LENDER",
   "claim_type": "REPORTED_FACT"
  }
 ],
 "counts": {
  "facts": 0,
  "relationships": 0,
  "events": 5,
  "claims_total": 5,
  "rows_total": 5,
  "uncertainty": 0,
  "coverage_limits": 1
 },
 "withheld": {
  "access": "anonymous"
 },
 "more": "detail=research returns the facts, relationships, claims and limits in full"
}

A research loop that holds up

  • Start with research; drill into a row with the tool named in next rather than guessing a tool.
  • Quote figures with their as_of date and cite the row's cite.url, never dfx_page.
  • Report withheld counts as withheld, not as absent; report NOT_COVERED and UNKNOWN as limits, not as negatives.
  • Read do_not_conclude before writing a conclusion; it lists the inferences the rows do not support.
  • Honour retry_after on a refusal; refused calls are never charged.

Checked against production on . The same page as Markdown, for agents: /api/data-factory/v1/docs/research.