{"openapi":"3.0.3","info":{"title":"CodeGraph API","description":"Dependency graph API for Success IT's full-stack product suite. Maps Angular frontend → C# API → Stored Procedures → SQL Server Tables → JasperReports → Legacy VBA (Access). Use this API to query cross-layer impact before making changes.\n\n**Products indexed**: iris (insurance), workshop, hire_purchase, expat_furniture (including legacy VBA from SIRFR.mdb), car (including legacy VBA from SirCar.mdb)\n\n**Layers**: angular, csharp, sql, jasper, vba\n\n**Node types**: component, service, route (angular); function, endpoint, class (csharp); stored_procedure, table, view, function (sql); jasper_report (jasper); vba_module, vba_form, vba_report, vba_query, vba_table (vba)\n\n**Edge types**: calls, handles, injects, invokes, executes, reads, writes, queries, triggers, produces, imports, extends, implements, references\n\n**Node keys** are prefixed by layer: `angular:ComponentName`, `proc:sp_Name`, `table:tblName`, `method:ClassName.MethodName`, `endpoint:POST /api/path`, `jasper:ReportName`, `vba:ModuleName`, `vba_form:frmName`, `vba_report:rptName`, `vba_query:qryName`\n\n**Legacy VBA**: The vba layer indexes extracted Microsoft Access 2003 databases (VBA modules, forms, reports, queries, tables). Cross-layer edges link VBA forms/modules to the same SQL Server tables used by modern C# endpoints, enabling full traceability from legacy to modern code.","version":"1.0.0"},"servers":[{"url":"/","description":"Current server"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-Api-Key"}},"schemas":{"Node":{"type":"object","properties":{"node_key":{"type":"string","description":"Unique key with layer prefix","example":"angular:APDNCNComponent"},"name":{"type":"string","example":"APDNCNComponent"},"node_type":{"type":"string","enum":["component","service","route","function","endpoint","class","method","stored_procedure","table","view","jasper_report","vba_module","vba_form","vba_report","vba_query","vba_table"],"example":"component"},"layer":{"type":"string","enum":["angular","csharp","sql","jasper","vba"],"example":"angular"},"product":{"type":"string","enum":["iris","workshop","hire_purchase","expat_furniture","car"],"example":"iris"},"file_path":{"type":"string","nullable":true,"example":"app/views/accounting/apdncn.component.ts"},"metadata":{"type":"string","nullable":true,"description":"JSON string with extra context (e.g. route, httpMethod, parameters)"}}},"TraceNode":{"type":"object","description":"A node found during graph traversal with depth and path info","properties":{"node_key":{"type":"string","example":"angular:AmplitudeService"},"name":{"type":"string","example":"AmplitudeService"},"node_type":{"type":"string","example":"service"},"layer":{"type":"string","example":"angular"},"file_path":{"type":"string","nullable":true},"depth":{"type":"integer","description":"Hops from the center node","example":1},"path_names":{"type":"string","description":"Human-readable traversal path","example":"APDNCNComponent → AmplitudeService"},"via_edge":{"type":"string","description":"Edge type used to reach this node","example":"injects"}}},"Edge":{"type":"object","properties":{"source":{"type":"string","example":"angular:APDNCNComponent"},"source_name":{"type":"string","example":"APDNCNComponent"},"source_type":{"type":"string","example":"component"},"source_layer":{"type":"string","example":"angular"},"target":{"type":"string","example":"angular:AmplitudeService"},"target_name":{"type":"string","example":"AmplitudeService"},"target_type":{"type":"string","example":"service"},"target_layer":{"type":"string","example":"angular"},"edge_type":{"type":"string","example":"injects"},"confidence":{"type":"number","description":"1.0=certain, 0.7=regex, 0.5=heuristic","example":1}}},"IndexRun":{"type":"object","properties":{"indexer":{"type":"string","enum":["sql-server","csharp","angular","jrxml","vba"],"example":"sql-server"},"product":{"type":"string","example":"iris"},"status":{"type":"string","enum":["running","completed","failed"],"example":"completed"},"started_at":{"type":"string","example":"2026-03-15 14:57:00"},"finished_at":{"type":"string","nullable":true,"example":"2026-03-15 14:57:53"},"node_count":{"type":"integer","example":1637},"edge_count":{"type":"integer","example":2376},"error_msg":{"type":"string","nullable":true}}},"SearchResult":{"type":"object","properties":{"score":{"type":"number","description":"Relevance score (higher is better)","example":80},"node_key":{"type":"string"},"name":{"type":"string"},"node_type":{"type":"string"},"layer":{"type":"string"},"product":{"type":"string"},"file_path":{"type":"string","nullable":true}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/api/health":{"get":{"summary":"Health check","description":"Returns server health, uptime, and database statistics. No authentication required.","security":[],"responses":{"200":{"description":"Server is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"healthy"},"total_nodes":{"type":"integer","example":7156},"total_edges":{"type":"integer","example":14792},"db_size_bytes":{"type":"integer","example":3092480},"uptime_seconds":{"type":"integer","example":86400}}}}}}}}},"/api/stats":{"get":{"summary":"Graph summary statistics","description":"Returns total node/edge counts, breakdowns by type and layer, and recent index runs. Good first call to understand what's in the graph.","responses":{"200":{"description":"Graph statistics","content":{"application/json":{"schema":{"type":"object","properties":{"total_nodes":{"type":"integer","example":7156},"total_edges":{"type":"integer","example":14792},"nodes_by_type":{"type":"array","items":{"type":"object","properties":{"node_type":{"type":"string"},"layer":{"type":"string"},"count":{"type":"integer"}}}},"edges_by_type":{"type":"array","items":{"type":"object","properties":{"edge_type":{"type":"string"},"count":{"type":"integer"}}}},"recent_index_runs":{"type":"array","items":{"$ref":"#/components/schemas/IndexRun"}}}}}}}}}},"/api/status":{"get":{"summary":"Indexing status","description":"Returns the most recent index run for each product and indexer type. Use this to check when data was last refreshed.","responses":{"200":{"description":"Index run status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"last_runs":{"type":"array","items":{"$ref":"#/components/schemas/IndexRun"}}}}}}}}}},"/api/search":{"get":{"summary":"Fuzzy search nodes","description":"Search across all nodes by name, key, file path, or metadata. Results are ranked by relevance score. This is the best endpoint for finding nodes when you don't know the exact node_key.","parameters":[{"name":"q","in":"query","required":true,"description":"Search term (searches name, node_key, file_path, metadata)","schema":{"type":"string"},"example":"RenewPolicy"},{"name":"type","in":"query","description":"Filter by node_type","schema":{"type":"string","enum":["component","service","route","function","endpoint","class","method","stored_procedure","table","view","jasper_report","vba_module","vba_form","vba_report","vba_query","vba_table"]}},{"name":"layer","in":"query","description":"Filter by layer","schema":{"type":"string","enum":["angular","csharp","sql","jasper","vba"]}},{"name":"product","in":"query","description":"Filter by product","schema":{"type":"string","enum":["iris","workshop","hire_purchase","expat_furniture","car"]}},{"name":"limit","in":"query","description":"Max results (default 25)","schema":{"type":"integer","default":25}}],"responses":{"200":{"description":"Ranked search results","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SearchResult"}}}}},"400":{"description":"Missing query parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/nodes":{"get":{"summary":"List and filter nodes","description":"Return nodes matching filter criteria. Use query params to narrow by type, layer, product, or name substring.","parameters":[{"name":"q","in":"query","description":"Filter by name or node_key substring","schema":{"type":"string"}},{"name":"type","in":"query","description":"Filter by node_type","schema":{"type":"string"}},{"name":"layer","in":"query","description":"Filter by layer","schema":{"type":"string"}},{"name":"product","in":"query","description":"Filter by product","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 100)","schema":{"type":"integer","default":100}}],"responses":{"200":{"description":"Array of matching nodes","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Node"}}}}}}}},"/api/edges":{"get":{"summary":"List edges (dependencies)","description":"Return edges (relationships between nodes). Optionally filter by edge_type.","parameters":[{"name":"type","in":"query","description":"Filter by edge_type (calls, handles, injects, invokes, executes, reads, writes, queries, etc.)","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Max results (default 500)","schema":{"type":"integer","default":500}}],"responses":{"200":{"description":"Array of edges","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Edge"}}}}}}}},"/api/node/{key}":{"get":{"summary":"Get node details with connections","description":"Returns a single node with all its inbound (what calls this) and outbound (what this calls) edges. The key must be the full node_key including prefix (e.g. `proc:sp_RenewPolicy`, `angular:DashboardComponent`).","parameters":[{"name":"key","in":"path","required":true,"description":"Full node_key (e.g. proc:sp_RenewPolicy, angular:DashboardComponent, table:tblPolicy)","schema":{"type":"string"},"example":"proc:sp_RenewPolicy"}],"responses":{"200":{"description":"Node with inbound and outbound connections","content":{"application/json":{"schema":{"type":"object","properties":{"node":{"$ref":"#/components/schemas/Node"},"inbound":{"type":"array","description":"Nodes that depend on this node (callers, consumers)","items":{"type":"object","properties":{"source":{"type":"string"},"source_name":{"type":"string"},"source_type":{"type":"string"},"source_layer":{"type":"string"},"edge_type":{"type":"string"},"confidence":{"type":"number"}}}},"outbound":{"type":"array","description":"Nodes that this node depends on (callees, dependencies)","items":{"type":"object","properties":{"target":{"type":"string"},"target_name":{"type":"string"},"target_type":{"type":"string"},"target_layer":{"type":"string"},"edge_type":{"type":"string"},"confidence":{"type":"number"}}}}}}}}},"404":{"description":"Node not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/blast/{key}":{"get":{"summary":"Blast radius / impact analysis","description":"The most important endpoint for impact assessment. Returns all nodes affected upstream (what breaks if I change this) and downstream (what does this depend on) from a given node, traversing the full dependency graph recursively. Use this before making changes to stored procedures, API endpoints, or tables.","parameters":[{"name":"key","in":"path","required":true,"description":"Full node_key to analyze (e.g. proc:sp_RenewPolicy, table:tblPolicy)","schema":{"type":"string"},"example":"proc:sp_GI_Dashboard_GetTotalValue"},{"name":"depth","in":"query","description":"Max traversal depth (default 10)","schema":{"type":"integer","default":10}}],"responses":{"200":{"description":"Full impact analysis","content":{"application/json":{"schema":{"type":"object","properties":{"center":{"type":"string","description":"The node_key being analyzed"},"upstream":{"type":"array","description":"Nodes that depend on this (what breaks if changed). Sorted by depth.","items":{"$ref":"#/components/schemas/TraceNode"}},"downstream":{"type":"array","description":"Nodes this depends on (what this uses). Sorted by depth.","items":{"$ref":"#/components/schemas/TraceNode"}},"total_affected":{"type":"integer","description":"Total unique nodes in the blast radius (excluding center)"}}}}}}}}},"/api/products":{"get":{"summary":"List indexed products","description":"Returns all indexed products with node counts broken down by layer and total edge counts.","responses":{"200":{"description":"Products with statistics","content":{"application/json":{"schema":{"type":"object","properties":{"by_layer":{"type":"array","items":{"type":"object","properties":{"product":{"type":"string"},"layer":{"type":"string"},"nodes":{"type":"integer"}}}},"totals":{"type":"array","items":{"type":"object","properties":{"product":{"type":"string"},"total_nodes":{"type":"integer"},"total_edges":{"type":"integer"}}}}}}}}}}}},"/api/graph":{"get":{"summary":"Full graph export","description":"Returns all nodes and edges in the graph. Warning: large response (~7100 nodes, ~14800 edges). Use /api/search or /api/blast for targeted queries instead.","responses":{"200":{"description":"Complete graph data","content":{"application/json":{"schema":{"type":"object","properties":{"nodes":{"type":"array","items":{"type":"object","properties":{"node_key":{"type":"string"},"name":{"type":"string"},"node_type":{"type":"string"},"layer":{"type":"string"},"product":{"type":"string"},"file_path":{"type":"string","nullable":true}}}},"edges":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"target":{"type":"string"},"edge_type":{"type":"string"},"confidence":{"type":"number"}}}}}}}}}}}},"/api/schema":{"get":{"summary":"OpenAPI specification","description":"Returns this OpenAPI spec. AI agents should call this first to understand all available endpoints.","security":[],"responses":{"200":{"description":"OpenAPI 3.0 specification","content":{"application/json":{"schema":{"type":"object"}}}}}}}}}