{"openapi":"3.1.0","info":{"title":"SN43 Knowledge Graph API","description":"Decentralized financial knowledge graph — validator, customer, and admin APIs","version":"1.0.0"},"paths":{"/api/v1/deltas/score":{"post":{"tags":["validator"],"summary":"Score Delta","description":"Score a KnowledgeDelta against the canonical graph.","operationId":"score_delta_api_v1_deltas_score_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeDelta"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Score Delta Api V1 Deltas Score Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/deltas/merge":{"post":{"tags":["validator"],"summary":"Merge Delta","description":"Score and merge a KnowledgeDelta into the canonical graph.\n\nBody shape:\n    {\"delta\": <KnowledgeDelta JSON>, \"documents\": {hash: b64_bytes}}\n\nThe validator forwards docs it has already fetched + hash-verified.\nCentral re-checks the hash before storing — defense in depth, even a\ncompromised validator can't poison the doc_store with mismatched bytes.","operationId":"merge_delta_api_v1_deltas_merge_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Merge Delta Api V1 Deltas Merge Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/miners/contributions":{"get":{"tags":["validator"],"summary":"Get Miner Contributions","description":"Return per-miner contribution counts for restoring submission history.\n\nA validator restarting from a clean process uses these counts to seed\nits in-memory `MinerPerformance.scores` lists — preserving rankings\nacross restarts using the canonical graph as the source of truth\nrather than approximating from chain weights.","operationId":"get_miner_contributions_api_v1_miners_contributions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Miner Contributions Api V1 Miners Contributions Get"}}}}}}},"/api/v1/tasks/contribute":{"get":{"tags":["validator"],"summary":"Get Contribute Task","description":"Get a contribute task for miners.","operationId":"get_contribute_task_api_v1_tasks_contribute_get","parameters":[{"name":"sector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Contribute Task Api V1 Tasks Contribute Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/verify":{"get":{"tags":["validator"],"summary":"Get Verify Task","description":"Get a verify task for miners. Returns 404 if no facts to verify.","operationId":"get_verify_task_api_v1_tasks_verify_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Verify Task Api V1 Tasks Verify Get"}}}}}}},"/api/v1/graph/stats":{"get":{"tags":["graph"],"summary":"Get Stats","description":"Get knowledge graph coverage statistics.","operationId":"get_stats_api_v1_graph_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Stats Api V1 Graph Stats Get"}}}}}}},"/api/v1/graph/underserved":{"get":{"tags":["graph"],"summary":"Get Underserved","description":"Get underserved sectors ranked by least coverage.","operationId":"get_underserved_api_v1_graph_underserved_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Underserved Api V1 Graph Underserved Get"}}}}}}},"/api/v1/graph/query":{"get":{"tags":["graph"],"summary":"Query Graph","description":"Query entities in the knowledge graph.\n\nSet `verified_only=true` to filter out url_only / paywalled items\nand superseded facts.","operationId":"query_graph_api_v1_graph_query_get","parameters":[{"name":"entity_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"}},{"name":"sector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector"}},{"name":"ticker","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"verified_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Verified Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Query Graph Api V1 Graph Query Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/entities/{entity_id}":{"get":{"tags":["customer"],"summary":"Get Entity","description":"Get entity details by ID.","operationId":"get_entity_api_v1_entities__entity_id__get","parameters":[{"name":"entity_id","in":"path","required":true,"schema":{"type":"string","title":"Entity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Entity Api V1 Entities  Entity Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/entities/{entity_id}/relationships":{"get":{"tags":["customer"],"summary":"Get Relationships","description":"Get all relationships for an entity.","operationId":"get_relationships_api_v1_entities__entity_id__relationships_get","parameters":[{"name":"entity_id","in":"path","required":true,"schema":{"type":"string","title":"Entity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Relationships Api V1 Entities  Entity Id  Relationships Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/entities/{entity_id}/facts":{"get":{"tags":["customer"],"summary":"Get Facts","description":"Get all facts for an entity.","operationId":"get_facts_api_v1_entities__entity_id__facts_get","parameters":[{"name":"entity_id","in":"path","required":true,"schema":{"type":"string","title":"Entity Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Facts Api V1 Entities  Entity Id  Facts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/search":{"get":{"tags":["customer"],"summary":"Search","description":"Search entities by name, ticker, or description.\n\nSet `verified_only=true` to skip items with `verified=False` (paywalled\nor non-whitelisted sources) and superseded facts.","operationId":"search_api_v1_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","title":"Q"}},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"}},{"name":"sector","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"verified_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Verified Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Search Api V1 Search Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/graph/path":{"get":{"tags":["customer"],"summary":"Find Path","description":"Find shortest path between two entities.","operationId":"find_path_api_v1_graph_path_get","parameters":[{"name":"source","in":"query","required":true,"schema":{"type":"string","title":"Source"}},{"name":"target","in":"query","required":true,"schema":{"type":"string","title":"Target"}},{"name":"max_depth","in":"query","required":false,"schema":{"type":"integer","default":6,"title":"Max Depth"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Find Path Api V1 Graph Path Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/graph/exposure":{"get":{"tags":["customer"],"summary":"Exposure Analysis","description":"Analyze 1st and 2nd degree connections and sector exposure.","operationId":"exposure_analysis_api_v1_graph_exposure_get","parameters":[{"name":"entity","in":"query","required":true,"schema":{"type":"string","title":"Entity"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Exposure Analysis Api V1 Graph Exposure Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/graph/compare":{"get":{"tags":["customer"],"summary":"Compare Entities","description":"Compare two entities: shared connections, path distance, direct relationships.","operationId":"compare_entities_api_v1_graph_compare_get","parameters":[{"name":"a","in":"query","required":true,"schema":{"type":"string","title":"A"}},{"name":"b","in":"query","required":true,"schema":{"type":"string","title":"B"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Compare Entities Api V1 Graph Compare Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chat":{"post":{"tags":["customer"],"summary":"Chat","description":"Customer asks a question; their LLM answers using graph tools.\n\nThe customer's Graphite API key authenticates the request and counts\ntoward the standard monthly graph-query quota. The LLM call uses the\ncustomer's OWN Anthropic or OpenAI key, sent on each request as:\n\n    X-LLM-Provider: anthropic | openai\n    X-LLM-API-Key:  sk-...\n\nGraphite pays for nothing on the LLM side. If a customer doesn't want\nto enter an LLM key in the frontend, point them at the MCP server —\nsame tools, their own client (Claude Desktop / Cursor / etc.) handles\nthe LLM call directly.\n\nBody:\n    {\"question\": str, \"customer_context\": str (optional), \"model\": str (optional)}\n\nResponse:\n    {\n        \"answer\": str,\n        \"tool_calls\": [{\"name\": str, \"input\": dict}, ...],\n        \"duration_ms\": int,\n        \"model\": str,\n        \"provider\": str,\n        \"usage\": {...}\n    }","operationId":"chat_api_v1_chat_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Chat Api V1 Chat Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/signup":{"post":{"tags":["customer"],"summary":"Signup","description":"Create a customer API key from just an email — no auth required.\n\nRate-limited to 3 signups per IP per hour. The key can be used\nimmediately for /search, /entities/*, /chat (2 free queries), etc.","operationId":"signup_api_v1_signup_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Signup Api V1 Signup Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/keys":{"get":{"tags":["admin"],"summary":"List Keys","description":"List all API keys.","operationId":"list_keys_api_v1_admin_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Response List Keys Api V1 Admin Keys Get"}}}}}},"post":{"tags":["admin"],"summary":"Create Key","description":"Create a new API key.","operationId":"create_key_api_v1_admin_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Key Api V1 Admin Keys Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/keys/{key}":{"delete":{"tags":["admin"],"summary":"Revoke Key","description":"Revoke an API key.","operationId":"revoke_key_api_v1_admin_keys__key__delete","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Revoke Key Api V1 Admin Keys  Key  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/usage/{key}":{"get":{"tags":["admin"],"summary":"Get Usage","description":"Get usage stats for a key.","operationId":"get_usage_api_v1_admin_usage__key__get","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Usage Api V1 Admin Usage  Key  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/validator/revoke":{"post":{"tags":["admin"],"summary":"Revoke Validator","description":"Remove every item submitted by a validator UID.\n\nUse this when you've discovered a validator's hotkey is compromised.\n\n  - With `after_time` set: removes only items merged at-or-after that\n    timestamp (useful when you know roughly when the compromise happened)\n  - Without `after_time`: removes ALL items the validator ever submitted\n    (worst-case: assume the entire history is poisoned)\n\nCascading: removing an entity removes all edges touching it. Counts\nof cascaded edges are returned separately so you can audit blast radius.","operationId":"revoke_validator_api_v1_admin_validator_revoke_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeValidatorRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Revoke Validator Api V1 Admin Validator Revoke Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/miners/stats":{"get":{"tags":["public"],"summary":"Get Miner Stats","description":"Per-miner contribution count + last contribution timestamp.\n\nReturns:\n    {\"miners\": [{uid, contributions, last_contribution_at}, ...]}\n\nSorted by uid ascending. last_contribution_at is ISO8601 UTC or null.\nThe frontend uses last_contribution_at to compute a 7-day liveness flag.","operationId":"get_miner_stats_api_v1_miners_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"items":{"additionalProperties":true,"type":"object"},"type":"array"},"type":"object","title":"Response Get Miner Stats Api V1 Miners Stats Get"}}}}}}},"/api/v1/graph/subgraph":{"get":{"tags":["public"],"summary":"Get Subgraph","description":"BFS subgraph: returns just the neighborhood within `hops` of `focus`.\n\nThe embedded viewer at /graphs/financial uses this instead of\n/graph/data so a focused view loads ~hundreds of nodes instead of\ntens of thousands. `focus` resolves against entity_id, ticker, then\nname (case-insensitive). `hops` is clamped to [0, 10].\n\nOptional date window filters facts and relationships by `valid_from`:\n    from_date <= valid_from <= to_date\nEither bound may be omitted. Accepts ISO-8601 strings (e.g. '2019',\n'2019-01-01', '2019-01-01T00:00:00'). Items missing valid_from are\nkept regardless — we don't drop legacy data silently.\n\nReturns same shape as /graph/data plus `focus` (resolved entity_id),\n`size` (row counts), and `date_filter` (echo of applied window).","operationId":"get_subgraph_api_v1_graph_subgraph_get","parameters":[{"name":"focus","in":"query","required":true,"schema":{"type":"string","title":"Focus"}},{"name":"hops","in":"query","required":false,"schema":{"type":"integer","default":5,"title":"Hops"}},{"name":"from_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From Date"}},{"name":"to_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Subgraph Api V1 Graph Subgraph Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/graph/data":{"get":{"tags":["public"],"summary":"Get Graph Data","description":"Raw canonical graph as JSON. Powers the public viewer iframe.\n\nReturns the exact shape `graph_viewer.html` expects: entities (dict),\nrelationships (list/dict on edges), facts (dict). No auth — same data\nis already exposed via /api/v1/graph/query for entity-by-entity reads.","operationId":"get_graph_data_api_v1_graph_data_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Graph Data Api V1 Graph Data Get"}}}}}}},"/api/v1/stats/coverage":{"get":{"tags":["public"],"summary":"Get Coverage Stats","description":"Sector + entity-type counts for the public dashboard heatmap.\n\nDrops `relationships_by_type` from graph_store.get_coverage_stats() so\nrelationship-strategy details aren't leaked publicly. Sector keys are\ncase-folded and run through an alias map so miner-submitted variants\n(\"Financial Services\", \"Financials\", \"Fintech\") collapse onto the\ncanonical sector slug. Sectors with fewer than `min_count` entities\nare dropped to suppress the long tail of one-off labels.","operationId":"get_coverage_stats_api_v1_stats_coverage_get","parameters":[{"name":"min_count","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Min Count"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Coverage Stats Api V1 Stats Coverage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/stats/subnet":{"get":{"tags":["public"],"summary":"Get Subnet Stats","description":"Bittensor subnet metagraph aggregate for the public dashboard.\n\nReturns total registered UIDs, validator/miner split, total alpha stake.\nFalls back to {available: false} if bittensor isn't installed on the\nserver or the chain is unreachable — the frontend hides the tile.","operationId":"get_subnet_stats_api_v1_stats_subnet_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Subnet Stats Api V1 Stats Subnet Get"}}}}}}},"/api/v1/admin/check":{"post":{"tags":["public"],"summary":"Check Admin Password","description":"One-shot password validation for the public site's /admin gate.\n\nThe FKG site posts {password} here; on success the client stores a flag\nin sessionStorage and renders the unconstrained viewer. This is a UX\ngate, not a security boundary — the underlying viewer URL is itself\npublic. Real protection of admin functionality belongs behind the\nAPI-key gate on other endpoints.\n\nUses hmac.compare_digest to avoid leaking the secret via timing on\nobviously-wrong attempts. Returns 503 (not 200/false) if the server\noperator forgot to set ADMIN_PASSWORD — distinguishes \"wrong password\"\nfrom \"gate is disabled\".","operationId":"check_admin_password_api_v1_admin_check_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"boolean"},"type":"object","title":"Response Check Admin Password Api V1 Admin Check Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/checkout":{"post":{"tags":["billing"],"summary":"Create Checkout","description":"Create a Stripe Checkout Session for the Builder tier.\n\nBody: {\"email\": \"you@example.com\"}\nReturns: {\"checkout_url\": \"https://checkout.stripe.com/...\"}\n\nNo auth required — Stripe handles the actual charge. We only need the\nemail so the webhook can match the payment back to (or mint) a key.","operationId":"create_checkout_api_v1_billing_checkout_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Checkout Api V1 Billing Checkout Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/webhook":{"post":{"tags":["billing"],"summary":"Stripe Webhook","description":"Receive Stripe events. Signature-verified.\n\nOnly events we care about:\n  - checkout.session.completed     → upgrade or mint key for the email\n  - customer.subscription.deleted  → downgrade the linked key\n  - customer.subscription.updated  → sync status (past_due / unpaid)","operationId":"stripe_webhook_api_v1_billing_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Stripe Webhook Api V1 Billing Webhook Post"}}}}}}},"/health":{"get":{"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"CausalDirection":{"type":"string","enum":["s_to_t","t_to_s","bidirectional","confounded"],"title":"CausalDirection"},"CausalEvidence":{"properties":{"method":{"$ref":"#/components/schemas/CausalMethod"},"direction":{"$ref":"#/components/schemas/CausalDirection"},"effect_size":{"type":"number","title":"Effect Size","description":"standardized coefficient, ATE, etc."},"effect_unit":{"type":"string","minLength":1,"title":"Effect Unit"},"p_value":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"P Value"},"confidence_interval_low":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence Interval Low"},"confidence_interval_high":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence Interval High"},"ci_level":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Ci Level","default":0.95},"test_statistic":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Test Statistic"},"n_observations":{"type":"integer","minimum":1.0,"title":"N Observations"},"data_window_start":{"type":"string","format":"date-time","title":"Data Window Start"},"data_window_end":{"type":"string","format":"date-time","title":"Data Window End"},"lag_structure":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lag Structure"},"controls":{"items":{"type":"string"},"type":"array","title":"Controls"},"assumptions":{"items":{"type":"string"},"type":"array","title":"Assumptions"},"robustness_checks":{"items":{"type":"string"},"type":"array","title":"Robustness Checks"},"input_data_hash":{"type":"string","maxLength":64,"minLength":64,"title":"Input Data Hash"},"input_data_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Input Data Url"},"code_hash":{"type":"string","maxLength":64,"minLength":64,"title":"Code Hash"},"code_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Url"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed"},"miner_uid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Miner Uid"},"submitted_at":{"type":"string","format":"date-time","title":"Submitted At"}},"type":"object","required":["method","direction","effect_size","effect_unit","n_observations","data_window_start","data_window_end","input_data_hash","code_hash"],"title":"CausalEvidence","description":"Reproducible causal-inference result attached to a relationship edge.\n\nA single edge may carry multiple CausalEvidence records — e.g., one from\nGranger, another from a synthetic-control study. The graph stores all of\nthem and computes consensus at query time.\n\nReproducibility:\n- `input_data_hash` identifies the exact input series used\n- `code_hash` identifies the exact inference code\n- `seed` pins any stochastic component\n- validators re-execute and confirm the claimed effect matches"},"CausalMethod":{"type":"string","enum":["granger_causality","difference_in_differences","instrumental_variable","synthetic_control","propensity_match","do_calculus","event_study","rdd"],"title":"CausalMethod","description":"Supported causal-inference methods.\n\nEach implies a methodology, assumptions, and expected inputs. Validators\nre-execute the miner's code against stored data in Phase 2."},"CreateKeyRequest":{"properties":{"role":{"type":"string","enum":["validator","customer"],"title":"Role"},"owner_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Email"},"rate_limit_per_minute":{"type":"integer","title":"Rate Limit Per Minute","default":60},"rate_limit_per_day":{"type":"integer","title":"Rate Limit Per Day","default":10000}},"type":"object","required":["role"],"title":"CreateKeyRequest"},"Entity":{"properties":{"entity_id":{"type":"string","title":"Entity Id","description":"Deterministic ID: '{entity_type}:{key}', e.g. 'company:AAPL'"},"entity_type":{"$ref":"#/components/schemas/EntityType"},"name":{"type":"string","minLength":1,"title":"Name"},"ticker":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ticker"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"sector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata"},"source_url":{"type":"string","minLength":1,"title":"Source Url"},"source_date":{"type":"string","format":"date-time","title":"Source Date"},"provenance":{"$ref":"#/components/schemas/ProvenanceChain"},"verified":{"type":"boolean","title":"Verified","description":"True if all anchors hash-verified by validator","default":true},"verification_source":{"$ref":"#/components/schemas/VerificationSource","default":"unknown"},"submitted_by_validator_uid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Submitted By Validator Uid","description":"UID of the validator that pushed this to central"},"merged_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Merged At","description":"UTC timestamp when central accepted the merge"}},"type":"object","required":["entity_id","entity_type","name","source_url","source_date"],"title":"Entity","description":"A node in the knowledge graph (company, person, patent, etc.)."},"EntityType":{"type":"string","enum":["company","person","patent","product","regulation","event"],"title":"EntityType"},"ExtractionMethod":{"type":"string","enum":["manual","regex","llm","heuristic","tabular_parse"],"title":"ExtractionMethod","description":"How a claim was extracted from a source document."},"Fact":{"properties":{"fact_id":{"type":"string","title":"Fact Id","description":"Deterministic: '{entity_id}:{fact_type}'"},"entity_id":{"type":"string","title":"Entity Id"},"fact_type":{"type":"string","minLength":1,"title":"Fact Type","description":"e.g. 'revenue', 'employee_count'"},"value":{"title":"Value"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"source_url":{"type":"string","minLength":1,"title":"Source Url"},"source_date":{"type":"string","format":"date-time","title":"Source Date"},"valid_from":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Valid From"},"valid_to":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Valid To"},"provenance":{"$ref":"#/components/schemas/ProvenanceChain"},"verified":{"type":"boolean","title":"Verified","description":"True if all anchors hash-verified by validator","default":true},"verification_source":{"$ref":"#/components/schemas/VerificationSource","default":"unknown"},"superseded":{"type":"boolean","title":"Superseded","description":"True if a verified fact later contradicted this","default":false},"superseded_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Superseded By","description":"fact_id of the verified fact that superseded this"},"submitted_by_validator_uid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Submitted By Validator Uid"},"merged_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Merged At"}},"type":"object","required":["fact_id","entity_id","fact_type","value","source_url","source_date"],"title":"Fact","description":"A specific data point about an entity."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"KnowledgeDelta":{"properties":{"delta_id":{"type":"string","title":"Delta Id"},"entities":{"items":{"$ref":"#/components/schemas/Entity"},"type":"array","title":"Entities"},"relationships":{"items":{"$ref":"#/components/schemas/Relationship"},"type":"array","title":"Relationships"},"facts":{"items":{"$ref":"#/components/schemas/Fact"},"type":"array","title":"Facts"},"miner_uid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Miner Uid"},"submitted_at":{"type":"string","format":"date-time","title":"Submitted At"}},"type":"object","title":"KnowledgeDelta","description":"A batch of new knowledge submitted by a miner."},"ProvenanceAnchor":{"properties":{"source_doc_hash":{"type":"string","maxLength":64,"minLength":64,"title":"Source Doc Hash","description":"sha256 of the source document, resolvable via doc_store"},"source_url":{"type":"string","minLength":1,"title":"Source Url"},"page":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Page","description":"1-indexed page number"},"paragraph_start":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Paragraph Start"},"paragraph_end":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Paragraph End"},"quoted_text":{"type":"string","maxLength":4000,"minLength":1,"title":"Quoted Text"},"extraction_method":{"$ref":"#/components/schemas/ExtractionMethod","default":"manual"},"extracted_at":{"type":"string","format":"date-time","title":"Extracted At"}},"type":"object","required":["source_doc_hash","source_url","quoted_text"],"title":"ProvenanceAnchor","description":"A single anchor pointing to an exact location in a source document.\n\nThe anchor enables validators and customers to verify that the quoted\ntext appears at the claimed location in the document identified by\n`source_doc_hash`."},"ProvenanceChain":{"properties":{"anchors":{"items":{"$ref":"#/components/schemas/ProvenanceAnchor"},"type":"array","title":"Anchors"}},"type":"object","title":"ProvenanceChain","description":"An ordered list of anchors backing a single claim.\n\nMost claims have one anchor (the source paragraph). Claims derived from\nmultiple documents (e.g., a supplier relationship cross-referenced across\ntwo filings) carry multiple anchors in chronological order."},"Relationship":{"properties":{"relationship_id":{"type":"string","title":"Relationship Id","description":"Deterministic: '{source_id}->{rel_type}->{target_id}'"},"source_entity_id":{"type":"string","title":"Source Entity Id"},"target_entity_id":{"type":"string","title":"Target Entity Id"},"relationship_type":{"$ref":"#/components/schemas/RelationshipType"},"weight":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Weight","default":1.0},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"source_url":{"type":"string","minLength":1,"title":"Source Url"},"source_date":{"type":"string","format":"date-time","title":"Source Date"},"valid_from":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Valid From"},"valid_to":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Valid To"},"provenance":{"$ref":"#/components/schemas/ProvenanceChain"},"causal_evidence":{"items":{"$ref":"#/components/schemas/CausalEvidence"},"type":"array","title":"Causal Evidence"},"verified":{"type":"boolean","title":"Verified","description":"True if all anchors hash-verified by validator","default":true},"verification_source":{"$ref":"#/components/schemas/VerificationSource","default":"unknown"},"submitted_by_validator_uid":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Submitted By Validator Uid"},"merged_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Merged At"}},"type":"object","required":["relationship_id","source_entity_id","target_entity_id","relationship_type","source_url","source_date"],"title":"Relationship","description":"An edge in the knowledge graph connecting two entities.\n\nThe `causal_evidence` list carries reproducible causal claims attached to\nthis edge. Multiple miners can submit evidence using different methods;\nall are retained and consensus is computed at query time."},"RelationshipType":{"type":"string","enum":["supplies","competes_with","employs","board_member_of","invested_in","subsidiary_of","partners_with","depends_on","litigates_against","licensed_from","revenue_from","produces"],"title":"RelationshipType"},"RevokeValidatorRequest":{"properties":{"validator_uid":{"type":"integer","title":"Validator Uid"},"after_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"After Time"}},"type":"object","required":["validator_uid"],"title":"RevokeValidatorRequest","description":"Request body for /admin/validator/revoke."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerificationSource":{"type":"string","enum":["whitelisted_fetch","paywalled_content","url_only","unknown"],"title":"VerificationSource","description":"How a claim's source document was verified.\n\n- WHITELISTED_FETCH: validator independently fetched the URL from a\n  whitelisted domain and the bytes hashed to the miner's claimed hash.\n  This is the only source that gives `verified=True`.\n- PAYWALLED_CONTENT: miner provided the bytes inline because the source\n  is paywalled / not publicly fetchable. Hash matches what the miner\n  claimed but origin can't be independently verified. `verified=False`.\n- URL_ONLY: non-whitelisted public URL. Validator did not fetch.\n  Treated as a tip — `verified=False`. Useful for hints that may\n  later be backed by a whitelisted source.\n- UNKNOWN: legacy / unset."}}}}