QEDGE AMFR Copilot

Documentation

User manual

How the workspace fits together: chatting with your research, ingesting documents into the search layer, organizing private and group libraries alongside the shared corpus, and paying for usage with credits.

Getting started

Account & access

Sign in from the Chat page. New registrations must use an email on the admin allowlist, and you need to verify your inbox before you can use the workspace.

  • Standard users see Chat, Ingestion, Skills, and Knowledge in the sidebar (private and group libraries).
  • Admins see the same workspace links plus an Admin section for billing, Shared corpus, Groups, inviting allowlisted users, and MCP servers.

For how data and research content are handled, see Privacy and Terms of use.

Preferences

Settings & models

Open Settings to manage your profile, API keys, and model preferences. The Models card is separate from profile fields — save each card with its own button.

Model preferences

Choose which Claude models power your research runs. Each dropdown offers three models —Claude Sonnet 4.6, Claude Opus 5, and Claude Fable 5 — plus Workspace default to inherit the deployment baseline.

  • Agent model — Runs General Research Copilot and packaged agents (tool use, subagents, and research).
  • Composer model — Writes the final user-facing answer after the agent finishes its research phase.

When you pick a specific model, the card shows Anthropic list pricing per million tokens (input, cache writes, cache hits, and output). Your workspace may apply additional platform charges on top; see Credits & billing.

When settings take effect

  • Interactive chat — Applies to both General Research Copilot and packaged agents when you chat in the browser or via the Chat API on a thread. The next turn uses your saved agent and composer models. Changing the agent model starts a fresh agent session on that thread (the prior session fingerprint no longer matches). Changing only the composer model does not reset the agent session.
  • Scheduled tasks — Each schedule captures MCP, skills, and model choices when the task is created. Later runs use that frozen snapshot, not your live Settings. See Scheduled tasks.
  • Chat APIPOST /copilot/api/ask has no per-request model fields. API runs use the models stored on your profile; update them with PATCH /auth/profile (see Chat API: models and profile).

Assistant

Chat & threads

The chat UI is built around threads — each thread keeps its own title, message history, and uploaded attachments.

  1. Use New chat to start a fresh thread. The first message you send (or file you attach) seeds the conversation.
  2. Search chats filters the thread list by titles and message content.
  3. Open a thread from the list; use Delete on a row to remove a thread when you no longer need it.
  4. Attach files in the composer to upload documents into the current thread before or while you ask questions. Uploads appear in the transcript for that thread.
  5. Submit with the Ask button, or press Ctrl+Enter (Windows/Linux) or ⌘+Enter (macOS) from the message box.
Knowledge scope. Use the Knowledge control in the composer to limit what indexed papers the assistant can retrieve: All accessible (default), Shared library, My library, or Selected groups (pick one or more group libraries). Standard users see public shared corpus papers, their own private uploads, and group libraries they belong to. Admins with All accessiblecan retrieve across the full workspace (including other users' private papers).

While the model works, you may see agent steps, tool activity, and downloadable artifacts (tables, plots, exports) tied to the assistant reply. Failed answers can be retried from the error state when available.

Concurrent threads and live runs

You can run several chats at the same time. Start a long answer in one thread, switch to another in the sidebar, and send a new question there — each thread keeps its own in-flight run on the server. Switching threads does not cancel the run you left behind.

  • One answer per thread — While a thread is working on a reply, the composer for that thread is disabled until the assistant message finishes (or fails). Start another question in a different thread if you want parallel work.
  • Live badge — Threads with an answer still in progress show a live label in the chat list so you can see which conversations are still running when you are looking at a different thread.
  • Background and resume — Runs continue on the server if you close the tab, refresh, or navigate elsewhere in the workspace. When you open that thread again, the UI reconnects to the in-flight message and streams any steps you missed, then keeps updating live until the answer completes.
  • Delete — You cannot delete a thread while it is live; wait for the run to finish or fail first.

Integrations

Connections

Connections links your personal accounts to Financial Research Copilot so you can pull files in, push chat output out, and wire packaged agents to external systems. Open the Connections panel from the Chat header (and from Ingestion when cloud import is available). Each link is per user — not shared across the workspace.

A connector shows Connected when your account is linked, Not connected when the server supports it but you have not signed in yet, or Unavailable when the workspace has not configured that provider (ask an admin to enable it on the backend).

  • Feishu Docs — OAuth sign-in. Use Send to… in the chat header to export the latest answer or the full thread as a Feishu document. On generated .md / .markdown artifacts, use Save to Feishu Docs.
  • Notion — OAuth sign-in. Same Send to… options for latest answer or full thread as a Notion page. Markdown artifacts can be saved to Notion from the Generated files list under an assistant reply.
  • Synology NAS — Sign in with your DSM URL, account, and password (and OTP if required).
    • Attach, ingest, and save — In chat, Attach From Synology NAS adds a file from shared folders to the current thread. On Ingestion, From Synology NAS queues PDF / DOCX / XLSX / PPTX files or whole folders for indexing. Under an assistant reply, Save to… uploads generated artifacts to a folder you choose.
    • Synology MCP (General Research Copilot) — With NAS connected, include Synology in the composer Tools list (Auto or Manual). The run can list shares and folders, read documents on the NAS, mount larger files into the agent environment for analysis, write or upload outputs, and create folders — without downloading everything to your laptop first.
  • GitLab — Sign in with your GitLab host URL and a personal access token with api scope, then link a project and working branch you want the workspace to treat as your default repository.
    • GitLab MCP (General Research Copilot) — With GitLab connected and a project linked, include GitLab in the composer Tools list (Auto or Manual). The run uses MCP tools on that linked project: repository info, list paths, read text files, commit a file, create a branch, and open a merge request.
    • Strategy Improver — This packaged agent always attaches GitLab MCP and will not start until a project and branch are linked in Connections. It uses the same GitLab connection as above; see Strategy Improver for repo layout, backtests, and prompting.
Connections vs MCP. Feishu and Notion are connection-only (send/save exports). GitLab and Synology are hybrid: Connections holds your account (and for GitLab, which repo/branch), while MCP & tools is how the General agent invokes them when you select those servers. Other MCP entries (Wind, Firecrawl, SEC EDGAR, custom URLs, etc.) are workspace services and do not use the Connections panel.
Tip: Switch the linked GitLab project in Connections when you move to a different repo; re-link the branch if your default integration branch changes. Disconnect any connector when you no longer want the workspace to use that account.

Integrations

MCP & tools

MCP (Model Context Protocol) connects the assistant to external tool servers your workspace exposes — data APIs, search, or custom backends. Admins register each server (name, URL, description, on/off) under Admin → MCP servers. Only servers marked enabled appear in chat.

In the chat composer, with the General Research Copilot agent (see Packaged agents), set Tools to:

  • Auto — the workspace picks relevant MCP servers from your question before the run (you’ll see a preview of the match while you type).
  • Manual — expand MCP servers and choose exactly which enabled servers to attach to the next message.

GitLab and Synology also appear in the Tools list when enabled, but they only activate for your user after the matching Connection is set up (GitLab also requires a linked project and branch). Selecting GitLab without a linked project, or Synology without a connected NAS, will not attach working MCP tools for that server. For GitLab, the backend injects your linked project path and branch into the prompt so the model knows which repository to use.

Packaged agents use a fixed data-source workflow (not the general MCP picker). The Tools control is locked to auto for those agents; use the General agent for full MCP auto/manual control, including optional GitLab and Synology when connected.

Reuse

Slash skills

Skills are reusable capabilities you invoke from chat with a slash id (for example /my_skill). Manage them from the Skills page; in the message box, type / to open suggestions for enabled skills.

  • Instruction skills — Free-form instructions the model follows when you trigger that skill. Any signed-in user can create and edit their own. Admins can set each skill’s visibility (for example public) so others in the workspace can reuse it; standard users' new instruction skills stay private.
  • Claude file skills — A slash alias mapped to a native Claude file-based skill: either an entry from Anthropic's catalog or a custom skill package (ZIP) uploaded for the workspace. Creating, editing, and catalog wiring is admin-only; everyone can use enabled aliases in chat once they exist.
Packaged agents do not use the general slash-skill workflow. Switch to General Research Copilot if you need custom instruction or file skills on a message.

Automation

Scheduled tasks

Use /schedule in a chat thread to run the same agent prompt on a recurring cadence. Schedules use your browser timezone and bill credits on each run like a normal chat turn.

  • /schedule every friday 9am Append latest flows to the dashboard
  • /schedule list — list tasks bound to this thread
  • /schedule stop task_abc123 — disable a task

Scheduled runs reuse the thread session when possible. The agent sees thread uploads, prior outputs, and your stored prompt each time and decides which files to continue from.

Frozen configuration. When you create a schedule, the platform snapshots your MCP mode, enabled MCP servers, slash skills, and agent/composer models at that moment. Subsequent runs keep that configuration even if you change Settings later. Create a new schedule to pick up new model or tool choices.

Agents

Packaged agents

The Agent control in the composer selects which research workflow backs the thread. General Research Copilot is the default: full knowledge scope and MCP tools in Auto or Manual mode, plus slash skills.

The other options are curated packaged agents:

  • Market Researcher — Sector primers, landscapes, comps, and thematic idea lists.
  • Earnings Reviewer — Earnings transcripts, filings, model updates, and note drafts.
  • Model Builder — DCF, LBO, three-statement, and comps-backed modeling artifacts.
  • Company Model Builder — Company-specific operating model, IRR output, sensitivities, and analyst gap memo.
  • Strategy Improver — Multi-agent workflow to diagnose, backtest, research, and verify improvements to code in a linked GitLab repository. See Strategy Improver for setup and prompting.
  • Compounder Identifier — Score whether a company can dominate, why, and for how long — CA matrix, moat sources, anti-fragile rating.

For packaged agents, the UI uses a preset tool stack (for example YFinance, Wind, DefeatBeta, SEC EDGAR) instead of the workspace MCP picker. Knowledge and manual MCP selection are disabled so the agent can follow its own retrieval and data rules.

If you later want follow-ups against ingested knowledge — changing Knowledge scope, using slash skills, or choosing MCP servers manually — switch the Agent control to General Research Copilot and continue in the same thread. Prior turns stay in the transcript so you do not need a new chat.

Packaged agent

Strategy Improver

Strategy Improver is a multi-agent research lead for production trading strategies checked into a GitLab repository. It scopes the strategy from code, runs Docker Compose backtests, searches SSRN for hypotheses, challenges ideas, verifies patches with A/B backtests, and opens a merge request only after you confirm in chat.

Select Strategy Improver from the Agent control in the chat composer. Like other packaged agents, Knowledge, manual MCP selection, and slash skills are disabled — the agent uses its fixed stack: GitLab, Firecrawl (SSRN search), and Backtest Runner.

Before your first run

  1. Connect GitLab — Follow Connections: connect with a token that has api scope, then link the GitLab project and working branch this agent will use (usually main). Strategy Improver refuses to start until that link exists.
  2. Prepare the repo for backtests — See Strategy repo layout below. The backtest runner reads manifests and compose files from Git; it does not guess entrypoints from main.py alone.
  3. Ensure the FR Copilot host can run Docker — Backtests execute on the workspace backend VM (where FR Copilot runs), not on your GitLab CI deploy target. That host needs the Docker CLI, access to /var/run/docker.sock, and images/env your backtest compose file expects.

Strategy repo layout

Production strategies typically deploy from one compose file via your GitLab pipeline (for example deployment/docker-compose.yml on the strategies VM). Strategy Improver uses a separate, self-contained backtest compose file so production and backtest stacks stay clearly apart.

Minimum files the agent expects in the linked repo:

  • deployment/docker-compose.backtest.yml — Self-contained Compose stack for backtests only: strategy services (for example frcopilot-pairs-trading), dependency services (data/nginx helpers), networks, and volumes. Not the same file your pipeline uses for live trading unless you intentionally share one host.
  • deployment/backtest_registry.yaml — Maps each strategy folder (for example pairs) to a compose service, depends_on, backtest command, and patch_mounts (repo path → container path for candidate patches).
  • deployment/.env — Environment variables referenced by the backtest compose file (API keys, image tags, data paths). The runner checks this out alongside compose.
  • Strategy source trees — For example pairs/, letf/, each with the entrypoint and config files listed in the registry.

Point the registry at your backtest compose file (top-level compose_file: deployment/docker-compose.backtest.yml). Each strategy entry should set mode: compose_run, compose_service, backtest_command (for example [python, main.py]), and patch_mounts for every .py file the verifier might edit during candidate runs.

Production vs backtest hosts. Your GitLab pipeline may docker compose up -d on a strategies VM. The backtest runner instead checks out files from GitLab and runs docker compose -f deployment/docker-compose.backtest.yml on the FR Copilot backend VM. Those can be the same machine or different; the runner always starts its own backtest containers locally — it does not attach to already-running production containers on another host.

How a run works (multi-agent)

One chat turn can take a long time (scope → baseline backtest → up to three improvement rounds → proposal). You will see agent steps, subagent activity, and JSON artifacts in the thread. Typical stages:

  1. Scope — Reads the repo via GitLab; writes scope.json (strategy path, entrypoint, config, evidence-backed strategy_profile).
  2. Baseline backtest — Runs the registry backtest with no patches; interprets stdout into baseline_backtest.json and diagnosis.json.
  3. Improvement loop (up to 3 rounds) — SSRN research (Firecrawl), idea challenging, then verifier backtests with ephemeral file patches (same command, patched files bind-mounted).
  4. Proposal — A confirmation card in chat with baseline vs candidate metrics and file changes. Nothing is pushed to GitLab yet.
  5. Your approval — Reply to confirm (or ask for revisions). Only then does the agent create a branch, commit, and open an MR.

How to prompt

Name the strategy folder, branch context, and improvement goal in the first message. Good prompts are specific about what to improve and which strategy:

  • Improve the pairs strategy on main. Focus on reducing max drawdown without hurting Sharpe. Start from the current production config.
  • Run Strategy Improver on letf. I want ideas for better timing around rebalance days; reject changes that need data we do not have in the repo.
  • Scope-only pass on adr — confirm entrypoint and backtest registry before we run a full improvement loop.

Follow-ups on the same thread reuse the managed-agent session (context and staged files). After a long run is interrupted or credentials refresh, you can send continue from where you left off or approve/reject the proposal card.

To revise the plan before publish, reply with constraints (for example do not change position sizing or rerun verification with a tighter stop). The orchestrator may dispatch another verifier round before re-issuing a proposal.

Backtests & concurrency

  • The runner returns raw stdout; the agent extracts metrics. If a backtest fails, check the job logs in the thread and your registry/compose setup.
  • Plan for one active backtest per FR Copilot backend host — parallel runs on the same VM can race on shared Docker container names.
  • Long runs consume credits like any packaged-agent turn (orchestrator + subagents). They follow the same live and background-resume behavior as other chats (see Chat & threads). The proposal card appears in the transcript when the run finishes.
Tip: Download JSON artifacts from the thread when you want the full scope.json or verification output. Inline file previews in the answer show compose-generated summaries when the model embeds them; use artifact download for complete files.

Data plane

Document & knowledge ingestion

There are several ways content enters the system — pick the one that matches your goal:

  • Chat attachments — Upload from the chat composer to work with files in a single thread. This is ideal for ad-hoc analysis alongside a conversation. The composer also accepts Markdown and JSON for thread-only use; indexing into your knowledge libraries uses the ingestion formats below.
  • Ingestion → Local documents — On the Ingestion page, open the Local documents tab, choose one or more files, then queue ingestion. Supported formats are PDF (.pdf), Word (.docx), Excel (.xlsx), and PowerPoint (.pptx) — modern Office Open XML only, not legacy .doc / .xls / .ppt. Import from Synology via From Synology NAS after you connect your NAS; use the file-type filter when browsing folders to narrow the list. You must be signed in. Each file becomes its own run; use the Runs tab for live status and per-paper stats. Optional toggles include Dry run (parse without writing to the vector store) and Only ingest new papers (skip content already present). Use Sharing settings on the ingest form to target Private (only you), Group (members of selected knowledge groups), or Shared (workspace-wide shared corpus — admins only). Standard users can ingest to Private or Group for groups they belong to; they cannot publish to the shared corpus.
  • Ingestion → ArXiv discovery — On the same Ingestion page, open the ArXiv discovery tab to search arXiv, review candidates, and queue ingestion. Any signed-in user can use this workflow with the same sharing settings and optional toggles as local uploads.
  • Ingestion → Broker reports — On the Ingestion page, open the Broker reports tab to pull sell-side research for a company and its comparable peers. Enter a primary company (name or ticker), add comparable companies — or press Suggest comps to have the assistant propose a peer set you can edit — then choose your report sources (foreign brokers, domestic brokers, or both; domestic coverage is CICC, CITIC, CSC, and Huatai) and a date range (defaults to the last year). Each report is fetched, tagged with its publication date, and indexed for search and chat like any other document, with the same Sharing settings, Dry run, and Only ingest new papers options. Use the Runs tab for live status and per-report stats.
  • Ingestion → Stock pools (admins only) — A stock pool is a saved watchlist that re-ingests itself on a schedule, so a monthly coverage refresh does not have to be run by hand. Because a coverage list is usually hundreds of names, a pool is built by uploading a JSON file rather than adding companies one at a time. Pick a schedule (cron preset plus timezone) and a date window: Previous month (the month that just closed — the usual choice), Month to date, or a rolling Trailing days. On each run, Gangtise entries are discovered over that date range and NAS folders are filtered by the date in each filename; files whose filename carries no parseable date are never picked up. Pools are shared across admins — every admin sees and can edit every pool — and standard users never see the tab. Use Preview window to check which files a NAS folder would pick up before enabling a schedule, Run now for an immediate run that leaves the cron schedule untouched, and Pause to stop it firing. Every run creates ordinary ingestion jobs, so they appear in the Runs tab under a Stock pool runs summary that links to its child jobs. If a pool's owner later loses admin, the next run aborts and disables the pool rather than quietly ingesting to a different destination.
  • The watchlist file. Upload a JSON array of rows, or an object with an entries array alongside the pool's settings. A company row takes name — a bare string works as shorthand — plus optional code (the Gangtise gtsCode), scope (foreign, domestic, or both; blank inherits the pool's broker toggles), and keywords (extra search terms when Gangtise has no exact security). Optional comparables (also comps / peers) lists peer companies ingested in the same broker job as that row — the same idea as comps on the Broker reports tab, not a second watchlist column type. A NAS row takes path and an optional file_type. Column names are forgiving (company, ticker, folder and similar all work) since the file usually comes out of a spreadsheet. On upload each row is checked and reported back: Resolved (matched a Gangtise security), Keyword (no exact match, so it will search the knowledge base by name instead), NAS folder, or Rejected. Rejected rows are skipped; the rest import.
  • Name vs code. The code is the Gangtise security identifier (600519.SH, TSM.N, 00700.HK) and it is what actually drives discovery; the name is a label. Lookups are a direct Gangtise search with no AI canonicalization, and only an exacthit on a ticker or on the security's own display name counts — so supply the name Gangtise itself shows (贵州茅台, not Kweichow Moutai), or the code. A near-miss is reported with the closest candidate so you can paste its code into the row. If a row carries both and they disagree, the code wins and the row is flagged as a Mismatch — nothing is mis-ingested, but the pool would otherwise show one company while ingesting another. Rows are relabelled with the canonical Gangtise name so the list stays consistent.
  • Companies Gangtise does not list. Some issuers have no security in Gangtise at all — PUMA and Adidas, for instance. Searching them does not fail cleanly: PUMA scores against Puma Biotechnology (PBYI.O) and Puma SE against 西南证券, both entirely different companies. That is why only exact matches become a code; everything else becomes a Keyword row that searches the knowledge base by name and never touches security resolution. Pool such names by leaving code out and listing every term worth searching under keywords — e.g. {"name": "Puma SE", "keywords": ["Puma SE", "PUMA", "Puma"]}, since the short ticker usually has better recall than the formal name and the results of both are combined.
  • NAS folders are the source for a period, not for a stock. A folder row ingests everyreport under that path whose filename date falls in the window — filenames are never matched against a company name. Point rows at folders that hold one desk's or one period's output, not at a shared archive. The folder path must match the NAS exactly, so use Browse NAS folder on an existing pool to copy a real path rather than typing one into the file.

Runs. The Runs tab lists your ingestion jobs with live status. Use the status tiles (queued / running / succeeded / failed) to narrow the list, and the search box to match titles, company names, filenames, or other job text. Clear filters restores the recent-runs view. Select a row for per-document summaries and errors.

SSRN PDFs. Upload a working paper you downloaded from SSRN through Ingestion → Local documents (or attach it from chat for a single thread). The pipeline recognizes common filenames such as ssrn-4098475.pdf. In chat, mention the paper in your question or use the rag knowledge-base tool to retrieve from ingested SSRN uploads.

Groups. Admins create groups and membership under Admin → Groups. A group gates both group knowledge libraries and threads shared with that group. Until you belong to at least one group, Group sharing is unavailable on ingest. Ask an admin to add you, or create a group if you have admin access.
Shared corpus. Publishing to the workspace-wide shared index (ingest Shared or maintaining Admin → Shared corpus) remains an admin responsibility. Standard users ingest to private or group libraries only.

Library

Knowledge libraries & access

Indexed research lives in one of three visibility tiers. Each tier controls who can retrieve a paper in chat and where it appears in the UI:

  • Private — Only you (and workspace admins) can search it. Listed under KnowledgePrivate.
  • Group — Members of the assigned groups can search it. Listed under KnowledgeGroups. You can share the same document with multiple groups at ingest time.
  • Shared — Everyone in the workspace can search it. This is the shared corpus (public knowledge base). Browse it under Admin → Shared corpus (admins can also delete papers there).

The Knowledge page is for private and group libraries only — not the shared corpus. Use it to review what you have indexed, delete papers you own (or any paper if you are an admin), and change sharing on a paper (move between private, group, or shared) when you have permission. The shared-corpus browser uses the same search controls.

Keyword vs Semantic search. Library browsers offer a Keyword | Semantic toggle:

  • Keyword — Filter by source (arXiv, Document, YouTube), title words, and partial paper ID, then paginate. Broker reports and audio recordings are stored with source Document.
  • Semantic — Enter a natural-language query and press Search (results do not update while typing). The list returns the top matches by embedding similarity (not a paginated browse), with an optional source filter. Clear search returns you to the normal browse list while staying on the Semantic tab.
Tip: Match ingest sharing to how you want others to access a document, then use chat Knowledge scope — for example My library, Selected groups, or All accessible — so answers pull from the right libraries.
Admins. Create groups and manage membership under Admin → Groups. Maintain workspace-wide shared papers under Admin → Shared corpus. Admins can ingest or reclassify to Shared and read all tiers in chat when scope is All accessible.

Developers

API access

Mint a personal API key in Settings to call programmatic routes as your account. One key covers three route families:

  • Ingestion (/ingestion/...) — index documents into knowledge libraries.
  • Chat (/copilot/api/...) — ask questions, attach files, and collect answers plus output artifacts.
  • Account (/auth/...) — read and update profile fields, including agent and composer models used by chat runs.

API keys

  1. Open SettingsAPI keys Create keywith a label (for example "Ingestion pipeline" or "Research bot").
  2. Copy the key immediately — the full value (starting with qrc_) is shown only once.
  3. Revoke unused keys from the same page. Revoked keys return 401.
Tip: Treat a key like a password. Keys persist when the workspace uses a database; in local dev without one they reset on restart.

Authentication

Send the key on every request: Authorization: Bearer qrc_.... The browser UI uses your session cookie instead — you do not need a key when using the web app.

Base URL

Programmatic routes are served by the backend API root, not the Next.js app shell. Build full URLs as:

https://your-domain/api + route path — for example https://frcopilot.qedgeam.com/api/copilot/api/ask.

  • /api is the FastAPI backend mount — always include it before route paths such as /ingestion/..., /copilot/api/..., or /auth/....

Curl examples below use the API_BASE placeholder for that backend root (for example https://frcopilot.qedgeam.com/api).

Async jobs

Ingestion uploads and chat asks are job-based: the server returns 202 with a job_id, then you poll until status is succeeded or failed. Account routes such as /auth/profile respond synchronously.

  1. Submit the upload or ask request and record the job_id.
  2. Poll the ingestion or chat job endpoint until the job finishes.
  3. Read results from the final payload (ingest summaries, or chat answer + artifacts).

Job status values: queuedrunning succeeded / failed. You only see your own jobs.

Ingestion API

Index documents into your knowledge libraries under /ingestion.

Endpoints

POST/ingestion/uploads

Body: raw document bytes (PDF, DOCX, XLSX, or PPTX). Headers: X-Filename and the document Content-Type. Query options: visibility, group_ids, dry_run, and only_new.

POST/ingestion/youtube

Body: JSON with urls (YouTube video or playlist URLs / IDs). Optional JSON fields: visibility, group_ids, dry_run, and only_new. No X-Filename header is needed.

POST/ingestion/audio

Body: raw audio bytes. Headers: X-Filename and the audio Content-Type. Query options: visibility, group_ids, dry_run, and only_new.

POST/ingestion/broker-reports

Body: JSON with primary (company name or ticker) and optional comparables, include_foreign, include_domestic, domestic_brokers (subset of CICC, CITIC, CSC, Huatai), date_from, date_to, and limit. Query options: visibility, group_ids, dry_run, and only_new. Requires broker-research credentials to be configured (otherwise returns 503).

POST/ingestion/broker-reports/suggest-comps

Body: JSON with primary. Returns a suggested, editable set of comparable companies (name + code) to feed into /ingestion/broker-reports.

GET/POST/ingestion/pools · /ingestion/pools/{pool_id}

Admins only (standard keys get 403). Manage scheduled stock pools. Create body: name, cron_expr, timezone, window_mode (previous_month / current_month / trailing_days), trailing_days, the ingest defaults (visibility, group_ids, dry_run, only_new, include_foreign, include_domestic, domestic_brokers, limit — max reports per Gangtise company job; NAS folders ignore it), and an optional entries array. PATCH updates any of those; DELETE removes the pool and its runs.

POST/ingestion/pools/import

Create a whole pool from a JSON watchlist — the practical way to load hundreds of names. Body: the pool settings plus an entries array (a bare array is also accepted, and creates a pool with default settings). Company rows take name (a bare string works), optional code, scope (foreign / domestic / both, blank inherits the pool default), keywords, and optional comparables (peer companies for the same broker job); NAS rows take path and optional file_type. Names are resolved directly against Gangtise, falling back to a keyword search on a miss. The response reports every row's outcome (code, keyword, folder, error); rejected rows are skipped rather than failing the import. Add ?dry_run=true to get the report without creating anything. Use POST /ingestion/pools/{pool_id}/entries/import (with ?replace=true to swap the list wholesale) to load the same file into an existing pool.

POST/ingestion/pools/{pool_id}/entries

Add one target to a pool. Body: kind (gangtise_code / gangtise_keyword / synology_folder) plus code, keyword + keywords, or synology_path + synology_file_type. Optional scope (foreign / domestic / both) overrides the pool broker toggles for this row, and comparable_refs ingests peer companies alongside the primary. PATCH / DELETE on /entries/{entry_id}.

POST/ingestion/pools/{pool_id}/run · GET/runs · /preview

POST /run queues an immediate run (202 with a run_id) without moving the cron schedule, and returns 422 if the pool has no enabled entries. GET /runs returns run history including the child job_ids to poll via /ingestion/jobs/{job_id}. GET /preview resolves the current window and lists the NAS files each folder entry would match, with no writes.

GET/ingestion/jobs

List recent jobs. Optional query: limit (1–100), q (case-insensitive match on query text, kind, owner, or result titles), status (queued / running / succeeded / failed), and kind (ingestion job kind). Standard users only see their own jobs.

GET/ingestion/jobs/{job_id}

Poll for status and per-document ingest summaries.

GET/ingestion/corpus/papers · /ingestion/my-corpus/papers · /ingestion/group-corpus/papers

Paginated keyword browse for the shared corpus, your private library, or group libraries. Optional filters: source, title, paper_id, abstract, doi, limit, offset. Group listing also accepts group_ids (comma-separated).

POST/ingestion/corpus/papers/search · /ingestion/my-corpus/papers/search · /ingestion/group-corpus/papers/search

Semantic (embedding) search within the same scopes. JSON body: query (required), optional source, limit (default 40, max 100). Group search may include group_ids. Returns top papers by similarity with optional score and match_excerpt — not offset-paginated.

Parameters by endpoint

  • /ingestion/uploads — raw document body; X-Filename header; optional query parameters visibility, group_ids, dry_run, and only_new.
  • /ingestion/youtube — JSON body with required urls; optional JSON fields visibility, group_ids, dry_run, and only_new.
  • /ingestion/audio — raw audio body; X-Filename header; optional query parameters visibility, group_ids, dry_run, and only_new.
  • /ingestion/broker-reports — JSON body with required primary and optional comparables, include_foreign, include_domestic, domestic_brokers, date_from, date_to, and limit; optional query parameters visibility, group_ids, dry_run, and only_new.
  • /ingestion/jobs — optional limit, q, status, and kind.
  • Corpus /papers (GET) — keyword filters and pagination; /papers/search (POST) — semantic query + optional source / limit (and group_ids for group corpus).
  • visibility accepts private, group, or public. Omit it to get your role’s default — the shared corpus for admins, your private library for everyone else (see visibility below).
  • group_ids is required when visibility=group. For raw upload/audio endpoints it is comma-separated in the query string; for YouTube it is a JSON list in the request body.

Visibility (where documents land)

Maps to the same tiers as the UI (see Knowledge libraries & access):

  • Private (visibility=private) — only you (and admins).
  • Group (visibility=group + group_ids) — members of those groups. Standard users must belong to each group or get 403.
  • Shared (visibility=public) — workspace corpus. Admins only; a standard-user key asking for it gets 403.
Mind the default. Omitting visibilitymeans “use my role’s default”: an admin key publishes to the shared corpus, a standard-user key lands in private. Naming a destination you may not write to is an error rather than a silent redirect, so a standard-user key that passes visibility=public gets 403 and nothing is ingested. Pass visibility explicitly whenever the destination matters.

Batch uploads

You can submit many uploads concurrently, but the backend processes with limited concurrency. Collect each job_id, then poll — do not assume immediate parallel completion. Re-uploading identical content is safe (de-duplicated).

Poll responsibly: space checks 5-10 seconds apart and back off when many jobs are still pending. For large batches, sweep with GET /ingestion/jobs?limit=N once per cycle instead of hitting /ingestion/jobs/{job_id} for every outstanding job. A job stuck in queued with nothing running for a long time is likely orphaned — resubmit it rather than polling indefinitely.

Example — private upload

curl -X POST "{{API_BASE}}/ingestion/uploads?visibility=private" \
  -H "Authorization: Bearer qrc_..." \
  -H "X-Filename: report.pdf" \
  --data-binary @report.pdf

Example — poll job

curl "{{API_BASE}}/ingestion/jobs/JOB_ID" \
  -H "Authorization: Bearer qrc_..."

Example — semantic search (private library)

curl -X POST "{{API_BASE}}/ingestion/my-corpus/papers/search" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{"query":"gold mining cost curve","source":"upload","limit":20}'

Chat API

Run headless chat under /copilot/api. API chats are real threads owned by your account — they appear in the browser Chat list. Reusing a thread_id restores the agent session and any files attached to the thread.

Models and profile

Chat runs resolve agent and composer models from your stored profile — POST /copilot/api/ask has no per-request model fields. Update models before asking, or rely on what you saved in Settings (see also Settings & models):

Account endpoints

GET/auth/model-presets

List selectable agent and composer models, workspace defaults, and Anthropic list pricing per model.

PATCH/auth/profile

Update profile fields. Set agent_model and/or compose_model to a preset id (e.g. claude-sonnet-4-6) or null to revert to workspace default.

Example — set models for subsequent chat jobs

curl -X PATCH "{{API_BASE}}/auth/profile" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{"agent_model": "claude-opus-5", "compose_model": "claude-sonnet-4-6"}'

Chat endpoints

POST/copilot/api/threads

Create a thread (optional title). Use before attaching files.

POST/copilot/api/threads/{thread_id}/uploads

Attach a file (raw body + X-Filename). Recorded on the thread; auto-attaches on every later turn.

GET/copilot/api/threads/{thread_id}

Thread sharing state, pasteable share_url, and latest_job (same poll object as /jobs/{job_id}).

PATCH/copilot/api/threads/{thread_id}/sharing

Share the thread. Body: visibility (private / group / public / internet) and group_ids when visibility=group. Returns share_url.

DELETE/copilot/api/threads/{thread_id}

Soft-archive the thread (same as the browser Chat delete). Returns 204. The row stays; it leaves the sidebar. 404 if missing, already archived, or not yours.

GET/copilot/api/skills

List slash skills visible to your account (skill_id, name, kind, enabled). Optional query: q to filter.

POST/copilot/api/ask

Ask a question. Body: question (required; may include /skill_id tokens), thread_id, title, then optionally agent_slug, mcp_mode, mcp_servers, knowledge_scope.

GET/copilot/api/jobs/{job_id}

Poll for answer and artifacts[] (each with download_url) when succeeded.

POST/copilot/api/jobs/{job_id}/cancel

Cancel a queued or running job. Queued jobs become cancelled immediately. Running jobs become cancelling while the agent is interrupted and the run drains to idle; then the job and message are cancelled. Idempotent — a no-op on jobs that already finished.

GET/copilot/api/jobs

List your recent chat jobs. Optional thread_id filters to one thread.

GET/copilot/api/jobs/{job_id}/artifacts/{file_id}/download

Download an output artifact. Add ?inline=true to view inline.

Agent, MCP, and knowledge scope

Optional POST /copilot/api/ask fields (same semantics as the browser composer). Order below reflects how you typically set them:

  1. agent_slug — packaged agent (e.g. company-model-builder). Omit for General Research Copilot. Packaged agents use a fixed MCP allowlist; mcp_mode / mcp_servers are ignored.
  2. mcp_mode + mcp_servers mcp_mode: "auto" (default) lets the router pick enabled servers from your question; mcp_mode: "manual" attaches the slugs you list (lowercase), e.g. edgar-13f, rag, firecrawl, yfinance, gitlab.
  3. knowledge_scope — which knowledge libraries the RAG MCP may search when rag is attached (auto or manual). Defaults to "all". Has no effect unless RAG is in the MCP set. Mirrors the composer Knowledge control (see Chat & threads).

Accepted knowledge_scope values:

  • "all" — all tiers your account can access (shared, private, and your groups; admins see more).
  • "public" — shared corpus only.
  • "private" — your private library only.
  • JSON string for selected groups (see example below). Optionally add "public": true and/or "private": true to mix tiers with specific groups.

Example — group-scoped knowledge_scope value

{"mode":"custom","group_ids":["GROUP_ID"]}

Example — EDGAR 13F MCP

curl -X POST "{{API_BASE}}/copilot/api/ask" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{
    "question": "Show Bill Ackman MSFT holdings in Q1 2026",
    "mcp_mode": "manual",
    "mcp_servers": ["edgar-13f"]
  }'

Example — RAG MCP with shared corpus only

curl -X POST "{{API_BASE}}/copilot/api/ask" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{
    "question": "What do broker reports say about MSFT?",
    "mcp_mode": "manual",
    "mcp_servers": ["rag"],
    "knowledge_scope": "public"
  }'

Slash skills

The chat API supports the same slash skills as the browser composer. There is no separate skill_ids field — prefix question with /skill_id (must be followed by whitespace or end of string). Use GET /copilot/api/skills to discover ids your API key can invoke.

  • Instruction skills — inject custom instructions for that turn.
  • Claude file skills — workspace file-based / native catalog skills.
  • Packaged agent_slug values ignore slash skills (same as the UI).
  • Unknown or disabled skills fail the background job — poll error on the job (validation runs after 202, not at submit time).

Example — list skills, then invoke one

curl "{{API_BASE}}/copilot/api/skills" \
  -H "Authorization: Bearer qrc_..."

curl -X POST "{{API_BASE}}/copilot/api/ask" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{"question": "/summarize What changed in the latest CPI print?"}'

Thread status, sharing, and delete

After POST /copilot/api/ask, poll the run until it finishes, then optionally share the thread and copy a pasteable link into a note or doc. This is the same Share hub as the browser Chat page. Delete is the same soft-archive as the Chat sidebar.

  • Job statusGET /copilot/api/jobs/{job_id} returns queued / running / cancelling / succeeded / failed / cancelled, plus answer and artifacts when done. Cancel an in-flight job with POST /copilot/api/jobs/{job_id}/cancel. GET /copilot/api/jobs?thread_id=... lists jobs on one thread.
  • Thread statusGET /copilot/api/threads/{thread_id} returns sharing fields and latest_job (same object as job poll) when you have a thread_id but not the job_id.
  • SharePATCH /copilot/api/threads/{thread_id}/sharing with visibility and, for groups, group_ids. The response share_url is the link to put in a document.
  • DeleteDELETE /copilot/api/threads/{thread_id} soft-archives the thread (204, empty body). Same as the browser Chat delete: the row stays, it leaves the sidebar, and local attachment copies are dropped. 404 if the thread is missing, already archived, or not yours. Public/shared links stop resolving once archived.

Accepted visibility values (thread sharing, not ingest):

  • private — owner only. share_url is null.
  • group — members of group_ids (you must belong, unless admin). Link is /chat/shared/{thread_id} (sign-in required).
  • public — whole workspace ("shared"). Same signed-in /chat/shared/{thread_id} link.
  • internet — anyone with the link, no sign-in. Mints a revocable public_share_slug and returns /chat/public/{slug}. Switching away from internet clears the slug and kills the public link.

Example — poll until the thread run finishes

curl "{{API_BASE}}/copilot/api/jobs/JOB_ID" \
  -H "Authorization: Bearer qrc_..."

curl "{{API_BASE}}/copilot/api/threads/THREAD_ID" \
  -H "Authorization: Bearer qrc_..."

Example — share to the workspace (signed-in link)

curl -X PATCH "{{API_BASE}}/copilot/api/threads/THREAD_ID/sharing" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{"visibility": "public"}'

Example — share to selected groups

curl -X PATCH "{{API_BASE}}/copilot/api/threads/THREAD_ID/sharing" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{"visibility": "group", "group_ids": ["GROUP_ID"]}'

Typical workflow

  1. Create a thread (optional) — skip for one-shot questions; ask creates a thread automatically.
  2. Attach files to the thread (optional), one upload per file.
  3. Ask with thread_id — thread attachments are picked up automatically.
  4. Poll until succeeded, then read answer and artifacts. GET /copilot/api/threads/{thread_id} also exposes the same job as latest_job.
  5. Download artifacts from each download_url.
  6. Share (optional) — PATCH /copilot/api/threads/{thread_id}/sharing and copy share_url into a doc. public is the whole workspace; internet is anyone with the link; group needs group_ids.
  7. Delete (optional) — DELETE /copilot/api/threads/{thread_id} soft-archives the thread (same as the browser Chat delete).

Example — one-shot ask

curl -X POST "{{API_BASE}}/copilot/api/ask" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{"question": "Summarize the latest CPI print"}'

Example — poll chat job

curl "{{API_BASE}}/copilot/api/jobs/JOB_ID" \
  -H "Authorization: Bearer qrc_..."

Example — cancel a running chat job

curl -X POST "{{API_BASE}}/copilot/api/jobs/JOB_ID/cancel" \
  -H "Authorization: Bearer qrc_..."

Example — share a finished thread and copy the link

curl -X PATCH "{{API_BASE}}/copilot/api/threads/THREAD_ID/sharing" \
  -H "Authorization: Bearer qrc_..." \
  -H "Content-Type: application/json" \
  -d '{"visibility": "internet"}'

Example — delete (soft-archive) a thread

curl -X DELETE "{{API_BASE}}/copilot/api/threads/THREAD_ID" \
  -H "Authorization: Bearer qrc_..."
Threads & sessions. Pass the same thread_id on later asks to continue the conversation. UI and API turns on the same thread share one agent session.
Tip: Chat jobs bill credits like a normal turn. A failed job may mean insufficient balance — check error on the job.

Billing

Credits & billing

Financial Research Copilot uses a prepaid credit balance. Each assistant turn can debit your account after usage is metered; you’ll see notices in the chat sidebar when a charge completes, when metering is still pending, or when a run failed (including insufficient balance).

  • View balance — Open the chat sidebar and expand Available balance. Recent ledger lines (purchases, grants, and chat debits) appear below when history exists.
  • Buy credits — With Stripe enabled on the API, preset amounts appear under Buy credits. Choosing one starts a secure checkout; after payment, your balance updates when Stripe confirms (usually within a few seconds). Return URLs may show a short billing status message.
  • Admin grants — Organization admins can credit an account from Admin → Billing for promos, manual payments, or adjustments. Those entries show up in the user’s history like any other top-up.

How charges are calculated. The expanded balance panel explains that consumption reflects platform AI processing pricing — underlying model usage, tax recovery, infrastructure, and margin — rather than a flat per-message fee. Admins can open a usage request’s detail drawer to compare base cost, tax recovery, markup, and per-component token metering for auditing.

Layout

Mobile & narrow screens

On small viewports, the chat sidebar becomes a slide-out drawer: use the menu control in the header to open threads, billing, workspace links, and sign out. The billing panel can expand to focus on balance and purchase controls. Workspace pages wrapped in the shell use the same pattern.

Tables (for example on Knowledge) scroll horizontally when needed so columns stay readable without cramping the layout.