> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leara.se/llms.txt
> Use this file to discover all available pages before exploring further.

# Fortnox-integrationen: arkitektur, dataflöden och säkerhet

> Teknisk översikt av Learas Fortnox-integration: OAuth 2.0-scopes, tvåvägssynk, realtidssynk, avstämning, anropsgränser och kända begränsningar.

Leara kopplas till Fortnox via Fortnox officiella API:er och håller data synkad åt båda håll: i realtid via Fortnox händelseström, med en automatisk avstämning var 15:e minut som skyddsnät. Kopplingen görs av kunden själv med OAuth 2.0 i Fortnox inloggning; Leara hanterar aldrig kundens Fortnox-lösenord.

## Sammanfattning

Leara har en egen, berikad datamodell. Produktdata, varianter, produktgrupper och kundregister ägs av Leara och skrivs till Fortnox. Bokföring, fakturanummer och betalstatus ägs av Fortnox och speglas till Leara. Fält som Fortnox saknar stöd för finns kvar i Leara och skrivs aldrig över av inkommande Fortnox-data.

Integrationen är byggd i Learas egen backend utan mellanliggande iPaaS eller tredjepartsconnector. Allt körs i AWS-regionen eu-north-1 (Stockholm).

## Arkitektur

Integrationen är en del av Learas backend och består av fyra delar: en skrivkö mot Fortnox, en realtidsklient mot Fortnox händelsetjänst, en schemalagd avstämning och en separat tokentjänst. Apparna pratar aldrig direkt med Fortnox.

<div className="leara-diagram my-6 w-full">
  <svg viewBox="0 0 960 440" width="100%" role="img" aria-label="Arkitekturdiagram för Leara och Fortnox">
    <defs>
      <marker id="fx-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
        <path d="M0,0 L10,5 L0,10 z" fill="currentColor" />
      </marker>
    </defs>

    <text x="95" y="80" textAnchor="middle" fontSize="15" fontWeight="600" fill="currentColor">Appar</text>

    <rect fill="none" x="20" y="150" width="150" height="44" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="95" y="177" textAnchor="middle" fontSize="14" fill="currentColor">Webbapp</text>

    <rect fill="none" x="20" y="210" width="150" height="44" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="95" y="237" textAnchor="middle" fontSize="14" fill="currentColor">iOS-app</text>

    <rect fill="none" x="20" y="270" width="150" height="44" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="95" y="297" textAnchor="middle" fontSize="14" fill="currentColor">Android-app</text>

    <line x1="172" y1="172" x2="238" y2="172" stroke="currentColor" strokeWidth="1.5" markerStart="url(#fx-arrow)" markerEnd="url(#fx-arrow)" />

    <line x1="172" y1="232" x2="238" y2="232" stroke="currentColor" strokeWidth="1.5" markerStart="url(#fx-arrow)" markerEnd="url(#fx-arrow)" />

    <line x1="172" y1="292" x2="238" y2="292" stroke="currentColor" strokeWidth="1.5" markerStart="url(#fx-arrow)" markerEnd="url(#fx-arrow)" />

    <rect fill="none" x="240" y="55" width="380" height="360" rx="14" stroke="currentColor" strokeWidth="1.5" />

    <text x="430" y="82" textAnchor="middle" fontSize="16" fontWeight="600" fill="currentColor">Leara backend</text>
    <text x="430" y="100" textAnchor="middle" fontSize="12" fill="currentColor" fillOpacity="0.65">AWS eu-north-1 (Stockholm)</text>

    <rect fill="none" x="270" y="115" width="320" height="40" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="430" y="140" textAnchor="middle" fontSize="14" fill="currentColor">Skrivkö</text>

    <rect fill="none" x="270" y="170" width="320" height="40" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="430" y="195" textAnchor="middle" fontSize="14" fill="currentColor">Avstämning (var 15:e min)</text>

    <rect fill="none" x="270" y="225" width="320" height="40" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="430" y="250" textAnchor="middle" fontSize="14" fill="currentColor">Realtidsklient</text>

    <rect fill="none" x="270" y="280" width="320" height="40" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="430" y="305" textAnchor="middle" fontSize="14" fill="currentColor">Tokentjänst</text>

    <rect fill="none" x="270" y="345" width="320" height="44" rx="8" stroke="currentColor" strokeOpacity="0.5" strokeDasharray="5 4" />

    <text x="430" y="372" textAnchor="middle" fontSize="14" fill="currentColor">Databas (MongoDB Atlas)</text>

    <line x1="592" y1="135" x2="718" y2="135" stroke="currentColor" strokeWidth="1.5" markerStart="url(#fx-arrow)" markerEnd="url(#fx-arrow)" />

    <line x1="592" y1="190" x2="718" y2="190" stroke="currentColor" strokeWidth="1.5" markerStart="url(#fx-arrow)" markerEnd="url(#fx-arrow)" />

    <line x1="718" y1="245" x2="592" y2="245" stroke="currentColor" strokeWidth="1.5" markerEnd="url(#fx-arrow)" />

    <line x1="592" y1="300" x2="718" y2="300" stroke="currentColor" strokeWidth="1.5" markerStart="url(#fx-arrow)" markerEnd="url(#fx-arrow)" />

    <rect fill="none" x="700" y="55" width="240" height="360" rx="14" stroke="currentColor" strokeWidth="1.5" />

    <text x="820" y="82" textAnchor="middle" fontSize="16" fontWeight="600" fill="currentColor">Fortnox</text>

    <rect fill="none" x="720" y="115" width="200" height="95" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="820" y="155" textAnchor="middle" fontSize="14" fill="currentColor">REST API v3</text>
    <text x="820" y="177" textAnchor="middle" fontSize="14" fill="currentColor">Warehouse API</text>

    <rect fill="none" x="720" y="225" width="200" height="40" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="820" y="250" textAnchor="middle" fontSize="14" fill="currentColor">Händelsetjänst (WebSocket)</text>

    <rect fill="none" x="720" y="280" width="200" height="40" rx="8" stroke="currentColor" strokeOpacity="0.5" />

    <text x="820" y="305" textAnchor="middle" fontSize="14" fill="currentColor">OAuth</text>
  </svg>

  <p className="mt-2 text-center text-sm opacity-70">All Fortnox-trafik går via Learas backend i AWS Stockholm.</p>
</div>

Dubbelpil betyder att data läses och skrivs; enkelpilen från Fortnox händelsetjänst går bara in till Leara. Alla Fortnox-objekt Leara arbetar med speglas i Learas databas, så apparna läser från Leara även när Fortnox är långsamt eller otillgängligt.

## Anslutning och behörighet

Kunden ansluter Fortnox i Learas webbapp med OAuth 2.0 (authorization code, offline-åtkomst). En administratör loggar in hos Fortnox, godkänner behörigheterna och skickas tillbaka till Leara. Ett Leara-konto kopplas till exakt ett Fortnox-bolag; underanvändare i Leara använder kontots koppling och kan inte ansluta egna.

| Scope | Används till |
| - | - |
| `article` | Artiklar och lagerartiklar |
| `price` | Prislistor och priser |
| `customer` | Kundkort |
| `supplier` | Leverantörer |
| `order` | Kundordrar |
| `offer` | Offerter |
| `invoice` | Fakturor och bokföring av fakturor |
| `payment` | Inbetalningar på fakturor |
| `warehouse` | Lagerställen, saldon, inleveranser, inköpsordrar, lagerflyttar och inventering |

Leara begär inte `bookkeeping`, `companyinformation` eller `settings` och har därmed ingen åtkomst till verifikationer, huvudbok eller bolagsinställningar.

### Tokenhantering

* Access-token och refresh-token lagras per kundkonto i Learas databas.
* En separat tokentjänst förnyar access-token var 5:e minut i god tid före utgång. Huvudtjänsten har en reservväg om förnyelsen skulle vara sen.
* Fortnox roterar refresh-token vid varje användning. Skrivningar sker villkorat (optimistisk låsning) så att två samtidiga förnyelser inte kan förstöra kopplingen.
* Kunden kan när som helst återkalla åtkomsten i Fortnox. Då upphör synken tills kopplingen görs om.

## Dataflöden

Varje dataobjekt har en bestämd ägare och en bestämd riktning. Ändringar i Leara skrivs till Fortnox via en kö, normalt inom sekunder; ändringar i Fortnox når Leara inom sekunder via händelseströmmen eller senast vid nästa avstämning.

| Objekt | Riktning | Utlösare | Ägare |
| - | - | - | - |
| Artiklar och produktdata | Båda håll | Realtid från Fortnox, avstämning var 15:e min; skrivkö till Fortnox vid varje ändring i Leara | Leara (produktdata, varianter, grupper) |
| Priser och prislistor | Leara → Fortnox, läses tillbaka | Ändring i Leara eller prisfilsimport, via skrivkön | Leara |
| Kunder | Båda håll | Realtid från Fortnox, kontroll var 5:e min, full import var 24:e h; skrivning när kund skapas i Leara | Leara (kundregister); Fortnox äger kundreskontra och spärrstatus |
| Kundordrar | Båda håll | Realtid från Fortnox, avstämning var 15:e min; skrivning när order skapas eller ändras i Leara, inklusive ordrar från e-handel | Leara samlar ordrar från alla kanaler; ordern skapas i Fortnox |
| Fakturor | Båda håll | Order → faktura → bokföring skapas från Leara; realtid och avstämning tillbaka | Fortnox (fakturanummer, bokföring, betalstatus) |
| Inbetalningar | Leara → Fortnox | När en betalning registreras i Leara | Fortnox |
| Offerter | Båda håll | Skrivning när offert skapas i Leara; inläsning vid manuell synk | Leara |
| Lagersaldon per lagerställe | Fortnox → Leara | Efter varje lagerpåverkande händelse, svep var 60:e s, fullt svep varje natt kl 03 | Fortnox, när Fortnox lagermodul används |
| Lagerställen | Fortnox → Leara | Läses och cachas i 10 min | Fortnox |
| Inleveranser | Leara → Fortnox | Skapas, uppdateras och klarmarkeras från Leara | Leara |
| Inköpsordrar | Båda håll | Skrivning från Leara; avstämning var 30:e min (Fortnox saknar händelser för inköpsordrar) | Leara |
| Lagerflyttar | Leara → Fortnox | När en flytt mellan lagerställen genomförs i Leara | Leara |
| Inventering och lagerjusteringar | Leara → Fortnox | Vid avslutad inventering eller manuell justering; bokas först i Fortnox, sedan i Leara | Leara (räkning), Fortnox (bokat saldo) |
| Leverantörer | Båda håll | Läses sidvis; skapas från Leara | Fortnox |
| Leveranssätt | Fortnox → Leara | Läses och cachas | Fortnox |

<Note>
  Använder kunden inte Fortnox lagermodul kan Leara i stället vara ägare av lagersaldot. Valet görs per konto.
</Note>

## Realtidssynk och avstämning

Fortnox-data når Leara i tre lager, så att ett tappat meddelande aldrig blir en bestående skillnad.

<Steps>
  <Step title="Realtid">
    Leara håller en WebSocket-anslutning mot Fortnox händelsetjänst (`wss://ws.fortnox.se/topics-v1`) och prenumererar på ämnena artiklar, ordrar, fakturor och kunder. En händelse innehåller bara vilket objekt som ändrats; Leara hämtar sedan objektet via REST-API:t och uppdaterar sin kopia. Position i strömmen sparas per konto och ämne, så att Leara vid återanslutning fortsätter där det slutade (Fortnox sparar händelser i cirka 14 dagar).
  </Step>

  <Step title="Avstämning var 15:e minut">
    Ett schemalagt jobb frågar Fortnox efter allt som ändrats sedan förra körningen, med 10 minuters överlapp, för ordrar, fakturor och artiklar. Allt som hittas hämtas och skrivs om. Det gör inget om ett objekt redan är aktuellt, eftersom uppdateringen är idempotent. Startpunkten sparas i databasen och överlever omstarter.
  </Step>

  <Step title="Fulla svep">
    Kunder läses in i sin helhet var 24:e timme, lagersaldon varje natt och inköpsordrar var 30:e minut. En fullständig synk av artiklar, ordrar, fakturor och offerter körs vid första anslutningen och kan startas manuellt.
  </Step>
</Steps>

Anslutningen övervakas. Bryts den återansluter Leara automatiskt med stigande väntetid (1 till 60 s) och registrerar om kontona med färska token. Raderade objekt fångas via realtidshändelsen eller när Leara får 404 vid hämtning, eftersom Fortnox ändringsfrågor inte listar raderade poster. När Leara själv skriver till Fortnox kommer ändringen tillbaka som en händelse. För artiklar känner Leara igen sina egna skrivningar och låter inte ett eko skriva över en nyare lokal ändring.

## Skrivvägen till Fortnox

Leara håller sig under Fortnox anropsgräns med marginal och gör om misslyckade skrivningar automatiskt. Fortnox tillåter 25 anrop per 5 sekunder per bolag; Learas gemensamma anropsbudget är 3,5 anrop per sekund med en topp på 8; flöden utanför budgeten begränsar sig själva och hanterar 429-svar.

* **Varaktig skrivkö för artiklar och priser.** Varje ändring läggs i en kö i databasen innan den skickas, så en omstart tappar ingenting. Flera ändringar på samma artikel slås ihop till en skrivning med artikelns aktuella värden. Ändringar från användare går före massimporter.
* **Budget delad i två.** Halva budgeten är reserverad för löpande synk och halva för tunga exporter (t.ex. inventering), så att en stor import inte stoppar vardagsflödet.
* **Omförsök.** Vid svar 429 (för många anrop) väntar Leara och försöker igen utan att det räknas som ett fel. Vid tillfälliga fel görs upp till 5 försök med växande väntetid (upp till 10 minuter). Därefter markeras posten som misslyckad och visas för användaren med möjlighet att försöka igen.
* **Idempotens.** Ordrar från e-handel bär en unik referens i Fortnox så att en order aldrig skapas två gånger vid omförsök. Lagerflyttar med okänt utfall stäms av i efterhand i stället för att skickas på nytt.
* **Spärr för osynkade artiklar.** En order, offert eller faktura kan inte skickas till Fortnox med en artikel som ännu inte finns där.
* **Synlig status.** Varje artikel visar om den är synkad, väntar eller misslyckades. Misslyckade ordrar och importrader visas med förklaring på svenska.

## Beroenden

Integrationen beror på Fortnox publika API:er, AWS och MongoDB Atlas. Inga andra tredjepartstjänster ligger i dataflödet mellan Leara och Fortnox.

| Beroende | Vad det används till |
| - | - |
| Fortnox OAuth (`apps.fortnox.se/oauth-v1`) | Anslutning och förnyelse av token |
| Fortnox REST API v3 (`api.fortnox.se/3`) | Artiklar, priser, prislistor, kunder, leverantörer, ordrar, offerter, fakturor, inbetalningar, leveranssätt |
| Fortnox Warehouse API (`api.fortnox.se/api/warehouse`) | Lagerställen, saldon, inleveranser, inköpsordrar, lagerflyttar, inventering |
| Fortnox WebSocket (`ws.fortnox.se/topics-v1`) | Realtidshändelser |
| AWS ECS, eu-north-1 | Kör Learas backend och tokentjänst |
| MongoDB Atlas | Learas datamodell, speglade Fortnox-objekt, skrivkö, synkstatus och token |

Backenden är skriven i Java 17 med Quarkus. Fortnox anropas med JDK:ns HTTP-klient utan Fortnox-SDK. Om Fortnox är otillgängligt fortsätter Leara att fungera; artikel- och prisändringar ligger kvar i kön, övriga skrivningar visas som misslyckade och kan göras om, och synken tar igen det som missats när Fortnox svarar igen.

## Säkerhet, multi-tenant och dataskydd

Varje kunds data och Fortnox-koppling hålls isolerad per Leara-konto, och all trafik går krypterad.

* **Isolering.** Alla speglade Fortnox-objekt lagras med kontots id som del av nyckeln, och token, synkstatus och köer hålls per konto.
* **En koppling per konto.** Token för ett Fortnox-bolag används bara för det kontots anrop. Realtidshändelser kommer märkta med Fortnox bolagsidentitet och sorteras till rätt konto.
* **Minsta behörighet.** Leara begär bara de scopes som integrationen använder (se ovan) och har ingen åtkomst till bokföringens verifikationer eller bolagsinställningar.
* **Transport.** Alla anrop till Fortnox går över HTTPS respektive WSS. Learas API nås via HTTPS på `api.leara.se`.
* **Datalokalisering.** Backend och tokentjänst körs i AWS eu-north-1 (Stockholm).
* **Personuppgifter.** Integrationen hanterar kunders namn, adresser, e-post och telefonnummer från kundkort, ordrar och fakturor. Uppgifterna används bara för att hålla Leara och Fortnox synkade.
* **Återkallelse.** Kunden kan när som helst återkalla Learas åtkomst i Fortnox.

## Drift och övervakning

Synkens hälsa följs per kundkonto i Learas interna adminverktyg, och missade ändringar tas igen automatiskt av avstämningen.

* En hälsokontroll rapporterar om realtidsanslutningen är uppe, när avstämningen senast lyckades och om anropsbudgeten tagit slut.
* Adminverktyget visar tokenstatus per konto (giltig, går snart ut, inaktuell, misslyckas, saknas) och skapar notiser var 15:e minut för konton som behöver åtgärdas.
* Loggar samlas i AWS CloudWatch.

## Kända begränsningar

De flesta begränsningarna kommer från Fortnox egna API:er. Här är de och hur Leara hanterar dem.

* Ändringar i Fortnox syns normalt inom sekunder, men i värsta fall efter upp till cirka 15 minuter, när avstämningen tar igen en missad händelse.
* Stora massimporter tar tid eftersom Fortnox begränsar antalet anrop. En artikel med flera prislistor kostar ett anrop per prislista.
* Refresh-token går ut efter 45 dagar utan användning, och kunden kan återkalla den i Fortnox. Då måste kopplingen göras om av en administratör.
* Ett Fortnox-bolag kan bara vara kopplat till en Leara-miljö åt gången, eftersom Fortnox roterar token vid varje förnyelse. En testmiljö behöver ett eget Fortnox-bolag, till exempel ett testbolag.
* Fortnox saknar realtidshändelser för offerter och inköpsordrar. Inköpsordrar stäms av var 30:e minut och offerter läses vid manuell synk.
* Fortnox saldo-API saknar lagerplatsnivå (hyllplats). Leara visar saldo per lagerställe från Fortnox.
* Fortnox ändringsfrågor listar inte raderade poster. Raderingar fångas via realtidshändelser.

För installationssteg, se [Anslut ditt affärssystem](/sv/integrations/erp).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.