Documentation
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
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.
For how data and research content are handled, see Privacy and Terms of use.
Preferences
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.
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.
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.
POST /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
The chat UI is built around threads — each thread keeps its own title, message history, and uploaded attachments.
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.
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.
Integrations
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).
.md / .markdown artifacts, use Save to Feishu Docs.Integrations
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:
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.
Reuse
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.
Automation
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 taskScheduled 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.
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:
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 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.
main). Strategy Improver refuses to start until that link exists.main.py alone./var/run/docker.sock, and images/env your backtest compose file expects.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.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.
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.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:
scope.json (strategy path, entrypoint, config, evidence-backed strategy_profile).baseline_backtest.json and diagnosis.json.Name the strategy folder, branch context, and improvement goal in the first message. Good prompts are specific about what to improve and which strategy:
letf. I want ideas for better timing around rebalance days; reject changes that need data we do not have in the repo.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.
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
There are several ways content enters the system — pick the one that matches your goal:
.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.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.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.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.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.
Library
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:
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:
Developers
Mint a personal API key in Settings to call programmatic routes as your account. One key covers three route families:
/ingestion/...) — index documents into knowledge libraries./copilot/api/...) — ask questions, attach files, and collect answers plus output artifacts./auth/...) — read and update profile fields, including agent and composer models used by chat runs.qrc_) is shown only once.401.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.
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).
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.
job_id.answer + artifacts).Job status values: queued → running → succeeded / failed. You only see your own jobs.
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.
/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./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.Maps to the same tiers as the UI (see Knowledge libraries & access):
visibility=private) — only you (and admins).visibility=group + group_ids) — members of those groups. Standard users must belong to each group or get 403.visibility=public) — workspace corpus. Admins only; a standard-user key asking for it gets 403.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.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.pdfExample — 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}'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.
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.
Optional POST /copilot/api/ask fields (same semantics as the browser composer). Order below reflects how you typically set them:
company-model-builder). Omit for General Research Copilot. Packaged agents use a fixed MCP allowlist; mcp_mode / mcp_servers are ignored.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.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."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"
}'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.
agent_slug values ignore slash skills (same as the UI).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?"}'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.
GET /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.GET /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.PATCH /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.DELETE /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"]}'ask creates a thread automatically.thread_id — thread attachments are picked up automatically.succeeded, then read answer and artifacts. GET /copilot/api/threads/{thread_id} also exposes the same job as latest_job.download_url.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.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_..."thread_id on later asks to continue the conversation. UI and API turns on the same thread share one agent session.failed job may mean insufficient balance — check error on the job.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).
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
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.