Přeskočit obsah

API endpointy

Backend FastAPI exposuje REST API přes https://nemoreport-ai-backend-v2.sliplane.app. Tato kapitola dokumentuje všechny endpointy, jejich auth, rate limity a request/response shapes.

Auto-generated OpenAPI docs

<backend>/docs (Swagger UI) — automaticky generované z FastAPI route signatures + Pydantic models. Tato dokumentace popisuje použití + sémantiku.

Auth schémata

Schéma Použití Header
JWT Bearer User-facing endpointy (frontend) Authorization: Bearer <supabase_jwt>
Admin Hash /admin/* endpointy X-Admin-Hash: <hash> nebo ?hash=<hash> query
HMAC /reports/{id}/attachments/system (Nette) X-Nette-Signature: <hmac_sha256>
No auth /health, public docs

Health check

GET /health

{ "status": "ok", "schema": "nemoreport", "redis": "connected" }

Sliplane healthcheck endpoint, never auth-required.

User profile

GET /me

Auth: JWT Rate limit: 60/minute

Vrací profile usera derived z JWT claims + DB lookup.

Response:

{
  "id": "uuid",
  "email": "user@example.com",
  "personal_tenant_id": "uuid",
  "active_tenant_id": "uuid",
  "display_name": "Jan Novák"
}

Models config

GET /models

Auth: JWT (volitelně, pro per-tenant override) Rate limit: 60/minute

Vrací dostupné LLM modely + default. Frontend použije pro chat model picker.

Reports (legacy v1 + v2)

GET /reports

Auth: JWT Response: list ReportSummary (RLS scoped per tenant)

POST /reports

Auth: JWT Use case: legacy MHTML upload (sync, pre-Phase B). Pro nové uploady použijte POST /ingest (async pipeline).

GET /reports/{report_id}

Auth: JWT Response: ReportDetail s metadata, parsed_markdown, parsed_metadata.

GET /reports/{report_id}/sections

Auth: JWT Response: list SectionInfo. Pro v2 reports vrací parsed_sections rows; pro v1 fallback regex split na clean_text.

GET /reports/{report_id}/sections/{section_name}

Auth: JWT Response: full section markdown.

Ingestion (Phase B)

POST /ingest

Auth: JWT Rate limit: 20/hour Body: multipart/form-data - file: PDF / PNG / JPEG / WEBP / MHTML / HTML / DOCX (max 50 MB) - title: optional string

Response 202 Accepted:

{
  "id": "uuid",
  "status": "uploaded",
  "filename": "report.pdf",
  "size_bytes": 374123,
  "content_type": "application/pdf",
  "title": "..."
}

Zpracování je async — frontend subscribe na Realtime channel report-{id} pro live status updates.

Errors: - 400 empty_file - 413 Request Entity Too Large - 415 Unsupported Media Type - 503 mistral_unavailable (pro PDF/image cesta pokud chybí MISTRAL_API_KEY)

GET /ingest/{report_id}/status

Auth: JWT Polling fallback pokud Realtime drop.

Response:

{
  "id": "uuid",
  "status": "embedding",
  "ingestion_started_at": "2026-04-30T08:00:00Z",
  "ingestion_finished_at": null,
  "ingestion_error": null,
  "ingestion_cost_cents": 18
}

GET /ingest/{report_id}/sections

Auth: JWT Response: list parsed_sections rows pro daný report (po status='parsed').

GET /ingest/{report_id}/figures

Auth: JWT Response: list figures s thumbnails URLs + annotation_json.

[
  {
    "id": "uuid",
    "section_name": "Riziko povodní",
    "page_number": 5,
    "storage_url": "https://cubdrgjdkatyecrgckwp.supabase.co/storage/v1/object/...",
    "annotation_source": "gemini",
    "annotation_quality_score": 0.85,
    "annotation_json": { "image_type": "map_flood", "summary": "...", "entities": [...], ... }
  }
]

DELETE /ingest/{report_id}

Auth: JWT (must be tenant member) Cascade: smaže report + všechny attachments + figures + parsed_sections + chunks. Storage objekty zůstávají orphan (admin cleanup TODO).

GET /ingest/{report_id}/attachments

Auth: JWT Response: list attachments (Nette + user_upload).

POST /ingest/{report_id}/uploads

Auth: JWT Use case: User přidá další soubor do reportu (folder model) Body: multipart/form-data (stejné jako POST /ingest) Effect: vytvoří attachment row s source='user_upload', enqueue run_ingestion('attachment', ...).

DELETE /ingest/{report_id}/attachments/{attachment_id}

Auth: JWT Restriction: user může smazat jen source='user_upload' (Nette systémové = immutable).

Retrieval (Phase C)

POST /retrieve

Auth: JWT Rate limit: 60/minute

Body:

{
  "query": "občanská vybavenost obchody dostupnost",
  "scope": {
    "type": "folder",
    "report_id": "uuid"
  },
  "top_k": 5
}

scope.type: - "folder" (Phase C MVP) — single report container, retrieves chunks from main + all attachments - "section" (plánované) — WHERE section_slug = X - "multi_report" (plánované) — WHERE report_id IN (...)

Response:

{
  "chunks": [
    {
      "id": "uuid",
      "report_id": "uuid",
      "attachment_id": null,
      "section_id": "uuid",
      "figure_id": null,
      "table_id": null,
      "content_type": "text",
      "source_type": "main",
      "section_name": "Občanská vybavenost",
      "section_slug": "obcanska-vybavenost",
      "attachment_filename": null,
      "source_label": "Občanská vybavenost",
      "content": "Sídla společností v domě...",
      "content_tokens": 220,
      "order_in_doc": 6,
      "embedding_type": "text",
      "rrf_score": 0.0327,
      "vector_rank": 1,
      "vector_dist": 0.34,
      "bm25_rank": 1,
      "bm25_score": 0.20,
      "rerank_score": 0.7151
    }
  ],
  "embed_ms": 360,
  "retrieval_ms": 58,
  "rerank_ms": 667,
  "scope_type": "folder",
  "top_k": 5,
  "fusion": "hybrid_rrf",
  "reranked": true,
  "used_hyde": false
}

Errors: - 404 report not in tenant scope - 501 scope.type not yet implemented (zatím jen folder) - 503 embedding unavailable

Chat (Phase A.1, Phase D rewrite TBD)

POST /chat

Auth: JWT Rate limit: 30/minute

Aktuálně používá full report text. Phase D bude rewire na /retrieve chunks.

Body:

{
  "report_id": "uuid",
  "conversation_id": "uuid",
  "message": "Jaká je občanská vybavenost?",
  "model_override": null
}

Response: SSE stream (Server-Sent Events) s JSON-encoded chunks (kvůli \n\n v markdown obsahu).

Conversations + feedback (Phase A.1)

GET /conversations?report_id=...

POST /conversations — create new

GET /conversations/{conv_id}/messages

PATCH /conversations/{conv_id} — rename

DELETE /conversations/{conv_id}

POST /feedback — thumbs up/down per message

Migration (v1 → v2 testers)

GET /migrate/status

Auth: JWT (preauth check by email) Vrací claim status pro user (nebyl claim / claim ready / claimed).

POST /migrate/claim

Auth: JWT Rate limit: 5/minute Importuje v1 reporty do v2 schema (přes nemoreport.import_v1() PL/pgSQL function).

Admin (HASH-gated)

GET /admin/verify

Vrací { ok: true } pokud hash valid.

Feedback dashboard

  • GET /admin/feedback — list feedback rows
  • GET /admin/feedback/stats{total, up, down} per report

LLM providers

  • GET /admin/providers — list per-tenant overrides
  • PUT /admin/providers/{provider_id} — update config

Migration

  • POST /admin/migrate/preauth — preregistrace email → claim ready
  • GET /admin/migrate/stats — claim flow analytics
  • POST /admin/migrate/reverse-claim — undo claim

Cost tracking

  • GET /admin/cost/global?days=N — top 10 tenants by cost (Phase B B.13)
  • GET /admin/cost/tenant/{tenant_id}?days=N — per-tenant breakdown + top reports

Embed backfill (Phase C C.13)

  • POST /admin/embed/{target_kind}/{target_id} — enqueue embed_target task
  • target_kind: report | attachment
  • Idempotent — worker DELETE existing chunks před re-insert

Retrieve diagnostic (Phase C E2E test)

  • POST /admin/retrieve/{report_id} — bypass JWT, full retrieve flow
  • Body: { "query": "...", "top_k": 5 }
  • Pro testing rerank + diversity bez JWT setup

Nette HMAC integration (Phase B.11, ready pre-Phase E)

POST /reports/{report_id}/attachments/system

Auth: HMAC nad canonical string <report_id>|<nette_id>|<attachment_type>|<sha256(file_bytes)> Header: X-Nette-Signature: <hex_hmac_sha256>

Body: multipart/form-data - file: PDF / image - nette_id: string (Nette přidělené ID — idempotence) - attachment_type: string (vyjadreni_cetin, geo_plan, foto, ...) - note: optional string

Response 201 Created:

{ "id": "uuid", "status": "uploaded", "report_id": "uuid", "nette_id": "..." }

Idempotence: stejný (report_id, nette_id) vrací existující attachment místo duplicate insert (UNIQUE partial index).

GET /reports/{report_id}/attachments/{attachment_id}/status

Auth: HMAC (stejný canonical string method) Response: status + ingestion_cost_cents pro Nette polling.

OpenAPI spec

<backend>/openapi.json (auto-generated). <backend>/docs (Swagger UI), <backend>/redoc (ReDoc UI).

Nett tým může z OpenAPI generovat client SDK.

Common errors

HTTP Příčina Akce
401 JWT missing/invalid/expired Frontend sign-out + re-login
403 Admin hash invalid nebo HMAC verify fail Check ENV
404 Resource not in tenant scope RLS deny → user nemůže vidět cizí data
413 File > 50 MB Chunkovat client-side
415 Unsupported MIME Allowlist kontrola
422 Pydantic validation Check request body shape
429 Rate limit Backoff + retry
500 Server error Check Sliplane logs
503 External API unavailable (Mistral, Gemini, Cohere) Graceful fallback nebo wait