{"openapi":"3.1.0","info":{"title":"Agentanbud","version":"0.3.0+163b6ab"},"paths":{"/mcp":{"get":{"summary":"Mcp Get","description":"MCP server info — clients call this to discover capabilities.","operationId":"mcp_get_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Mcp Post","description":"Handle JSON-RPC 2.0 MCP requests.\n\nPer MCP spec, the request body is a single JSON-RPC object (not batched).\nResponses follow JSON-RPC 2.0:\n  - Success: { jsonrpc, id, result }\n  - Error:   { jsonrpc, id, error: { code, message, data? } }","operationId":"mcp_post_mcp_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"delete":{"summary":"Mcp Delete","description":"End a session (MCP Streamable HTTP).","operationId":"mcp_delete_mcp_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"options":{"summary":"Mcp Options","description":"CORS preflight.","operationId":"mcp_options_mcp_options","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/analytics":{"get":{"summary":"Api Analytics","description":"Public usage figures as JSON — the same numbers as /analytics.\n\nExists so an operator agent without MCP (or any script) can report on\ntraffic without scraping the HTML page, which llms.txt tells agents\nnot to do. Aggregate only: no IP addresses, no visitor ids, nothing\nper-person. Free to republish.\n\n`days` limits the window: 1 for the last 24h, 7 for a week, omitted\nfor all time. The daily series always covers the last 30 days.","operationId":"api_analytics_api_analytics_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":365,"minimum":1},{"type":"null"}],"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/health":{"get":{"summary":"Health","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Api Health Get"}}}}}}},"/api/stats":{"get":{"summary":"Stats","operationId":"stats_api_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Stats Api Stats Get"}}}}}}},"/api/winners":{"get":{"summary":"Winners","description":"Who wins contracts in a given area (from TED award notices).\n\nFilter by authority and/or cpv prefix. Returns suppliers ranked by\nnumber of awards won, with total awarded value. At least one filter\nis required to keep the aggregation meaningful.","operationId":"winners_api_winners_get","parameters":[{"name":"authority","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authority"}},{"name":"cpv","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cpv"}},{"name":"top","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":15,"title":"Top"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Winners Api Winners Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blog":{"get":{"summary":"Api Blog List","operationId":"api_blog_list_api_blog_get","parameters":[{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tag"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"summary":"Api Blog Create","description":"Create a post (admin/agent only). Body: title, body_md, summary?, tags?.","operationId":"api_blog_create_api_blog_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/blog/{slug}":{"get":{"summary":"Api Blog Get","operationId":"api_blog_get_api_blog__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Api Blog Update","operationId":"api_blog_update_api_blog__slug__put","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blog/{slug}/stats":{"get":{"summary":"Api Blog Stats","operationId":"api_blog_stats_api_blog__slug__stats_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blog/{slug}/event":{"post":{"summary":"Api Blog Event","description":"Privacy-preserving engagement beacon. Body: {\"kind\": \"view\"|\"read\"}.","operationId":"api_blog_event_api_blog__slug__event_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tenders":{"get":{"summary":"List Tenders","operationId":"list_tenders_api_tenders_get","parameters":[{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},{"name":"authority","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authority"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"cpv","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CPV code prefix, e.g. '72' (IT) or '45' (construction)","title":"Cpv"},"description":"CPV code prefix, e.g. '72' (IT) or '45' (construction)"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":25,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Tenders Api Tenders Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tenders/{tid}":{"get":{"summary":"Get Tender","operationId":"get_tender_api_tenders__tid__get","parameters":[{"name":"tid","in":"path","required":true,"schema":{"type":"integer","title":"Tid"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Tender Api Tenders  Tid  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sync":{"post":{"summary":"Trigger Sync","description":"Fire-and-forget: spawn the orchestrator in the background.","operationId":"trigger_sync_api_sync_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/backfill":{"post":{"summary":"Backfill","description":"Trigger a backfill with a longer lookback for TED EU.\nDefault: 90 days. Max: 365 days.\nTED has ~6500 SWE notices per 90 days.","operationId":"backfill_api_backfill_post","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":90,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/repair-links":{"post":{"summary":"Repair Links","description":"Repair tender links (2026-07 URL audit).\n\n1. ted / ted_awards / ted_pin: rebuild as /en/notice/-/detail/{nr}\n   — the only form that renders in a browser. Both /en/notice/{nr}\n   and /en/notice/{nr}/html bounce users to the TED homepage.\n2. mercell: delete all rows and re-sync. Old rows are keyed on\n   unstable search-index ids (duplicates after Mercell\n   re-indexes) and point at the dead /sv-SE/m/tender/ route.\n   The scraper now keys on repsNoticeId, links to /tender/{id}\n   and skips Mercell's TED mirrors.","operationId":"repair_links_api_repair_links_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/reset-ted":{"post":{"summary":"Reset Ted","description":"Purge and rebuild the three TED sources (2026-07 notice-type fix).\n\nThe old scrapers filtered on legacy notice-subtype numbers, which the\nTED expert-search silently mapped to cn-standard. Result: `ted` mixed\nawards/PINs in with open tenders, and `ted_awards`/`ted_pin` were 100%\nduplicates of `ted` with no winner data. The scrapers now filter on\nnotice-type (cn-* / can-* / pin-*). Old rows won't be overwritten by\nthe new (different) publication numbers, so we delete first, then\nre-sync the three sources with a wider lookback.","operationId":"reset_ted_api_reset_ted_post","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":180,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/query":{"post":{"summary":"Admin Query","description":"Read-only SQL access for maintenance and diagnostics.\n\nRequires ADMIN_API_KEY to be configured AND supplied — unlike the\nmutating endpoints this one fails closed when no key is set.\nSELECT/WITH single statements only; the connection is opened with\nPRAGMA query_only so writes are rejected at the SQLite level too.\n\nBody: {\"sql\": \"SELECT ...\", \"params\": [...]}\nReturns: {\"columns\": [...], \"rows\": [[...], ...], \"truncated\": bool}","operationId":"admin_query_api_admin_query_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/knowledge":{"get":{"summary":"List Knowledge","operationId":"list_knowledge_api_knowledge_get","parameters":[{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"criteria | questions","title":"Source"},"description":"criteria | questions"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":25,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Knowledge Api Knowledge Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/knowledge/stats":{"get":{"summary":"Knowledge Stats","description":"Counts per source_system, and per top category.","operationId":"knowledge_stats_api_knowledge_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Knowledge Stats Api Knowledge Stats Get"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}