Files
alva/felsokning/services/plattform/openapi.yaml
T
Claude 301c477e25 Merge main: Guidad Felsökning flyttar ur roten till felsokning/
Main är sedan den här grenen skapades en helt annan produkt — Semantika,
en mobilapp med egen CDK-infrastruktur. Den äger nu repots rot: en
npm-workspaces-monorepo med apps/mobile, services/api och infra.

För att båda ska rymmas i samma repo flyttar Guidad Felsökning in i en
egen katalog i stället för att göra anspråk på roten:

  felsokning/app        webbklienten (Vite, egen package.json och
                        eslint-/vitest-konfiguration)
  felsokning/services   plattformstjänsten och AI-orkestern
  felsokning/infra      Terraform och databasschemat
  felsokning/docs       vision, moduler, drift
  felsokning/supabase   edge-funktion och migrationer

Merge:n hade tagit bort 128 filer som Guidad Felsökning bygger på —
värdapplikationens komponenter, Supabase-klienten, tillgångar — eftersom
main raderat dem och den här grenen inte råkat ändra just dem. De är
återställda på sin nya plats. Utan dem gick varken bygget eller
testerna: ai.ts och synk.ts importerar Supabase-klienten.

Semantikas rotfiler är orörda: package.json, eslint.config.js och
.github/workflows/ är deras. Guidad Felsökning har egna motsvarigheter i
sin katalog.

CI flyttar samtidigt från GitHub Actions till .gitea/workflows — samma
syntax, egna runners. .github/workflows/ tillhör Semantika härefter.

Verifierat på den nya platsen: 96 vitest-tester, typkontroll, eslint på
både klient och tjänster, bygge, och integrationstest mot riktig Postgres.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-04 12:22:49 +00:00

1112 lines
38 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
openapi: 3.0.3
info:
title: Guidad Felsökning plattforms-API
version: 1.0.0
description: |
API-first-ytan för Guidad Felsökning: autentisering, organisationens
användare, ärenden med append-only händelselogg, arbetsledaröversikt,
publik Live Share-delning samt AI-orkestern.
**Händelseloggen är append-only.** Det finns inga update- eller
delete-operationer — historik kan aldrig ändras eller raderas, vilket
även garanteras av databastriggers. En händelse med redan känt `id`
ignoreras tyst (idempotent synk).
**Multi-tenant.** All ärendedata är organisationsknuten. Ett ärende i
en annan organisation ger `404`, som om det inte fanns.
Specen serveras live av tjänsten på `GET /api/openapi.yaml`.
servers:
- url: https://app.exempel.se
description: Klustrets ingress (ersätt med er domän)
tags:
- name: Auth
- name: Användare
- name: Ärenden
- name: Översikt
- name: Delning
- name: AI
- name: Integrationer
- name: Drift
paths:
/halsa:
get:
tags: [Drift]
summary: Hälsokontroll
security: []
responses:
"200":
description: Tjänsten svarar.
content:
application/json:
schema:
type: object
properties:
status: { type: string, example: ok }
/api/openapi.yaml:
get:
tags: [Drift]
summary: Denna specifikation
security: []
responses:
"200":
description: OpenAPI-specen i YAML.
content:
application/yaml:
schema: { type: string }
/api/auth/registrera:
post:
tags: [Auth]
summary: Skapa organisation + systemadministratör
description: >
Registrerar en ny organisation (tenant) och dess första användare,
som blir systemadministratör. Kan stängas av driften
(REGISTRERING_OPPEN=false → 403).
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [epost, losenord, namn, organisation]
properties:
epost: { type: string, format: email }
losenord: { type: string, minLength: 8 }
namn: { type: string }
organisation: { type: string }
responses:
"200": { $ref: "#/components/responses/Inloggad" }
"400": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
"409": { $ref: "#/components/responses/Fel" }
/api/auth/logga-in:
post:
tags: [Auth]
summary: Logga in
description: >
Takt-begränsad i databasen, så spärren håller bakom flera
repliker: 10 misslyckade försök per konto och 30 per källadress
inom 15 minuter ger 429. Spärren gäller kontot även vid rätt
lösenord — annars vore den meningslös. Ett avstängt konto svarar
403.
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [epost, losenord]
properties:
epost: { type: string, format: email }
losenord: { type: string }
responses:
"200": { $ref: "#/components/responses/Inloggad" }
"401": { $ref: "#/components/responses/Fel" }
"403":
description: Kontot är avstängt.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
"429":
description: För många misslyckade försök.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
/api/anvandare:
get:
tags: [Användare]
summary: Lista organisationens användare
description: Kräver rollen `admin` eller `arbetsledare`.
responses:
"200":
description: Användare i den egna organisationen.
content:
application/json:
schema:
type: object
properties:
anvandare:
type: array
items: { $ref: "#/components/schemas/Anvandare" }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
post:
tags: [Användare]
summary: Skapa användare i den egna organisationen
description: Kräver rollen `admin`.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [epost, losenord, namn, roll]
properties:
epost: { type: string, format: email }
losenord: { type: string, minLength: 8 }
namn: { type: string }
roll: { $ref: "#/components/schemas/Roll" }
responses:
"200":
description: Den skapade användaren.
content:
application/json:
schema: { $ref: "#/components/schemas/Anvandare" }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
"409": { $ref: "#/components/responses/Fel" }
/api/ecm/regler:
get:
tags: [Drift]
summary: ECM Knowledge Library — aktuellt regelpaket
description: >
Serverdistribuerat, versionerat regelpaket (compliance-regler per
ärendetyp, orsakskategorier, evidenskällor, undantagsorsaker).
Uppdateras i driften utan appändring; klienten cachar och faller
tillbaka till sitt inbyggda standardpaket offline.
responses:
"200":
description: Regelpaketet.
content:
application/json:
schema:
type: object
properties:
version: { type: string }
arendetypRegler: { type: object, additionalProperties: true }
undantagsorsaker: { type: array, items: { type: string } }
orsakskategorier: { type: array, items: { type: string } }
underlagskallor: { type: array, items: { type: string } }
/api/fordon/{identifierare}/historik:
get:
tags: [Ärenden]
summary: Fordonshistorik — tidigare ärenden på samma objekt
description: >
Organisationens tidigare ärenden där objektet (regnr/VIN) matchar,
med dokumenterade felorsaker. Underlag för pre-diagnostikens
historiksteg och orsakskedjan.
parameters:
- name: identifierare
in: path
required: true
schema: { type: string }
responses:
"200":
description: Tidigare ärenden (senaste 20).
content:
application/json:
schema:
type: object
properties:
arenden:
type: array
items:
type: object
properties:
id: { type: string }
nummer: { type: integer }
skapad: { type: string, format: date-time }
avslutat: { type: boolean }
felbeskrivning: { type: string, nullable: true }
felorsaker: { type: array, items: { type: object, additionalProperties: true } }
"401": { $ref: "#/components/responses/Fel" }
/api/statistik/felorsaker:
get:
tags: [Översikt]
summary: Felorsaksstatistik per orsakskategori
description: >
Flottdata ur dokumenterade felorsaksanalyser i organisationen.
Kräver rollen `arbetsledare` eller `admin`.
responses:
"200":
description: Antal per orsakskategori, fallande.
content:
application/json:
schema:
type: object
properties:
orsaker:
type: array
items:
type: object
properties:
orsak: { type: string }
antal: { type: integer }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
/api/anvandare/{anvandarId}/avaktivera:
post:
tags: [Användare]
summary: Stäng av ett konto
description: >
Kräver rollen `admin` och att användaren tillhör samma
organisation. Avstängningen höjer kontots token-version, så
**pågående sessioner upphör omedelbart** — annars vore den
verkningslös tills utfärdade tokens gick ut. Ett konto kan inte
stänga av sig självt.
parameters:
- name: anvandarId
in: path
required: true
schema: { type: string, format: uuid }
responses:
"200":
description: Kontot är avstängt.
content:
application/json:
schema:
type: object
properties:
id: { type: string, format: uuid }
namn: { type: string }
aktiv: { type: boolean }
"400":
description: Försök att stänga av sitt eget konto.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
/api/anvandare/{anvandarId}/aktivera:
post:
tags: [Användare]
summary: Öppna ett avstängt konto
description: >
Kräver rollen `admin`. Kontot kan logga in igen, men tokens som
återkallades vid avstängningen förblir ogiltiga.
parameters:
- name: anvandarId
in: path
required: true
schema: { type: string, format: uuid }
responses:
"200": { description: Kontot är öppnat. }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
/api/auth/logga-ut-alla:
post:
tags: [Auth]
summary: Logga ut på alla enheter
description: >
Höjer den egna token-versionen, vilket gör samtliga utfärdade
tokens för kontot ogiltiga direkt — vägen ut när en enhet
tappats bort.
responses:
"200": { description: Alla sessioner är avslutade. }
"401": { $ref: "#/components/responses/Fel" }
/api/arenden/{arendeId}/bilagor:
post:
tags: [Ärenden]
summary: Ladda upp en bilaga
description: >
Foton, videoklipp och instrumentbilder. Kroppen är råa bytes och
`Content-Type` anger mediatypen — endast bild och video tas emot.
Servern beräknar innehållets SHA-256 och returnerar en referens
som ska läggas i händelsen; **hashen hamnar därmed i den
append-only-skyddade loggen**, så en utbytt bild går att upptäcka.
Innehållsadresserat: samma innehåll lagras en gång.
parameters:
- name: arendeId
in: path
required: true
schema: { type: string }
requestBody:
required: true
content:
image/jpeg: { schema: { type: string, format: binary } }
image/png: { schema: { type: string, format: binary } }
image/webp: { schema: { type: string, format: binary } }
video/mp4: { schema: { type: string, format: binary } }
video/webm: { schema: { type: string, format: binary } }
responses:
"200":
description: Referensen att spara i händelsen.
content:
application/json:
schema:
type: object
properties:
id: { type: string }
hash: { type: string, description: "SHA-256 av innehållet, hex." }
mediatyp: { type: string }
storlek: { type: integer }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
"413":
description: Bilagan är större än 32 MB.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
"415":
description: Endast bilder och videoklipp tas emot.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
/api/bilagor/{bilagaId}:
get:
tags: [Ärenden]
summary: Hämta en bilaga
description: >
Organisationsknuten. Innehållet kontrolleras mot hashen innan det
lämnas ut — stämmer det inte svarar tjänsten 409 i stället för att
visa en bild som kan ha bytts ut.
parameters:
- name: bilagaId
in: path
required: true
schema: { type: string }
responses:
"200":
description: Innehållet.
content:
application/octet-stream:
schema: { type: string, format: binary }
"401": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
"409":
description: Innehållet stämmer inte med hashen i loggen.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
/api/delad/{delningskod}/bilagor/{bilagaId}:
get:
tags: [Delning]
summary: Hämta en bilaga via delningslänk
description: >
Samma filtrering som händelserna: bilagan lämnas bara ut om
händelsen den hör till är synlig på delningens nivå. En bild som
hör till en intern händelsetyp — t.ex. den skannade arbetsordern —
nås alltså aldrig via kundlänken.
security: []
parameters:
- name: delningskod
in: path
required: true
schema: { type: string }
- name: bilagaId
in: path
required: true
schema: { type: string }
responses:
"200":
description: Innehållet.
content:
application/octet-stream:
schema: { type: string, format: binary }
"404": { $ref: "#/components/responses/Fel" }
"409": { $ref: "#/components/responses/Fel" }
/api/integrationer/leverantorer:
get:
tags: [Integrationer]
summary: Registret över märkesspecifika kopplingar
description: >
Leverantörer är data, inte kod: registret läses ur
`integrationer.json` (eller filen i `INTEGRATIONER_FIL`) och kan
bytas via ConfigMap utan att applikationen byggs om. Innehåller
endast fältdefinitioner — aldrig någon organisations uppgifter.
responses:
"200":
description: Leverantörsdefinitioner.
content:
application/json:
schema:
type: object
properties:
version: { type: string }
leverantorer:
type: array
items:
type: object
properties:
id: { type: string }
namn: { type: string }
beskrivning: { type: string }
nyckeltyp:
type: string
enum: [vin, regnr]
description: Vad uppslaget sker på. Utelämnat betyder VIN.
falt:
type: array
items:
type: object
properties:
nyckel: { type: string }
etikett: { type: string }
hemlig:
type: boolean
description: >
Hemliga fält maskeras alltid i svar och
visas aldrig igen efter sparande.
"401": { $ref: "#/components/responses/Fel" }
/api/integrationer:
get:
tags: [Integrationer]
summary: Organisationens konfigurerade kopplingar
description: >
Kräver rollen `admin`. Uppgifterna lagras krypterade
(AES-256-GCM) och returneras alltid maskerade — hemliga värden
lämnar aldrig servern i klartext.
responses:
"200":
description: Konfigurerade kopplingar med maskerade uppgifter.
content:
application/json:
schema:
type: object
properties:
krypteringKonfigurerad:
type: boolean
description: Falskt om `INTEGRATION_NYCKEL` saknas — då kan inget sparas.
integrationer:
type: array
items:
type: object
properties:
leverantor: { type: string }
namn: { type: string }
aktiv: { type: boolean }
uppdaterad: { type: string, format: date-time }
senast_testad: { type: string, format: date-time, nullable: true }
senaste_status: { type: string, nullable: true }
uppgifter:
type: object
additionalProperties: { type: string }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
post:
tags: [Integrationer]
summary: Spara eller uppdatera en kopplings uppgifter
description: >
Kräver rollen `admin`. Endast leverantörens definierade fält
sparas och samtliga måste fyllas i. Uppgifterna krypteras innan
de skrivs. Ett sparande nollställer tidigare testresultat.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [leverantor, uppgifter]
properties:
leverantor: { type: string }
aktiv: { type: boolean, default: true }
uppgifter:
type: object
additionalProperties: { type: string }
responses:
"200": { description: Sparad. }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
"503":
description: Kryptering är inte konfigurerad (`INTEGRATION_NYCKEL` saknas).
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
/api/integrationer/{leverantor}:
delete:
tags: [Integrationer]
summary: Ta bort en kopplings uppgifter
description: Kräver rollen `admin`.
parameters:
- name: leverantor
in: path
required: true
schema: { type: string }
responses:
"200": { description: Borttagen. }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
/api/integrationer/{leverantor}/uppslag:
post:
tags: [Integrationer]
summary: Slå upp ett fordon hos leverantören
description: >
Anropet görs alltid av servern — kundens leverantörsnycklar når
aldrig webbläsaren. Svaret mappas till våra fält enligt
registrets `svarsfalt`. Resultatet skrivs som kopplingens
senaste teststatus.
parameters:
- name: leverantor
in: path
required: true
schema: { type: string }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [identifierare]
properties:
identifierare:
type: string
description: VIN eller registreringsnummer beroende på leverantörens `nyckeltyp`.
responses:
"200":
description: Fordonsuppgifter från leverantören.
content:
application/json:
schema:
type: object
properties:
fordon:
type: object
additionalProperties: { type: string }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"404":
description: Kopplingen är inte konfigurerad för organisationen.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
"502":
description: Leverantören svarade med fel eller inga kända fält.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
"503":
description: Kryptering är inte konfigurerad.
content:
application/json:
schema: { $ref: "#/components/schemas/Fel" }
/api/delad/{delningskod}/beslut:
post:
tags: [Delning]
summary: Kundens besked på ett åtgärdsförslag
description: >
Den enda skrivande publika vägen. Endast delningar på **kundnivå**
som inte återkallats får svara, det måste finnas ett
åtgärdsförslag, och **ett besked per ärende** — svaret kan inte
ändras i efterhand. Takt-begränsad per delningskod. Beskedet
loggas som `kundbeslut` med kanal `Delningslänk`.
security: []
parameters:
- name: delningskod
in: path
required: true
schema: { type: string }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [beslut]
properties:
beslut: { type: string, enum: [godkant, avbojt, delvis] }
kommentar: { type: string, maxLength: 500 }
responses:
"200":
description: Beskedet registrerat.
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
"400": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
"409": { $ref: "#/components/responses/Fel" }
"429": { $ref: "#/components/responses/Fel" }
/api/organisation:
get:
tags: [Organisation]
summary: Hämta organisationens namn och inställningar
description: >
Inställningarna styr vad som visas när ett ärende startas
(objekttyper och identifieringsmetoder). Alla inloggade läser.
responses:
"200":
description: Organisationen.
content:
application/json:
schema:
type: object
properties:
namn: { type: string }
installningar: { $ref: "#/components/schemas/OrganisationsInstallningar" }
"401": { $ref: "#/components/responses/Fel" }
/api/organisation/installningar:
post:
tags: [Organisation]
summary: Uppdatera organisationens inställningar
description: Kräver rollen `admin`. Minst ett alternativ per lista.
requestBody:
required: true
content:
application/json:
schema: { $ref: "#/components/schemas/OrganisationsInstallningar" }
responses:
"200":
description: Sparat.
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
/api/arenden:
get:
tags: [Ärenden]
summary: Lista organisationens ärenden
responses:
"200":
description: Ärenden (senaste 200).
content:
application/json:
schema:
type: object
properties:
arenden:
type: array
items: { $ref: "#/components/schemas/Arende" }
"401": { $ref: "#/components/responses/Fel" }
post:
tags: [Ärenden]
summary: Registrera ett ärende (idempotent)
description: >
Skapar ärendet i användarens organisation. Ett redan känt `id`
ignoreras tyst — ärenderaden ändras aldrig.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [id, nummer, skapad]
properties:
id: { type: string }
nummer: { type: integer }
skapad: { type: string, format: date-time }
delningskod: { type: string, nullable: true }
metodikId: { type: string, nullable: true }
responses:
"200":
description: Mottaget.
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
/api/arenden/{arendeId}/handelser:
parameters:
- name: arendeId
in: path
required: true
schema: { type: string }
get:
tags: [Ärenden]
summary: Hämta ärendets händelselogg
responses:
"200":
description: Händelser i tidsordning.
content:
application/json:
schema:
type: object
properties:
handelser:
type: array
items: { $ref: "#/components/schemas/LoggPost" }
"401": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
post:
tags: [Ärenden]
summary: Lägg till händelser (append-only, idempotent)
description: >
Lägger till händelser i loggen. Max 500 per anrop. En händelse med
redan känt `id` ignoreras tyst — befintliga händelser skrivs aldrig
över, och databastriggern stoppar alla ändringsförsök.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [handelser]
properties:
handelser:
type: array
maxItems: 500
items: { $ref: "#/components/schemas/LoggPost" }
responses:
"200":
description: Mottaget.
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
/api/oversikt:
get:
tags: [Översikt]
summary: Organisationsöversikt med status och statistikunderlag
description: >
Kräver rollen `arbetsledare` eller `admin`. Status, deltagande
tekniker och sammanfattning härleds ur händelseloggen.
responses:
"200":
description: Alla ärenden i organisationen med härledd status.
content:
application/json:
schema:
type: object
properties:
arenden:
type: array
items: { $ref: "#/components/schemas/OversiktsRad" }
"401": { $ref: "#/components/responses/Fel" }
"403": { $ref: "#/components/responses/Fel" }
/api/arenden/{arendeId}/delningar:
parameters:
- name: arendeId
in: path
required: true
schema: { type: string }
get:
tags: [Delning]
summary: Lista ärendets delningslänkar
responses:
"200":
description: Delningar (inklusive återkallade).
content:
application/json:
schema:
type: object
properties:
delningar:
type: array
items: { $ref: "#/components/schemas/Delning" }
"401": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
post:
tags: [Delning]
summary: Skapa delningslänk med behörighetsnivå
description: >
Verkstaden kontrollerar alltid delningen: kund (det kunddelbara),
partner (även hypoteser, tydligt märkta ej verifierade) eller
intern (full insyn). Nivåfiltreringen sker på serversidan.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [niva]
properties:
niva: { $ref: "#/components/schemas/DelningsNiva" }
responses:
"200":
description: Den skapade länkens kod.
content:
application/json:
schema:
type: object
properties:
kod: { type: string }
niva: { $ref: "#/components/schemas/DelningsNiva" }
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
/api/delningar/{kod}/aterkalla:
post:
tags: [Delning]
summary: Återkalla en delningslänk
description: Efter återkallelse ger länken 404. Kan inte ångras.
parameters:
- name: kod
in: path
required: true
schema: { type: string }
responses:
"200":
description: Återkallad.
content:
application/json:
schema:
type: object
properties:
ok: { type: boolean }
"401": { $ref: "#/components/responses/Fel" }
"404": { $ref: "#/components/responses/Fel" }
/api/delad/{delningskod}:
get:
tags: [Delning]
summary: Publik Live Share-vy via delningskod
description: >
Kräver ingen inloggning — delningskoden är nyckeln. Filtreringen
styrs av länkens behörighetsnivå: kund utesluter kategoribyten,
hypoteser och AI-dialog; partner utesluter kategoribyten och
AI-dialog; intern visar allt. Ärendets ursprungliga delningskod
fungerar bakåtkompatibelt som kundnivå.
security: []
parameters:
- name: delningskod
in: path
required: true
schema: { type: string, pattern: "^[A-Za-z0-9_-]+$" }
responses:
"200":
description: Ärendet med nivåfiltrerad händelselogg.
content:
application/json:
schema:
type: object
properties:
arende:
type: object
properties:
id: { type: string }
nummer: { type: integer }
skapad: { type: string, format: date-time }
handelser:
type: array
items: { $ref: "#/components/schemas/LoggPost" }
niva: { $ref: "#/components/schemas/DelningsNiva" }
"404": { $ref: "#/components/responses/Fel" }
/api/ai:
post:
tags: [AI]
summary: AI-orkestern (separat tjänst bakom samma ingress)
description: >
Routas av ingressen till ai-orkester-tjänsten. Servern äger
modellval, effort, systemprompt och svarsschema per uppgift:
handledning (Claude Sonnet 5), granskning (Claude Opus 5),
sammanfattning (Claude Sonnet 5), metodikval (Claude Haiku 4.5).
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [uppgift, prompt]
properties:
uppgift:
type: string
enum: [handledning, granskning, sammanfattning, metodikval]
prompt: { type: string, maxLength: 40000 }
responses:
"200":
description: Klassificerat AI-svar med modellen som svarade.
content:
application/json:
schema:
type: object
properties:
modell: { type: string, example: claude-sonnet-5 }
svar:
oneOf:
- $ref: "#/components/schemas/AiSvar"
- type: object
properties:
metodikId:
type: string
enum: [vibration, elsystem, generisk]
"400": { $ref: "#/components/responses/Fel" }
"401": { $ref: "#/components/responses/Fel" }
"502": { $ref: "#/components/responses/Fel" }
"503": { $ref: "#/components/responses/Fel" }
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: >
HS256-JWT från /api/auth/logga-in eller /api/auth/registrera.
Anspråk: sub (användar-id), namn, org (organisations-id),
roll, iat, exp (12 timmar).
responses:
Inloggad:
description: Inloggad — token + kontouppgifter.
content:
application/json:
schema:
type: object
properties:
token: { type: string }
namn: { type: string }
roll: { $ref: "#/components/schemas/Roll" }
organisation: { type: string }
Fel:
description: Felsvar.
content:
application/json:
schema:
type: object
properties:
error: { type: string }
schemas:
Fel:
type: object
properties:
error: { type: string }
Roll:
type: string
enum: [tekniker, arbetsledare, admin]
OrganisationsInstallningar:
type: object
required: [objekttyper, identifieringsmetoder]
properties:
objekttyper:
type: array
minItems: 1
items: { type: string }
identifieringsmetoder:
type: array
minItems: 1
items: { type: string }
DelningsNiva:
type: string
enum: [kund, partner, intern]
Delning:
type: object
properties:
kod: { type: string }
niva: { $ref: "#/components/schemas/DelningsNiva" }
skapad: { type: string, format: date-time }
aterkallad: { type: string, format: date-time, nullable: true }
Anvandare:
type: object
properties:
id: { type: string, format: uuid }
epost: { type: string, format: email }
namn: { type: string }
roll: { $ref: "#/components/schemas/Roll" }
Arende:
type: object
properties:
id: { type: string }
nummer: { type: integer }
skapad: { type: string, format: date-time }
delningskod: { type: string, nullable: true }
metodik_id: { type: string, nullable: true }
LoggPost:
type: object
required: [id, tidpunkt, anvandare, handelse]
properties:
id: { type: string }
tidpunkt: { type: string, format: date-time }
anvandare: { type: string }
handelse: { $ref: "#/components/schemas/Handelse" }
Handelse:
type: object
description: >
Diskriminerad på `typ`; fullständiga fältdefinitioner i klientens
domänmodell (src/felsokning/domain.ts).
required: [typ]
additionalProperties: true
properties:
typ:
type: string
enum:
- objekt_identifierat
- felbeskrivning
- fraga_besvarad
- kontroll_utford
- observation
- matvarde
- hypotes
- foto
- kommentar
- kategori_byte
- inaktivitet_forklarad
- overlamning
- ansvarig_satt
- arbetsorder_skannad
- arendetyp_satt
- historik_kontrollerad
- matarstallning
- video
- atgardsforslag
- kundbeslut
- atgard_utford
- kvalitetskontroll
- reproducering
- felorsak
- export_skapad
- ai_svar
- arende_avslutat
OversiktsRad:
type: object
properties:
id: { type: string }
nummer: { type: integer }
skapad: { type: string, format: date-time }
delningskod: { type: string, nullable: true }
metodik_id: { type: string, nullable: true }
antal_handelser: { type: integer }
forsta: { type: string, format: date-time, nullable: true }
senaste: { type: string, format: date-time, nullable: true }
avslutat: { type: boolean }
objekt: { type: string, nullable: true }
felbeskrivning: { type: string, nullable: true }
tekniker:
type: array
items: { type: string }
nullable: true
AiSvar:
type: object
required: [rader, nastaSteg]
properties:
rader:
type: array
items:
type: object
required: [typ, text]
properties:
typ:
type: string
enum: [observation, verifierat, hypotes, rekommendation]
text: { type: string }
nastaSteg: { type: string }
security:
- bearerAuth: []