# Kela FundOS — Full API & Integration Reference for AI Agents # Last updated: 2026-05-21 > FundOS by Kela — https://kela.com > Machine-readable integration guide for AI agents, LLMs, and developers. --- ## Authentication All API and MCP calls use Bearer token auth. Defensive note: the server's `_authenticate()` strips an accidental inner `Bearer ` prefix from the token before checking it (so requests with `Authorization: Bearer Bearer vdr_xxx` from agents that double- prefixed the env var still succeed). The CLI does the same client-side in `cli/fundos_cli/config.py::_clean_api_key()`. If you see a 401, the diagnostic block shipped with the CLI prints the redacted key prefix + base URL so you can spot env-mismatch errors in 3 seconds. ### API Key (recommended for AI agents) Generate in Settings → API Keys. Format: starts with `vdr_`. ``` Authorization: Bearer vdr_xxxxxxxxxxxxxxxx ``` ### JWT Token (24-hour, browser sessions) ``` POST https://kela.com/api/v1/auth/login {"email": "...", "password": "..."} → {"token": "eyJ...", "expires_in": 86400} ``` Rate limit: 100 req/min. Headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. --- ## MCP Server (Claude, Cursor, other MCP clients) ```bash claude mcp add fundos https://kela.com/mcp ``` For SSE-only clients: https://kela.com/mcp/sse POST messages to the session URL returned in the SSE endpoint event. ### MCP Endpoints - GET /mcp/sse SSE stream - POST /mcp/message JSON-RPC 2.0 - GET /mcp/info Server capabilities - GET /mcp/tools Full tool list with JSON Schema - POST /mcp/call Simple REST tool call (no SSE needed) ### Available MCP Tools (47) **VDR / Deal Rooms** list_deal_rooms, get_deal_room, create_deal_room*, list_documents, get_document_metadata, get_document_download_url, search_documents, list_room_members, add_room_member*, list_users, get_audit_log, get_document_activity, get_signature_status, list_qa_questions, answer_qa_question* **Deal CRM + Transactions + Pricer** fundos_list_deals, fundos_get_deal, fundos_create_deal*, fundos_get_pipeline, fundos_list_transactions, fundos_draft_transaction*, fundos_run_pricer **Risk** fundos_list_covenants, fundos_check_covenant, fundos_list_risk_alerts **ODD / CIM** fundos_generate_odd, fundos_vdr_analyze, fundos_list_cim_reports, fundos_list_cim_templates, fundos_generate_cim **Investor Portal** fundos_list_lps, fundos_get_lp, fundos_create_capital_call* **CFO Center** fundos_list_fund_accounts, fundos_compute_pnl, fundos_compute_waterfall **Syndication** fundos_list_syndications, fundos_get_syndication, fundos_allocate* **HF Ops (DTCC ITP)** hf_ops_dtcc_get_trade_status, hf_ops_dtcc_list_unaffirmed, hf_ops_dtcc_get_affirmation_scorecard, hf_ops_dtcc_lookup_ssi **OMS / Trading** oms_list_orders, oms_get_order, oms_create_order*, oms_list_positions, oms_check_pretrade, oms_get_fix_status **Utility** fundos_list_tools, fundos_call_tool, search, fetch * = requires human approval (side-effect tool) ### MCP Resources myvdr://rooms, myvdr://fundos/deals, myvdr://fundos/transactions, myvdr://fundos/covenants, myvdr://fundos/lps, myvdr://fundos/positions --- ## REST API Reference Base URL: https://kela.com/api/v1 Interactive docs: https://kela.com/api/docs ### VDR — Deal Rooms GET /rooms List rooms POST /rooms Create room GET /rooms/{id} Room detail + members GET /rooms/{id}/documents List documents POST /rooms/{id}/documents Upload (multipart) GET /rooms/{id}/documents/{doc_id}/download DELETE /rooms/{id}/documents/{doc_id} GET /rooms/{id}/members POST /rooms/{id}/members Invite {email, role} GET /rooms/{id}/qa Q&A questions POST /rooms/{id}/qa/{q_id}/answer GET /rooms/{id}/analytics/audit ### FundOS — Deal CRM GET /api/v1/fundos/crm/deals List deals POST /api/v1/fundos/crm/deals Create deal (?ephemeral=true to dry-run) GET /api/v1/fundos/crm/deals/{id} PATCH /api/v1/fundos/crm/deals/{id} GET /api/v1/fundos/crm/pipeline Deals grouped by stage ### FundOS — LP CRM GET /api/v1/fundos/lp-crm/investors POST /api/v1/fundos/lp-crm/investors/{id}/activity ### FundOS — Pricer POST /api/v1/fundos/pricer IRR/MOIC/WAL/waterfall — ?ephemeral=true safe ### FundOS — Risk GET /api/v1/fundos/risk/covenants POST /api/v1/fundos/risk/covenants/{id}/check GET /api/v1/fundos/risk/alerts ### FundOS — Investor Portal GET /api/v1/fundos/investors/lps GET /api/v1/fundos/investors/lps/{id} Includes capital-call ledger POST /api/v1/fundos/investors/lps/{id}/capital-calls ### FundOS — CFO Center GET /api/v1/fundos/cfo/accounts Fund account list POST /api/v1/fundos/cfo/report P&L + NAV computation POST /api/v1/fundos/cfo/waterfall European waterfall splits CFO Center Audit Pack (web UI, /fundos/cfo): - Generate a ZIP audit pack (trial balance, LP capital roll-forward, mgmt fee, waterfall, cover memo) for any fund account with a custom as-of date. - View the contents of any generated pack (individual file list with per-file download buttons). - Delete any audit pack (including errored ones) via the trash icon. - Per-account persistent configuration via AuditPackConfig: toggle each of the 5 standard artifacts on/off and attach extra VDR documents to include in the pack. Config is shared across all CFO users in the workspace. - Downloads are proxied through Flask (authenticated); direct DO Spaces URLs are not exposed. ### FundOS — OMS / Trading GET /api/v1/fundos/oms/orders List orders (status, fa_id filters) POST /api/v1/fundos/oms/orders Submit order (runs pre-trade compliance) GET /api/v1/fundos/oms/orders/{id} Order + executions GET /api/v1/fundos/oms/positions Position book POST /api/v1/fundos/oms/pretrade Dry-run compliance check (no order created) GET /api/v1/fundos/oms/fix-status FIX sidecar health + session state ### FundOS — Syndication GET /api/v1/fundos/syndication POST /api/v1/fundos/syndication/{id}/allocate --- ## Common Agent Workflows ### 1. Due Diligence on an inbound deck 1. fundos_create_deal (ephemeral=true) — validate 2. Human confirms → fundos_create_deal (persist) 3. create_deal_room → spin up diligence room 4. fundos_vdr_analyze → red flags + entity map 5. search_documents for specific terms ### 2. LP fundraising follow-up 1. fundos_list_lps → find target LP 2. fundos_get_lp → commitment + capital-call ledger 3. search_documents (LP room) → last meeting notes 4. Draft follow-up — STOP, ask human to send ### 3. Pre-trade compliance check (OMS) 1. oms_check_pretrade (ticker, side, qty, limit_price) → passed/blocked + notes 2. If passed → oms_create_order (HUMAN APPROVAL required) 3. oms_list_positions → confirm position updated after fills ### 4. Capital call ILPA notice 1. fundos_get_lp → confirm LP and lp_room_id 2. Human creates CapitalCall row in /fundos/investors/{id} 3. GP clicks ILPA button → notice generated into LP room 4. Agent NEVER auto-sends LP notices ### 5. T+0 affirmation chase (HF Ops) 1. hf_ops_dtcc_list_unaffirmed → pending confirms 2. hf_ops_dtcc_get_trade_status → live state per trade 3. hf_ops_dtcc_lookup_ssi → check SSI if mismatch suspected 4. hf_ops_dtcc_get_affirmation_scorecard → COO summary --- ## Webhooks Register an HTTPS endpoint to receive real-time FundOS events. ```bash POST /api/v1/webhooks/ {"url": "https://your-server/hooks/fundos", "event_types": ["action.approval_required", "job.completed"]} ``` Events: credit.low, credit.exhausted, credit.granted, action.approval_required, action.approved, action.rejected, action.expired, job.completed, job.failed, module.enabled, module.disabled Payload signed with HMAC-SHA256 in X-FundOS-Signature header. Retry schedule: immediate → 1min → 5min → 30min → 2hr → 8hr (6 total). --- ## Error Codes 400 Bad Request — Invalid parameters 401 Unauthorized — Missing or invalid API key 403 Forbidden — Insufficient permissions 404 Not Found — Resource does not exist 422 Unprocessable — Pre-trade compliance block or validation failure 429 Too Many Requests — Rate limit (100 req/min); retry after X-RateLimit-Reset 500 Internal Error — Server error; retry with exponential backoff {"error": true, "message": "...", "code": "ERROR_CODE"} --- ## Key Facts for AI Agents - ?ephemeral=true on any POST: validates and returns shape, zero DB writes - AI processing on uploaded documents: automatic, ~30–120s; poll status - No AI model trains on or retains document content - FundOS agents propose actions; humans approve — nothing auto-executes - BYOD MCP connectors: Google Drive, SharePoint, Egnyte, QuickBooks, Salesforce, custom - OMS FIX engine: QuickFIX/n sidecar on DigitalOcean, connecting to BTIG prime brokerage - Gemini 2.5 Flash (thinkingBudget:0) powers all AI generation --- ## What's new — May 2026 ### Compliance OS (11th module) — /fundos/compliance/ Models: ComplianceKYCRecord, FilingDeadline, RestrictedSecurity, PreClearanceRequest, ComplianceObligation, KYCNameScreenResult. Routes: - GET /fundos/compliance/ Compliance dashboard - GET /fundos/compliance/kyc/ KYC/AML records - GET /fundos/compliance/filings/ Regulatory filings - GET /fundos/compliance/trade/ Restricted securities + pre-clearance - GET /fundos/compliance/obligations/ Obligations register - POST /fundos/compliance/kyc//screen Gemini-powered name screen - POST /fundos/compliance/agent/scan Run Agent H MCP tools: fundos_list_kyc_records, fundos_list_filings, fundos_check_restricted_list (by ticker/ISIN/company name), fundos_list_obligations. ### Compliance Dashboard — /fundos/dashboard/compliance A third executive dashboard alongside GP (/fundos/dashboard/gp) and Admin (/fundos/dashboard/admin). KYC pipeline counts, expiring records in 60 days, recent name screens, upcoming filings, overdue filings list, restricted-list count, pre-clearance queue, obligations, recent Agent H runs. Safe-queries every column so a freshly-deployed DB shows zeros + a "schema not provisioned" banner instead of 500s. ### Per-user dashboard customization (persistent) Every executive dashboard supports: - Toggle widgets on/off + drag-reorder via the Customize modal (top right). - Set this dashboard as my post-login landing page (overrides /fundos/). Schema: users.allowed_dashboards CSV, users.default_dashboard, users. dashboard_prefs_json. Helpers: User.allowed_dashboards_set(), User.can_view_dashboard(key), User.dashboard_prefs(key), User.set_dashboard_prefs(key, hidden, order). Endpoints: - POST /fundos/dashboard//prefs Save widget hidden/order - POST /fundos/dashboard//default Mark as post-login landing - GET /admin/dashboard-access Admin grants per-user access ### Schema-deployment safety net When inline _migrate_db() is gated behind RUN_MIGRATIONS=1 (the default on Vercel due to DigitalOcean connection limits) and someone forgets to flip the flag, app/utils/schema_guard.py catches missing-table / column errors anywhere in the app and renders a friendly page. Superadmins get a "Run migrations now" button that calls _migrate_db() on demand via POST /superadmin/run-migrations. ### Sanity portfolio sync GET /superadmin/sanity-sync (dry-run preview) and POST /superadmin/sanity-sync (apply) push the FundOS portfolio for org=SANITY_SYNC_ORG_ID into the public Sanity CMS that backs eight-capital.com. The sync collapses VI follow-on rows ("Foo (FO)") to a single canonical company, only patches fields it owns (name, status, batch), and never touches hand-curated Sanity fields (description, logo, founder bios). Re-running is safe (createIfNotExists + targeted patches); deletes and renames are never automatic — orphans on the site are surfaced for manual reconciliation in Sanity Studio. Implementation: app/services/sanity_sync.py. ### Per-page "How to use" help Every FundOS page now has a small ? button in the top-right corner that opens a modal with page-specific help (also accessible via the ? key). Coverage is seeded for ~20 high-traffic pages. Registry lives in app/utils/page_help.py — adding help for a new page is one tuple entry. ### Admin module access bypass @module_required now lets admins / superadmins bypass both gates (the per-user enabled_modules layer AND the org-subscription layer). Non- admin users still hit both. Rationale: admins make billing decisions and shouldn't be blocked from clicking into a module they're about to subscribe to. ### Operator CLI — `fundos` (May 2026) Standalone Python CLI for operator workflows. Lives in cli/ in the repo. pip install -e ./cli fundos login # prompts for API key + base URL fundos whoami # identity check Command tree: fundos org list / get / create / enable-vertical / cleanup-spam fundos credits balance / history / add fundos fix status / config / connect / test-order / fills fundos db status / migrate / seed fundos mcp tools / call # read-only tools only fundos logs audit / agents / errors (all --org-id filter) The CLI talks HTTP only — no DB access, no app/ imports. Backend at /api/cli/* (18 routes). Same Bearer-token auth as /api/v1/*. Both the CLI and the server defensively strip an accidental "Bearer " prefix from pasted tokens, so the most common copy-paste 401 doesn't happen. On a 401, the CLI prints the redacted key prefix + base URL so users can spot env-mismatches (dev key against prod, etc.). Env vars: FUNDOS_API_KEY, FUNDOS_BASE_URL (default https://www.kela.com). ### Try-before-you-pay billing (auth-then-capture, May 2026) For packaged AI tasks (ODD, CIM, audit pack, K-1 generation, Level 3 valuation memos), credits are reserved when the task runs but not charged until the customer uses the output. Promise to the customer: "View any AI-generated artifact for free. We only charge if you download it, accept it, or feed it into another tool. If 7 days pass without confirmation, the hold expires and you're not charged." State machine on CreditLedger.hold_status: pending — task done, output viewable, NOT counted toward balance confirmed — customer used the output (download / explicit confirm / chained tool call); charge stands refunded — customer rejected the output; no charge expired — 7 days elapsed with no action; no charge REST API (Bearer auth): GET /api/v1/tasks/holds/ list pending for org GET /api/v1/tasks/holds/ read a hold's state POST /api/v1/tasks/holds//confirm customer accepts the charge POST /api/v1/tasks/holds//refund customer rejects the output POST /api/v1/tasks/holds/cron/expire hourly cron (CRON_SECRET) Document download gate: any Document whose billing_hold_id points at a pending hold auto-confirms on download; a refunded or expired hold returns 402 with structured JSON `{error, hold_id, hold_status}`. Schema columns (auto-migrated unconditionally on every cold start — not gated by RUN_MIGRATIONS, because the ORM SELECTs these on every Document query and missing them would 500 every page that loads docs): credit_ledger.hold_status VARCHAR(20) credit_ledger.hold_expires_at TIMESTAMP documents.billing_hold_id INTEGER (FK credit_ledger.id) ### Anti-spam signup guards (May 2026) Every signup path runs three independent checks at app/utils/signup_guard.py: 1. Disposable-email blocklist (17 domains: mailinator, guerrillamail, tempmail, sharklasers, dispostable, maildrop, …). yopmail.com is explicitly whitelisted in ALLOWED_TEST_DOMAINS for internal QA. 2. Random-name detector — two complementary heuristics: - vowel ratio < 0.20 on the cleaned (letters-only) string - ≥ 3 lower→upper case transitions The second heuristic catches high-vowel bot strings like "HrzIIXAEjlmnTIuDjgAu" (40% vowels) that vowel-ratio alone can't. Tested against the full Eight Capital / VantedgeAI / Bristol Myers Squibb / PricewaterhouseCoopers corpus — zero false positives. 3. IP rate limit — 5 attempts per IP per hour, Postgres-backed, fails open on DB hiccups so legit signups are never blocked by infra. Wired into POST /signup, POST /create-org, and the new-user branch of the Google OAuth callback. Returns HTML errors for browser submits and JSON 400s for the API path. Cleanup of existing spam: POST /api/cli/admin/cleanup-spam-orgs (or `fundos org cleanup-spam` from the CLI). Defaults to dry-run. Six nested safety guards: - PROTECTED_ORG_IDS (= {1, 2, 17, 30}) never touched - PROTECTED_EMAIL_DOMAINS (yopmail.com) never touched - already-suspended orgs not re-suspended - paid plans not touched (only free / starter / NULL) - orgs < 24h old not touched - orgs with any Document / DealRoom / Deal rows not touched (real work) Then matches at least one spam signal (random-string name, dotted-bot name, disposable-email owner) before flipping status='suspended' and writing an AuditLog row. ## Links Homepage: https://kela.com Developer: https://kela.com/developer API Docs: https://kela.com/api/docs MCP Info: https://kela.com/mcp/info CLAUDE.md: https://kela.com/CLAUDE.md llms.txt: https://kela.com/llms.txt llms-full.txt: https://kela.com/llms-full.txt (this document) Privacy: https://kela.com/privacy Trust: https://vantedgeai.trust.site/