API

Dette kapitel er henvendt til udviklere, og kræver erfaring med programmering.

En rækkke funktioner i ForeningLet er udstillet via et API. Dermed er det muligt at lave integrationer op mod ForeningLet fra andre systemer. I det følgende benævnes »andre systemer« som klient.

Læs indholdet af dette kapitel omhyggeligt, herunder begrænsninger og ansvar som beskrevet nedenfor, for at forstå generelle restriktioner.

Hvis ForeningLet vurderer, at I har eller har forsøgt at overtræde betingelserne for brugen af API’et, kan jeres adgang til at bruge API’et blive midlertidigt eller permanent tilbagekaldt.

Sikkerhed og autentifikation

For at få lov til at tilgå API’et, skal klienten som ønsker at integrere med ForeningLet.dk først logge på med et brugernavn og kodeord. Dette foregår via HTTP Basic Authentication, som eksempelvis kan gøres på følgende måde via kommandolinjeværktøjet curl:

curl https://foreninglet.dk/api/members?version=1 -u {brugernavn}:{kodeord}

Parameteren version er obligatorisk og fortæller hvilken version af API’et, som tilgås. Nuværende version er 1.

Parameteren efter -u er brugernavn og kodeord adskilt af et kolon. Brugernavn og kodeord kan ses af foreningens administrator under »Opsætning -> Integrationer -> API«.

Kodeordet (i kombination med brugernavnet) giver adgang til foreningens data, hvorfor kodeordet ikke må udleveres til tredjepart, da tredjepart dermed kan få adgang til foreningens data.

URL’en https://foreninglet.dk/api/members?version=1 kan også indtastes i en browser, hvorefter browseren vil spørge om brugernavn og kodeord. På denne vis er det let at afprøve API’et.

Versionering

Klienten skal altid angive URL-parameteren version på en forespørgsel. Dermed sikres det, at API’et løbende kan udvides uden at bryde kontrakten med eksisterende klienter, idet en given version aldrig vil ændre på navngivning eller fjerne et felt i de svar som API’et returnerer til klienten.

Når der sker ændringer til API’et, så sker det altid via tilføjelsen af en ny version af API’et, således at klienter, som ikke kører på nyeste version, stadig virker.

Hvis en ældre version af API’et udgår, så bliver de foreninger som anvender den pågældende version af API’et notificeret i rimelig tid. Typisk vil en forening blot kunne ændre til nyeste version af API’et uden at skulle foretage kodeændringer i klienten.

Begrænsninger i antal forespørgler

Der er en øvre grænse for hvor mange forespørgsler man må lave mod API’et per time. Grænsen er 10.000 forespørgsler i timen.

Hvis grænsen overskrides vil API’et svare tilbage med en HTTP 429 (»Too Many Requests«).

Protokol og dataformat

Der integreres via https-protokollen. API’et bygger på principperne bag REST.

Dataformatet er json. Hvis en klient ønsker et svar i et andet format, fx html (eller xml), så tilføj følgende URL-parametre til forespørgslen: /format/html. Eksempelvis: https://foreninglet.dk/api/members/format/html?version=1

Når der oprettes nye data (POST) eller opdateres eksisterende data (PUT), så skal »Content-Type: application/json« altid angives i forespørgslen.

Tidsstempler følger ISO8601 og vil altid blive vist i UTC.

Når der oprettes eller opdateres en ressource, vil svaret være at finde i body formateret som json, og der sættes en Location http header som peger på ressourcen.

Begrænsninger og ansvar

Jeres abonnement på ForeningLet gør det muligt at få adgang til at bruge et API, herunder at udvikle og implementerer integrationer til brug i forbindelse med jeres abonnement på ForeningLet til jeres interne forretningsformål.

For at kunne bruge og få adgang til API’et, skal I bruge API brugernavn og kodeord, som er tilgængeligt hvis man er oprettet i ForeningLet. I må ikke dele dette brugernavn og kodeord, og skal opbevare det sikkert. Brugernavn og kodeord er den eneste måde, hvorigennem I har tilladelse til at få adgang til API’et.

I må ikke bruge API’et til at kopiere produkter eller tjenester, der tilbydes af ForeningLet, herunder funktioner eller klienter på platforme (såsom iOS eller Android) hvor ForeningLet tilbyder sin egen klient eller funktion.

I må ikke videresælge API’et. I må ikke bruge API’et på nogen måde, der potentielt kunne underminere sikkerheden i API’et.

I er eneansvarlige (og ForeningLet har intet ansvar af nogen art), for indholdet, udvikling, drift eller vedligeholdelse af de integrationer I måtte udvikle via API’et. I er eneansvarlige for at jeres integrationer ikke overtræder eller krænker immaterielle rettigheder tilhørende en tredjepart.

I skal respektere og overholde de tekniske og politiske implementerede begrænsninger af API’et.

I det omfang I udvikler eller implementere integrationer, der sender eller på anden måde bruger API’et til at overføre jeres data uden for ForeningLet, vil I være ansvarlige for eventuelle nødvendige anmeldelser til eller samtykker fra slutbrugere om at deres data vil blive sendt uden for ForeningLet. ForeningLet er ikke ansvarlig for sikkerhed eller integritet i jeres data, i det omfang de sendes uden for ForeningLet via jeres integrationer til API’et.

API-kald

Her følger en liste over de forskellige kald til API’et.

Medlemsliste

GET /api/members

Følgende kald returnerer alle indmeldte medlemmer

curl https://foreninglet.dk/api/members?version=1 -u {brugernavn}:{kodeord}

Hvis man vil have en liste over alle de udmeldte medlemmer, så gøres det på følgende vis:

curl https://foreninglet.dk/api/members/status/resigned?version=1 -u {brugernavn}:{kodeord}

Opret medlem

POST /api/member

Når man opretter et medlem, så skal man som minimum angive et fornavn.

Eksempel på forespørgsel

{
  "member": {
    "first_name": "Hans Christian",
    "last_name": "Andersen"
  }
}

Via curl

curl https://foreninglet.dk/api/member?version=1 \
  -d '{"member": {"first_name": "Hans Christian","last_name": "Andersen"}}' \
  -H "Content-Type: application/json" -u {brugernavn}:{kodeord} -X POST

Eksempel på svar

Status: 201 Created
Location: https://foreninglet.dk/api/member/id/1?version=1
{
  "member": {
    "id": 12345,
    "display_id": 1,
    "first_name": "Hans Christian",
    "last_name": "Andersen",
    "address": "",
    "address2": "",
    "zip": "",
    "city": "",
    "email": "",
    "email2": "",
    "email3": "",
    "phone": "",
    "mobile": "",
    "gender": 2,
    "birthday": "0000-00-00",
    "country_code": "",
    "ean_number": "",
    "enrollment": "0000-00-00",
    "resignation_date": "0000-00-00",
    "password": "63C4B",
    "field1": "",
    "field2": "",
    "field3": "",
    "field4": "",
    "field5": "",
    "field6": "",
    "field7": "",
    "field8": "",
    "field9": "",
    "field10": "",
    "created": "2015-06-18T12:02:14+0000",
    "updated": "2015-06-18T12:02:14+0000",
    "activity_ids": [],
    "cpr_number": "",
    "reg_number": "",
    "account_number": ""
  }
}

Systemet tildeler automatisk et kodeord til nye medlemmer. Man kan også angive et kodeord i oprettelsen.

Parametre

Når der oprettes et nyt medlem via POST, så kan der angives følgende parametre, hvoraf first_name er den eneste obligatoriske parameter.

Navn

Beskrivelse

display_id

Medlemsnr.

first_name

Fornavn

last_name

Efternavn

address

Adresse

address2

Adresse 2

zip

Postnr.

city

By

email

Email-adresse

email2

Email-adresse 2

email3

Email-adresse 3

phone

Telefonnr.

mobile

Mobilnr.

gender

Køn (0=kvinde, 1=mand, 2=intet køn angivet)

birthday

Fødselsdato

country_code

Landekode (ISO 3166 - 2 bogstaver)

ean_number

EAN-nr.

enrollment

Indmeldelsedato

resignation_date

Udmeldelsesdato

password

Kodeord

field1

Felt 1

field2

Felt 2

field3

Felt 3

field4

Felt 4

field5

Felt 5

field6

Felt 6

field7

Felt 7

field8

Felt 8

field9

Felt 9

field10

Felt 10

activity_ids

ID’er på aktiviteter

cpr_number

CPR-nummer

reg_number

Registreringsnummer i bank

account_number

Kontonummer i bank

Hvis der ikke angives et medlemsnr., så tildeler systemet selv det næste ledige medlemsnr.

Bemærk

Betalingsservice: Hvis felterne CPR-nummer, registreringsnummer og kontonummer er udfyldt, og foreningen er sat op til at bruge Betalingsservice, så vil systemet automatisk forsøge at oprette en PBS-aftale på det nye medlem.

Opdater medlem

PUT /api/member/id/{id}

Når man opdaterer et medlem, skal man angive ID på det medlem som ønskes opdateret. ID’er på medlemmerne returneres fra API-kaldet til medlemslisten. Bemærk at felterne id og display_id er 2 forskellige felter, hvor id er det interne databasenr. som er unikt for medlemmet og bruges i API-kald, mens display_id svarer til medlemmets medlemsnr.

Man kan nøjes med at angive de felter man gerne vil ændre.

Vær opmærksom på at hvis man angiver en tom liste af aktivitets-ID’er, så bliver medlemmets eventuelle tildelte aktiviteter slettet. Hvis man ikke ønsker at påvirke medlemmets aktiviteter, så skal man helt undlade at anvende feltet activity_ids. I det øjeblik feltet activity_ids er sat, så vil medlemmet få opdateret sin samlede liste af aktiviteter til præcis kun at indeholde de angivne aktiviteter. Således kan aktiviteter også blive slettet.

Eksempel på forespørgsel

{
  "member": {
    "first_name": "Hans Christian",
    "last_name": "Jensen"
  }
}

Via curl

curl https://foreninglet.dk/api/member/id/{id}?version=1 \
  -d '{"member": {"first_name": "Hans Christian","last_name": "Jensen"}}' \
  -H "Content-Type: application/json" -u {brugernavn}:{kodeord} -X PUT

Eksempel på svar

Status: 200 OK
{
  "member": {
    "id": 12345,
    "display_id": 122153,
    "first_name": "Hans Christian",
    "last_name": "Jensen",
    "address": "",
    "address2": "",
    "zip": "",
    "city": "",
    "email": "",
    "email2": "",
    "email3": "",
    "phone": "",
    "mobile": "",
    "gender": 2,
    "birthday": "0000-00-00",
    "country_code": "",
    "ean_number": "",
    "enrollment": "0000-00-00",
    "resignation_date": "0000-00-00",
    "password": "63C4B",
    "field1": "",
    "field2": "",
    "field3": "",
    "field4": "",
    "field5": "",
    "field6": "",
    "field7": "",
    "field8": "",
    "field9": "",
    "field10": "",
    "created": "2015-06-18T12:02:14+0000",
    "updated": "2015-06-18T12:02:16+0000",
    "activity_ids": []
  }
}

Login

POST /api/memberlogin

Når man vil lave et login på et medlem, så skal man angive et brugernavn og et kodeord. Brugernavnet kan være medlemsnr. eller email-adresse, og dette styres i parameteren field, der som udgangspunkt er sat til email. Den anden mulighed er member_number.

Hvis medlemmet har status som indmeldt og brugernavn og kodeord matcher, så returneres medlemmets detaljer.

Følgende er et eksempel på login med email-adresse test@foreninglet.dk samt kodeord 12345.

Eksempel på forespørgsel

{
  "credentials": {
    "username": "test@foreninglet.dk",
    "password": "12345",
    "field": "email"
  }
}

Via curl

curl https://foreninglet.dk/api/memberlogin?version=1 \
  -d '{"credentials": {"username": "test@foreninglet.dk","password": "12345", "field": "email"}}' \
  -H "Content-Type: application/json" -u {brugernavn}:{kodeord} -X POST

Eksempel på svar

Status: 200 OK
{
    "id": 12345,
    "display_id": 1000,
    "first_name": "Hans",
    "last_name": "Jensen",
    "address": "",
    "address2": "",
    "zip": "",
    "city": "",
    "email": "test@foreninglet.dk",
    "password": "12345"
}

Ovenstående svar et blot et udsnit af de medlemsinformationer som returneres, hvis medlemmet findes. Fx returneres også en liste med de aktiviteter som medlemmet er tilknyttet. Det er et felt ved navn activity_ids, som indeholder en liste med ID’er på aktiviteter.

Hvis medlemmet ikke findes, så returneres følgende svar:

Status: 404 OK
{
        "error":"Member with username [test@foreninglet.dk] could not be found"
}

Aktivitetsliste

GET /api/activities

Følgende kald returnerer alle aktiviteter

curl https://foreninglet.dk/api/activities?version=1 -u {brugernavn}:{kodeord}

Eksempel på svar

HTTP/1.1 200 OK
Content-Type: application/json

[
    {
        "ActivityId": "11111",
        "Categories": ["Ungdomsafdelingen"],
        "ExternalDescriptions": [],
        "Name": "Ungdomskontingent",
        "OnlineEnrollmentEnabled": "0"
    },
    {
        "ActivityId": "22222",
        "Categories": ["Seniorafdelingen"],
        "ExternalDescriptions": [],
        "Name": "Seniorkontingent",
        "OnlineEnrollmentEnabled": "0"
    },
    {
        "ActivityId": "33333",
        "ExternalDescriptions": [
            {
                "Headline": "Tilmelding \u00e5bner",
                "Text": "1. august 2018, kl. 12:00"
            },
            {
                "Headline": "Nye medlemmer",
                "Text": "Hvis du \u00f8nsker at blive medlem af foreningen, s\u00e5 klik p\u00e5 'Tilmeld'."
            }
        ],
        "Name": "Nye medlemmer",
        "OnlineEnrollmentEnabled": "1"
    }
]

Ovenstående eksempel viser et svar med 3 aktiviteter. Én af aktiviteterne er åben for online tilmelding, hvilket kan aflæses i feltet OnlineEnrollmentEnabled, hvor værdien er sat til 1. Hvis en aktivitet er åben for online tilmelding, så vil der også være udfyldt en række beskrivelser af aktiviteten i feltet ExternalDescriptions, som er en liste med overskrifter og tekster.

Hvis man vil bruge aktivitetslisten til at lave en tilmeldingsliste på sin egen hjemmeside, så kan man bruge ActivityID til at linke dybt fra denne liste på hjemmesiden og ind i medlemsportalen, hvilket fx kan gøres sådan her:

https://<forenings-ID>.foreninglet.dk/memberportal/teamenrollment/subscribe/<ActivityID>

Man skal naturligvis erstatte forenings-ID og ActivityID med de rigtige værdier. Foreningens ID kan ses inde i systemet, når man er logget ind ved at klikke på menupunktet »Medlemsportal«.

Opret opkrævning

POST /api/invoice

Når man opretter en opkrævning, så skal man som minimum angive medlems-ID (bemærk at medlems-ID ikke er det samme som medlemsnr.), en beskrivelse af hvad der opkræves samt beløbet der skal opkræves.

Eksempel på forespørgsel

{
  "invoice": {
    "member_id": 123456,
    "description": "Kontingent",
    "amount": 100.00
  }
}

Via curl

curl https://foreninglet.dk/api/invoice?version=1 \
  -d '{"invoice": {"member_id": 123456, "description": "Kontingent", "amount": 100.00}}' \
  -H "Content-Type: application/json" -u {brugernavn}:{kodeord} -X POST

Eksempel på svar

Status: 201 Created
Location: https://foreninglet.dk/api/invoice/id/1000?version=1
{
  "invoice": {
    "credit_card_payment_url": "https://foreninglet.dk/main/apiredirectcreditcardwindow/{x}"
    "id": 1000,
    "text": "Kontingent",
    "total_amount": 100
  }
}

Systemet tildeler automatisk et opkrævningsnr. til en ny opkrævning. Det findes i feltet »id«.

I svaret er der en vigtig parameter ved navn credit_card_payment_url, som indeholder en adresse. Hvis der redirectes til denne adresse, så åbnes et betalingsvindue, hvor medlemmet kan betale opkrævningen med kreditkort. Det er naturligvis en forudsætning, at dankortmodulet er tilkøbt og sat op.

Opret booking

POST /api/booking

Når man opretter en booking, så skal man som minimum angive ID på den ressourcekalender der bookes i og ID på den ressource som bookingen vedrører samt start- og slutdato, samt en tekst eller en række medlems-IDer.

Eksempel på forespørgsel

{
  "booking": {
    "resource_calendar": 5678,
    "resource_id": 1234,
    "text": "Generalforsamling",
    "start_date": "2017-11-14T18:00:00+0000",
    "end_date": "2017-11-14T20:00:00+0000",
    "create_timed_code": "true",
  }
}

Via curl

curl https://foreninglet.dk/api/booking?version=1 \
  -d '{"booking": {"resource_calendar_id": 5678, "resource_id": 1234, "text": "Generalforsamling", "start_date": "2017-11-14T18:00:00+0000", "end_date": "2017-11-14T20:00:00+0000", "create_timed_code": "true"}}' \
  -H "Content-Type: application/json" -u {brugernavn}:{kodeord} -X POST

Eksempel på svar

Status: 201 Created
Location: https://foreninglet.dk/api/booking/id/1000?version=1
{
  "booking": {
    "id": 1000,
    "resource_calendar_id": 5678,
    "resource_id": 1234,
    "text": "Generalforsamling",
    "start_date": "2017-11-14T18:00:00+0000",
    "end_date": "2017-11-14T20:00:00+0000",
    "created": "2017-11-07T16:42:14+0000",
    "timed_code": 476294
  }
}

Systemet tildeler automatisk et booking-ID til en ny booking. Det findes i feltet »id«.

Hvis man har sat »create_timed_code« til »true«, så vil systemet danne en 6-cifret kode som kan anvendes i dørsystemet (hvis dette er installeret), og give adgang til bookingen i det tidsrum der omkranser bookingens start- og sluttidspunkt.

Ressourcekalenderen definerer en række regler på de ressourcer som indgår i kalenderen. En ressourcekalender indeholder typisk én til flere ressourcer. Hvis det er booking af badmintonbaner, så vil en ressourcekalender typisk svare til en hal, og ressourcerne vil være de baner som er i hallen.

Slet booking

DELETE /api/booking/id/{id}

Når man sletter en booking, så skal man angive ID på den booking man ønsker at slette.

Via curl

curl https://foreninglet.dk/api/booking/id/1234?version=1 \
  -H "Content-Type: application/json" -u {brugernavn}:{kodeord} -X DELETE

Eksempel på svar

Status: 200 Deleted
{"id":"1234","message":"DELETED!"}

Systemet sletter også en eventuel kode til dørsystemet, hvis sådan en kode er tilknyttet bookingen.

Bookingliste

GET /api/bookings/start_date/{startdato}/end_date/{slutdato}

Når man henter en bookingliste, så skal man som minimum angive start- og slutdato, som vist ovenfor. Systemet returnerer alle bookinger som er indenfor det angivne datointerval.

Formatet på datoen skal være ÅÅÅÅ-MM-DD - fx 2018-12-24, hvilket er juleaften.

Via curl

curl https://foreninglet.dk/api/bookings/start_date/{startdato}/end_date/{slutdato}?version=1 -u {brugernavn}:{kodeord}

Eksempel på svar

HTTP/1.1 200 OK
Content-Type: application/json

[
    {
        "activity_id": "0",
        "created": "2017-02-27T10:56:12+0000",
        "created_by": "admin",
        "end_time": "2017-12-01T13:00:00+0000",
        "id": "99128",
        "resource_id": "1234",
        "start_time": "2017-12-01T11:00:00+0000",
        "text": "Booking af bane 1",
        "url": ""
    },
    {
        "activity_id": "0",
        "created": "2017-07-31T21:13:52+0000",
        "created_by": "admin",
        "end_time": "2017-12-01T19:00:00+0000",
        "id": "124282",
        "resource_id": "1235",
        "start_time": "2017-12-01T15:00:00+0000",
        "text": "Booking af bane 2",
        "url": ""
    }
]

Bemærk at start_time og end_time i ovenstående svar er angivet i UTC.

Info om ressourcekalender

GET /api/bookings/resourcecalendar/id/{id}

Når man henter info om en ressourcekalender, så skal man angive ID på ressourcekalenderen, som vist ovenfor. Foreningen kan oplyse ID’er på de ressourcekalendere som skal anvendes.

Systemet returnerer info om ressourcekalenderen, og denne info kan bruges - i kombination med allerede bookede tider -, hvis man ønsker at præsentere ledige tider i et eksternt system. Det kunne fx være et system som Wannasport (https://www.wannasport.dk), som integrerer med ForeningLet.

Via curl

curl https://foreninglet.dk/api/resourcecalendar/id/{id}?version=1 -u {brugernavn}:{kodeord}

Eksempel på svar

HTTP/1.1 200 OK
Content-Type: application/json

{
    "close_days": [
        "24-12-2020: Lukket pga. juleaften",
        "31-12-2020: Lukket pga. nyt\u00e5rsaften"
    ],
    "description": "",
    "duration": "60",
    "end_date": "2020-12-31",
    "end_time": "23:00",
    "id": "1111",
    "name": "Baneoversigt",
    "rule_max_days_ahead": "14",
    "selected_days": "1,2,3,4,5,6,7",
    "start_date": "2020-01-01",
    "start_time": "07:00”
}

Et par bemærkninger til svaret:

start_date

Dato for hvornår kalenderen åbner.

start_time

Tidspunkt på dagen (dansk tid) hvor kalenderen åbner - altså hvornår der kan foretages bookinger fra.

end_date

Dato for hvornår kalenderen lukker.

end_time

Tidspunkt på dagen (dansk tid) hvor kalenderen lukker - altså hvornår der kan foretages bookinger til.

selected_days

1 er lig med mandag, 2 er lig med tirsdag, osv. Denne kalender er altså åben for bookinger hele ugen. Og den er åben fra kl. 7.00-23.00.

rule_max_days_ahead

Det er en regel som siger, at der maksimum kan bookes 14 dage frem. Denne regel bliver ikke håndhævet via kald fra API’et, men kun når medlemmer booker direkte via foreningens medlemsportal.

close_days

Liste over datoer hvor kalenderen slet ikke kan bookes. Altså “lukkedage”.

Ovenstående information - i kombination med de bookinger som er lagt ind, og som kan hentes via andet API-kald (Bookingliste) - er tilstrækkeligt til at man kan præsentere ledige tider i et eksternt system.