We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
v1 · för utvecklare
Kossorna CDB-API
Ger journalsystem tillgång till nötkreatursdata ur Jordbruksverkets centrala nötkreatursregister (CDB), samt hanterar de medlemmar och samtycken som ger rätten till datan.
Den här sidan förklarar upplägget. Det fullständiga kontraktet - fältnamn, scheman, exakta felkuvert - står i OpenAPI-specen. Vid konflikt är det den filen som gäller.
Kom igång
Stegen låser upp varandra i tur och ordning: utan BusinessUuid går inga medlemsanrop, och utan signerat samtycke svarar djurdatan 401 hur giltig din nyckel än är.
-
1
Teckna avtal med Jordbruksverket
Ett avtal om utlämnande av CDB-uppgifter mellan er och Jordbruksverket är en förutsättning för att få en API-nyckel. Utan det avtalet får uppgifterna inte lämnas ut till er, och ingen nyckel skapas. Avtalets bilaga 1 avgör dessutom VILKA uppgifter ni får se - den styr vilka fält som kommer med i svaren.
-
2
Få en API-nyckel av kossorna
När avtalet är på plats skapar kossorna nyckeln. Den hör till journalsystemet, inte till en enskild verksamhet. Du får den tillsammans med basadressen.
-
3
Onboarda verksamheten
Svaret innehåller ett BusinessUuid. Spara det - det ska med som header i allt som rör medlemmar och djur. Anropet är en upsert, så det går att köra om.
POST /api/v1/businesses
-
4
Lägg till djurhållaren
Har du redan ett signerat samtycke skickar du med det i anropet. Har du inte det skickar kossorna automatiskt ett SMS till djurhållaren med en signeringslänk. Anropet är idempotent på personnummer - kör det så ofta du vill.
POST /api/v1/members
-
5
Vänta in samtycket
Innan djurhållaren signerat svarar djurdatan 401 med code no_consent. Det är det enda 401 som löser sig av sig självt. Kossorna pingar din webhook när något ändrats.
GET /api/v1/members/{ssn}/find_by_ssn
-
6
Hämta anläggningarna
Lätt anrop som ger djurhållarens anläggningar med antal levande djur på varje. Använd det för att bygga en lista att välja i.
GET /api/v1/cdb/{ssn}/ppns_for_ssn
-
7
Hämta djuren
Samma svar som steg 5 fast med hela djurlistan per anläggning. Tyngre - hämta det när användaren valt anläggning, inte i förväg.
GET /api/v1/cdb/{ssn}/ppns_with_animals_for_ssn
-
8
Håll dig uppdaterad
Webhookpingen betyder 'hämta om den här medlemmen', inte vad som hänt. Vad som faktiskt förändrats i beståndet läser du ur registerhållnings-endpointsen. Om datan alls är färsk svarar last_file_date på - ett tomt svar från ett dygn då leveransen uteblev är inte samma sak som att inget hänt.
GET /api/v1/cdb/{ssn}/dead · GET /api/v1/cdb/{ssn}/departed · GET /api/v1/last_file_date
Så hänger det ihop
Fem nivåer. Varje anrop du gör pekar ut var i kedjan du befinner dig.
- kund - journalsystemet självt, det som håller API-nyckeln
- verksamhet (business) - veterinärpraktiken eller motsvarande, identifieras av BusinessUuid
- medlem - djurhållaren, identifieras av person- eller organisationsnummer
- anläggning - platsen där djuren står
- djur - det enskilda nötkreaturet
Ord som betyder samma sak, eller nästan
- aktuellt_ppn
- Anläggningen djuret står på just nu.
- anläggning
- Anläggning, produktionsplats och PPN är tre ord för samma sak. Fältet heter `plants` i API:t och `produktionsplats` hos Jordbruksverket.
- djurets uuid
- Djurets stabila nyckel. Den ändras aldrig under djurets liv, till skillnad från öronnumret. Lagra uuid, inte öronnumret - se fallgroparna.
- öronnummer / identitet
- Djurets läsbara namn, t.ex. SE-900001-0222-2. Kan bytas under djurets liv; ett djur kan ha haft flera.
Autentisering
Token identifierar kunden, BusinessUuid verksamheten, och samtycket avgör vilken medlems data som får läsas. Alla tre måste stämma - en giltig token räcker inte.
- Authorization
- Kundens API-nyckel. `Bearer <nyckel>` eller bara `<nyckel>` - ledande `Bearer ` strippas serversidan. Identifierar journalsystemet. Nyckeln förutsätter ett avtal om utlämnande av CDB-uppgifter med Jordbruksverket.
- BusinessUuid
- Vilken verksamhet anropet gäller. Värdet är det uuid som POST /businesses gav. Krävs av alla /members- och /cdb-anrop utöver Authorization. Läses skiftlägesokänsligt.
Bara den här menyn och /healthz. Allt annat svarar 401.
Samtycke
Ingen djurdata utan att djurhållaren gett verksamheten samtycke. Saknas det svarar /cdb 401 med code `no_consent`.
BankID-signering (bankid_order_ref)
FrejaID-signering (freja_sign_ref)
Uppladdat signerat dokument (fil)
- Om inget samtycke skickas med
- Skapas en medlem utan något av dem skickar kossorna automatiskt ett SMS till medlemmen med en signeringslänk. Går numret inte att skicka till svarar POST /members 422 - men medlemmen är ändå skapad.
- Vem som får signera
- Är medlemmen ett företag får vem som helst signera. Är medlemmen privatperson eller enskild firma måste signaturen bära medlemmens eget personnummer - annars sparas ingenting.
- Innan det finns
- `no_consent` kan lösa sig av sig självt när medlemmen signerar. Det är det enda 401 som är värt att fråga igen på senare.
Var datan kommer ifrån
- Ursprung
- All djur- och anläggningsdata kommer från Jordbruksverkets centrala nötkreatursregister. Kossorna beställer, hämtar och tolkar filerna; API:t serverar resultatet.
- Takt
- Beställning skickas kvällen före, filerna hämtas och tolkas morgonen därpå. Datan är alltså upp till ett dygn gammal. Det finns inget sätt att trigga en hämtning via API:t.
- Två leveranser per dygn
- Varje dygn beställs två filuppsättningar. INKREMENTELL bär förändringarna för de djurhållare som redan fått en fullständig leverans; TOTAL bär hela beståndet för dem som aldrig fått en, plus de äldsta i rotationen. Uppsättningarna överlappar inte.
- Hur färsk är datan?
- GET /api/v1/last_file_date svarar när filerna senast blev korrekt inlästa. Datumet är BESTÄLLNINGENS dag, alltså vilket läge datan speglar - inte när vi hämtade den. Svaret delas upp i `total` och `incremental` därför att de två leveranserna täcker olika djurhållare: den inkrementella går till i stort sett alla varje dygn, den totala till en roterande delmängd. Ett anrop per dygn räcker gott.
- Första leveransen
- En nytillagd medlems djurdata finns inte förrän hennes första fullständiga leverans har hämtats - normalt morgonen efter att samtycket kom in. Tomma listor dessförinnan betyder 'ännu inte levererat', inte 'inga djur'.
- Eftersläpning
- Jordbruksverket kan leverera en händelse långt i efterhand. Därför räknar /dead och /departed sina fönster på när VI bokförde händelsen, inte på när den inträffade.
Endpoints
Listan genereras ur applikationens router, så den kan inte bli inaktuell. Kolumnen Kräver visar vilka headers anropet behöver.
Menyn
| GET |
/api/v1 Den här menyn. Öppen, kräver ingen token. |
öppen |
Verksamheter
| GET |
/api/v1/businesses Verksamheterna som den här kundens token är kopplad till. |
Authorization |
| POST |
/api/v1/businesses Onboarda en verksamhet och få tillbaka BusinessUuid, som alla medlems- och djuranrop kräver. Upsert på verksamhetens orgnr per kund: 201 nytt, 200 ny koppling, 409 om kopplingen redan finns. |
Authorization |
| GET |
/api/v1/businesses/:id En verksamhet. :id är BusinessUuid, inte ett löpnummer. |
Authorization |
| PATCH |
/api/v1/businesses/:id Uppdatera verksamhetens uppgifter. |
Authorization |
| PUT |
/api/v1/businesses/:id Samma som PATCH. |
Authorization |
Medlemmar och samtycken
| GET |
/api/v1/members Verksamhetens medlemmar. |
Authorization BusinessUuid |
| POST |
/api/v1/members Lägg till en djurhållare under verksamheten. Idempotent på personnummer inom verksamheten: finns medlemmen uppdateras den (200) i stället för att dubbletteras (201). Samtycke kan följa med i anropet (bankid_order_ref, freja_sign_ref eller uppladdad fil); utan det skickas ett SMS till medlemmen med en signeringslänk. |
Authorization BusinessUuid |
| DELETE |
/api/v1/members/:id Genererad av `resources` men MemberController saknar delete/2. Anropet ger 500. Ett samtycke återkallas, medlemmen raderas inte. Ej implementerad - anropet svarar 500. Använd anropet som beskrivs ovan i stället. |
Authorization BusinessUuid |
| GET |
/api/v1/members/:id En medlem, med samtyckesstatus och SMS-status. |
Authorization BusinessUuid |
| PATCH |
/api/v1/members/:id Genererad av `resources` men MemberController saknar update/2. Anropet ger 500. POST /api/v1/members uppdaterar i stället, på personnummer. Ej implementerad - anropet svarar 500. Använd anropet som beskrivs ovan i stället. |
Authorization BusinessUuid |
| PUT |
/api/v1/members/:id Samma som PATCH - saknar update/2, ger 500. Ej implementerad - anropet svarar 500. Använd anropet som beskrivs ovan i stället. |
Authorization BusinessUuid |
| GET |
/api/v1/members/:id/edit Genererad av `resources` men MemberController saknar edit/2. Anropet ger 500. Ej implementerad - anropet svarar 500. Använd anropet som beskrivs ovan i stället. |
Authorization BusinessUuid |
| GET |
/api/v1/members/:ssn/find_by_ssn Slå upp en medlem på personnummer i stället för id. Det här är vägen ett journalsystem normalt går - det har personnumret, inte vårt id. |
Authorization BusinessUuid |
| GET |
/api/v1/members/new Genererad av `resources` men MemberController saknar new/2. Anropet ger 500. Använd POST /api/v1/members. Ej implementerad - anropet svarar 500. Använd anropet som beskrivs ovan i stället. |
Authorization BusinessUuid |
Anläggningar
| GET |
/api/v1/plants Alla anläggningar kunden har rätt att se. |
Authorization |
| GET |
/api/v1/plants/:id En anläggning. |
Authorization |
| GET |
/api/v1/plants/:ssn/by_ssn Anläggningar för en djurhållare, uppslaget på hennes person- eller organisationsnummer. |
Authorization |
Djurdata
| GET |
/api/v1/cdb/:ssn/animals/:id Ett enskilt djur. :id är djurets uuid - se fallgropen om uuid mot öronnummer. |
Authorization BusinessUuid |
| GET |
/api/v1/cdb/:ssn/dead Djur som dött, räknat på när VI bokförde dödsfallet - inte på dödsdatumet. from_days/to_days räknas bakåt från idag, inklusiva i båda ändar, max 365 dagar bakåt. |
Authorization BusinessUuid |
| GET |
/api/v1/cdb/:ssn/departed Djur som lämnat anläggningen, med samma fönsterlogik som dead. |
Authorization BusinessUuid |
| GET |
/api/v1/cdb/:ssn/ppns_for_ssn Medlemmens anläggningar med antal levande djur på var och en. Lätt anrop - använd det för att bygga en lista att välja i, innan du hämtar djuren. |
Authorization BusinessUuid |
| GET |
/api/v1/cdb/:ssn/ppns_with_animals_for_ssn Samma som ppns_for_ssn, fast med hela djurlistan per anläggning. Tyngre anrop. |
Authorization BusinessUuid |
Drift och datafärskhet
| GET |
/api/v1/last_file_date När Jordbruksverkets filer senast blev korrekt inlästa - hämtade OCH tolkade. Redovisas per beställningstyp, eftersom TOTAL och INKREMENTELL täcker olika djurhållare. Använd den för att skilja 'inget har hänt' från 'dagens leverans uteblev'. Kräver bara Authorization; svaret är detsamma för alla verksamheter. |
Authorization |
När något går fel
Gren på `code` i kroppen, inte på statuskoden. 401 är överlagrat och betyder både 'ogiltig token' och 'du saknar rätt till den här medlemmen'.
| invalid_token | Authorization saknas eller matchar ingen kund. |
| missing_business | BusinessUuid saknas eller är okänd. |
| no_customer_business_link | Din kund är inte kopplad till den verksamheten. |
| no_business_member_link | Verksamheten har ingen koppling till den medlemmen. |
| no_consent | Kopplingen finns men medlemmen har inget aktivt samtycke. |
| invalid_ssn | Personnumret går inte att tolka. |
| member_not_found | Ingen medlem med det numret. |
| animal_not_found | Inget djur med det uuid:t hos den medlemmen. |
4xx ska inte retryas - samma anrop ger samma svar. 5xx och nätverkstimeout är den enda rimligt retrybara klassen. Ingen Retry-After skickas.
Manuella fel ger {"error": "..."}, valideringsfel ger {"errors": {"fält": [...]}}, oväntade fel ger {"errors": {"detail": "..."}}. `code` finns på auth-felen och på /cdb, men inte överallt. Läs OpenAPI-filen innan du parsar felkroppar hårt.
Webhook
Kossorna pingar kundens konfigurerade webhook_url när en medlems uppgifter kan ha ändrats. Pingen är en GET utan body - den säger 'hämta om den här medlemmen', inte vad som hänt.
GET {webhook_url}/{VERKSAMHETENS_SSN}/business/{MEDLEMMENS_SSN}/member
- Triggas av
- medlem skapad eller uppdaterad · samtycke godkänt eller signerat · SMS-status från operatören
- Omförsök
- Första anropet plus tre omförsök: efter 10 minuter, 2 timmar och 24 timmar.
- Ingen signatur
- Det finns ingen HMAC eller signatur idag. Behandla pingen som ett tips om att hämta, aldrig som en betrodd uppgift i sig.
Fallgropar
Det här är sådant som inte syns i en fältlista, men som kostar tid att upptäcka själv.
Lagra djurets uuid, inte öronnumret
Öronnumret kan bytas när djuret märks om. Ett system som nycklar på SE-900001-0222-2 tappar djuret vid ommärkning. uuid ändras aldrig.
En anläggning har två adresser - blanda inte ihop dem
`adress`/`postnummer`/`postadress` är platsens egen. `djurhallare_*` är djurhållarens. En djurhållare med flera anläggningar har EN djurhållaradress men olika platsadresser - visar du djurhållarens blir hennes anläggningar omöjliga att skilja åt i listan.
Person- och organisationsnummer förekommer i både tio- och tolvsiffrig form
Samma djurhållare kan ligga lagrad i båda formaten. Jämför inte råa strängar rakt av; matcha på de sista tio siffrorna när du parar ihop en djurhållare mellan system.
En tom djurlista betyder inte alltid noll djur
Innan medlemmens första fullständiga leverans hämtats finns ingen data att visa. Skilj 'ännu inte levererat' från 'inga djur' innan du meddelar en användare något. `member.sjv_status` säger om orsaken är känd: `orgnr_invalid` betyder att numret måste rättas hos dig, `orgnr_not_found` att SJV inte känner igen det ännu - det senare får aldrig visas som ett skrivfel, det är också svaret för skyddad identitet. GET /api/v1/last_file_date säger om leveransen över huvud taget kommit in idag.
Döda och avförda djur ligger kvar en tid
Ett djur som dött eller lämnat anläggningen ligger kvar i svaret i 40 dagar, så att anslutande system hinner se ATT det hände i stället för att djuret tyst försvinner mellan två anrop.
avliden_datum kan ligga långt bak i tiden på ett djur du får idag
Fönstren i /dead och /departed räknas på när vi bokförde händelsen. Räknar du själv på avliden_datum missar du djur som levererades sent.
Vilka fält du får se styrs av kundavtalet
Anläggningsfält är flaggstyrda per kund och speglar bilaga 1 i kundens SJV-avtal. Ett fält som saknas i ditt svar behöver inte saknas i datan - din kund kan sakna rätten.
POST /members är en upsert, inte ett create
Samma personnummer två gånger ger en uppdatering (200), inte en dubblett (201). Kör den så ofta du vill.
Basadress i produktion: https://kossorna.se · lokal utveckling: http://localhost:4000
Allt på den här sidan finns också som JSON på /api/v1, för den som hellre läser maskinellt.