WebAPI - Documentatie powered by Swagger

Introductie

De WebAPI van SDB Groep stelt je in staat om geautomatiseerd gegevens op te halen, te bewerken en aan te maken in onze applicaties.

Deze is gebouwd volgens het REST principe, dit zorgt er voor dat het schrijven van je applicatie makkelijker is omdat o.a. de URL's voorspelbaar zijn en er met objecten (resources) gewerkt wordt. Verder kunnen we met deze REST API de meeste clients ondersteunen omdat het gebaseerd is op het HTTP protocol.

Elk object is benaderbaar via een URL, hieronder per omgeving de base URL waarop de API bereikbaar is:

Test

https://api.test-sdbstart.nl/api/

Acceptatie

https://api-acceptatie.sdbstart.nl/api/

Productie

https://api.sdbstart.nl/api/

Voordat je met de WebAPI aan de slag kunt zijn de volgende gegevens benodigd, deze kan je vinden in SDB Beheer onder Koppelingen -> WebAPI.

Gegeven Omschrijving
UserDe gebruiker waarmee je de WebAPI aanroept, deze wordt gebruikt om je te identificeren.
KeyDe sleutel die gebruikt wordt in het authenticatie mechanisme.
KlantnummerHet klantnummer waarvoor de request wordt uitgevoerd, het kan zijn dat je bevoegd bent voor meerdere klanten binnen de WebAPI daarom willen we altijd weten voor welk klantnummer de requests bedoeld zijn. In veel gevallen is het klantnummer gelijk aan de user.

Binnen het REST principe geeft de request method aan wat het doel van de request is, hieronder een opsomming van de beschikbare request methods binnen onze WebAPI.

Methode Omschrijving
GETHet ophalen van één of meerdere objecten.
PUTUpdaten van een bestaand object.
POSTAanmaken van een nieuw object.
DELETEHet verwijderen van een bestaand object.

Authenticatie

Alle requests naar onze WebAPI moeten voorzien zijn van de volgende request headers.

Attribuut Omschrijving
TimestampDe huidige tijd in het volgende formaat dd-MM-yyyy HH:mm:ss.fff
KlantnummerHet klantnummer waarvoor de request van toepassing is
AuthenticationDit is een combinatie van je WebAPI User en de berekende authenticatie hash
Api-VersionDit is het versienummer van de API

De headers van een request komen er dan als volgt uit te zien:

Timestamp: 09-05-2014 11:40:05.126
Klantnummer: 8700
Authentication: 8700:sCLPWFj1yBEX9f47+vFs8xyinjjduJBWwiBjOP2yPTc=
Api-Version: 2.0

Onderstaand figuur geeft visueel weer hoe de authenticatie plaatsvindt tussen de client en de server:

Hoe wordt de hash opgebouwd?

De hash wordt opgebouwd uit 2 delen, de huidige timestamp van het request en het klantnummer, samen met de secret key worden deze gegevens gehashed en in de request headers meegestuurd naar de server.

Datum: 09-05-2014 11:40:05.126
Klantnummer: 8700
Key: 37d9ba90-2abf-4399-8dcc-e66fefed36d1

Hiermee bouwen we de volgende base string:

09-05-2014|11:40:05.126|8700

Vervolgens hashen we deze met de HMACSHA256 method

Hier komt de volgende voorbeeld hash uit:

iYGCj1kMae0fprWxSyEXGz0Wl8PUKxjPtMmt9vZyC84=

Voor PHP clients is het belangrijk om de raw_output optie aan te zetten bij het genereren van de hash

hash_hmac($raw_output = true)
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;

namespace API.Examples
{
    public class Authenticatie
    {
        static void Main(string[] args)
        {
            string apiUser = "8700";
            string apiKey = "37d9ba90-2abf-4399-8dcc-e66fefed36d1";
            string apiKlantNummer = "8700";
            string datum = DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss.fff");
            string baseString = string.Format("{0}|{1}|{2}", datum.Substring(0, 10), datum.Substring(11, 12), apiKlantNummer);

            var request = WebRequest.Create("https://acceptatie.sdbstart.nl/Start/api/dienstverbanden");
            request.Method = HttpMethod.Get.ToString();
            request.ContentType = "application/xml";
            request.Headers.Add("Timestamp", datum);
            request.Headers.Add("Klantnummer", apiKlantNummer);
            request.Headers.Add("Authentication", string.Format("{0}:{1}", apiUser, ComputeHash(apiKey, baseString)));
            request.Headers.Add("Api-Version", "2.0");

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();

            using (Stream responseStream = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
                var result = reader.ReadToEnd();

                XDocument xDocument = XDocument.Parse(result);
            }
        }

        private static string ComputeHash(string apiKey, string baseString)
        {
            var key = Encoding.UTF8.GetBytes(apiKey);
            string hashString;

            using (var hmac = new HMACSHA256(key))
            {
                var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString));
                hashString = Convert.ToBase64String(hash);
            }

            return hashString;
        }
    }
}

date_default_timezone_set('UTC');
$curl = curl_init();
$apiUser = "8700";
$apiKey = "37d9ba90-2abf-4399-8dcc-e66fefed36d1";
$apiKlantNummer = "8700";
$datum = date_format(date_create(), "d-m-Y H:i:s.u");
$baseString = sprintf("%s|%s|%s", substr($datum, 0, 10), substr($datum, 11, 12), $apiKlantNummer);
$headers = [
    'Content-Type: application/xml',
    'Accept: application/xml',
    'Timestamp: '.substr($datum, 0, 23),
    'Klantnummer: '.$apiKlantNummer,
    'Authentication: '.sprintf("%s:%s", $apiUser, base64_encode(hash_hmac("sha256", $baseString, strtoupper($apiKey), true))),
    'Api-Version: 2.0'
];

curl_setopt($curl, CURLOPT_URL, "https://api-acceptatie.sdbstart.nl/api/dienstverbanden");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

$server_output = curl_exec($curl);
curl_close ($curl);
print_r($server_output);
?>
import base64
import datetime
import hashlib
import hmac
import requests

apiUser = "8700"
apiKey = "37d9ba90-2abf-4399-8dcc-e66fefed36d1"
apiKlantNummer = "8700"

datum = datetime.datetime.utcnow()
baseString = "{0}|{1}|{2}".format(datum.strftime("%d-%m-%Y"), datum.strftime("%I:%M:%S.%f")[:-3], apiKlantNummer)
digest = hmac.new(apiKey.encode('utf-8'), msg=bytearray(baseString, 'utf-8'), digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest).decode()

url = "https://acceptatie.sdbstart.nl/Start/api/dienstverbanden"
headers = {
    "Content-Type": "application/xml",
    "Accept": "application/xml",
    "Timestamp": datum.strftime("%d-%m-%Y %I:%M:%S.%f")[:-3],
    "Klantnummer": apiKlantNummer,
    "Authentication": "{0}:{1}".format(apiUser, signature),
    "Api-Version": "2.0"
}

response = requests.get(url, headers=headers)

print(response.status_code);
print(response.text);

Errors

Omdat onze WebAPI volgens het REST principe is gebouwd gebruiken wij ook de HTTP status codes om aan te geven wat de status van elk request is. In het algemeen geldt dat status codes in de 200 range een succesvolle request representeren, de 400 range geeft dan weer aan dat er iets inhoudelijks fout is gegaan, en de 500 range dat er een technische fout is opgetreden. De meest voorkomende statuscodes zijn hieronder toegelicht.

Status Code Omschrijving
200OK
201Created
400Bad Request (ongeldige data of verkeerde formaat)
401Unauthorized (ongeldige HASH)
404URL not found
405Method not allowed
500Internal server error

Validatie

Bij elke request valideren wij de inhoud, eventuele fouten of validatiemeldingen koppelen wij terug. Wij maken onderscheid tussen 2 type meldingen.

Algemene meldingen

Dit gaat om algemene fouten zoals een verkeerde opbouw van de XML of een verkeerd datumformaat, vaak zijn deze van technische aard en voor de eindgebruiker niet belangrijk.

<Error>
    <Message>Ongeldige XML</Message>
</Error>

Modelstate meldingen

Alle models in onze API worden gevalideerd op compleetheid en correctheid, ook vinden er functionele validaties plaats, deze meldingen zijn wel belangrijk voor de eindgebruiker.

<Error>
    <Message>The request is invalid.</Message>
    <ModelState>
        <dienstverband.UrenPerWeek>The field UrenPerWeek must be between 0 and 50.</dienstverband.UrenPerWeek>
    </ModelState>
</Error>

Data specifiek

Het datumformaat wat gebruikt wordt volgt de ISO 8601 standaard.

Als binnen een object een property of subobject leeg gemaakt dient te worden gebeurt dit met het xsi:nil attribuut.

<Dienstverband xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <DatumUitDienst xsi:nil="true" />
</Dienstverband>

Voorbeeld 1: Contractvorm aanpassen

In dit voorbeeld passen we de contractvorm aan van parttimer naar fulltimer, een 200 response geeft aan dat de aanpassing gelukt is.

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;

namespace API.Examples
{
    public class Example1
    {
        static void Main(string[] args)
        {
            string apiUser = "8700";
            string apiKey = "37d9ba90-2abf-4399-8dcc-e66fefed36d1";
            string apiKlantNummer = "8700";
            string datum = DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss.fff");
            string baseString = string.Format("{0}|{1}|{2}", datum.Substring(0, 10), datum.Substring(11, 12), apiKlantNummer);

            var request = WebRequest.Create("https://api-acceptatie.sdbstart.nl/api/dienstverbanden/12");
            request.Method = HttpMethod.Put.ToString();
            request.ContentType = "application/xml";
            request.Headers.Add("Timestamp", datum);
            request.Headers.Add("Klantnummer", apiKlantNummer);
            request.Headers.Add("Authentication", string.Format("{0}:{1}", apiUser, ComputeHash(apiKey, baseString)));
            request.Headers.Add("Api-Version", "2.0");

            var xml = @"<Dienstverband xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
                            <UrenPerWeek>40</UrenPerWeek>
                            <Contractvorm>
                                <Code>11</Code>
                            </Contractvorm>
                        </Dienstverband>";

            var postData = Encoding.ASCII.GetBytes(xml);
            request.ContentLength = postData.Length;
            using (var dataStream = request.GetRequestStream())
            {
                dataStream.Write(postData, 0, postData.Length);
            }

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            using (Stream responseStream = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
                var result = reader.ReadToEnd();

                XDocument xDocument = XDocument.Parse(result);
            }
        }

        private static string ComputeHash(string apiKey, string baseString)
        {
            var key = Encoding.UTF8.GetBytes(apiKey);
            string hashString;

            using (var hmac = new HMACSHA256(key))
            {
                var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString));
                hashString = Convert.ToBase64String(hash);
            }

            return hashString;
        }
    }
}
import base64
import datetime
import hashlib
import hmac
import requests
import xml.etree.ElementTree as ET

def getRequestHeaders(datum):
    apiUser = "8700"
    apiSecret = "37d9ba90-2abf-4399-8dcc-e66fefed36d1"
    apiKlantNummer = "8700"
    baseString = "{0}|{1}|{2}".format(datum.strftime("%d-%m-%Y"), datum.strftime("%I:%M:%S.%f")[:-3], apiKlantNummer)
    digest = hmac.new(apiSecret.encode('utf-8'), msg=bytearray(baseString, 'utf-8'), digestmod=hashlib.sha256).digest()
    signature = base64.b64encode(digest).decode()

    headers = {
        "Content-Type": "application/xml",
        "Accept": "application/xml",
        "Timestamp": datum.strftime("%d-%m-%Y %I:%M:%S.%f")[:-3],
        "Klantnummer": apiKlantNummer,
        "Authentication": "{0}:{1}".format(apiUser, signature),
        "Api-Version": "2.0"
    }

    return headers

# Dienstverband met id 12 aanpassen
# De contractvorm kan ook met de WebAPI opgehaald worden
url = "https://api-acceptatie.sdbstart.nl/api/dienstverbanden/12"
xml = """<Dienstverband xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <UrenPerWeek>40</UrenPerWeek>
            <Contractvorm>
                <Code>11</Code>
            </Contractvorm>
        </Dienstverband>"""
response = requests.put(url=url, headers=getRequestHeaders(datetime.datetime.utcnow()), data=xml)

# Als het updaten gelukt is verwachten we hier een 200 response en het nieuwe dienstverband object
print(response.status_code)
print(response.text)

Voorbeeld 2: Verzuimmelding aanmaken

In dit voorbeeld melden we een dienstverband ziek, als het aanmaken gelukt is verwachten we een 201 response (Created).

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;

namespace API.Examples
{
    public class Example2
    {
        static void Main(string[] args)
        {
            string apiUser = "8700";
            string apiKey = "37d9ba90-2abf-4399-8dcc-e66fefed36d1";
            string apiKlantNummer = "8700";
            string datum = DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss.fff");
            string baseString = string.Format("{0}|{1}|{2}", datum.Substring(0, 10), datum.Substring(11, 12), apiKlantNummer);

            var request = WebRequest.Create("https://api-acceptatie.sdbstart.nl/api/verzuim/12");
            request.Method = HttpMethod.Post.ToString();
            request.ContentType = "application/xml";
            request.Headers.Add("Timestamp", datum);
            request.Headers.Add("Klantnummer", apiKlantNummer);
            request.Headers.Add("Authentication", string.Format("{0}:{1}", apiUser, ComputeHash(apiKey, baseString)));
            request.Headers.Add("Api-Version", "2.0");

            var xml = @"<Verzuim xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>
                            <BeginDatum>2017-01-05</BeginDatum>
                            <Percentage>100</Percentage>
                            <Reden><Code>1</Code></Reden>
                            <Ongeval><Code>0</Code></Ongeval>
                        </Verzuim>";

            var postData = Encoding.ASCII.GetBytes(xml);
            request.ContentLength = postData.Length;
            using (var dataStream = request.GetRequestStream())
            {
                dataStream.Write(postData, 0, postData.Length);
            }

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            using (Stream responseStream = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
                var result = reader.ReadToEnd();

                XDocument xDocument = XDocument.Parse(result);
            }
        }

        private static string ComputeHash(string apiKey, string baseString)
        {
            var key = Encoding.UTF8.GetBytes(apiKey);
            string hashString;

            using (var hmac = new HMACSHA256(key))
            {
                var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString));
                hashString = Convert.ToBase64String(hash);
            }

            return hashString;
        }
    }
}
import base64
import datetime
import hashlib
import hmac
import requests

def getRequestHeaders(datum):
    apiUser = "8700"
    apiSecret = "37d9ba90-2abf-4399-8dcc-e66fefed36d1"
    apiKlantNummer = "8700"
    baseString = "{0}|{1}|{2}".format(datum.strftime("%d-%m-%Y"), datum.strftime("%I:%M:%S.%f")[:-3], apiKlantNummer)
    digest = hmac.new(apiSecret.encode('utf-8'), msg=bytearray(baseString, 'utf-8'), digestmod=hashlib.sha256).digest()
    signature = base64.b64encode(digest).decode()

    headers = {
        "Content-Type": "application/xml",
        "Accept": "application/xml",
        "Timestamp": datum.strftime("%d-%m-%Y %I:%M:%S.%f")[:-3],
        "Klantnummer": apiKlantNummer,
        "Authentication": "{0}:{1}".format(apiUser, signature),
        "Api-Version": "2.0"
    }

    return headers

# Nieuwe verzuimmelding aanmaken
# De verzuimreden en ongevalcode kunnen ook met de WebAPI opgehaald worden
url = "https://api-acceptatie.sdbstart.nl/api/verzuim/12"
xml = """<Verzuim xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
            <BeginDatum>2017-01-05</BeginDatum>
            <Percentage>100</Percentage>
            <Reden><Code>1</Code></Reden>
            <Ongeval><Code>0</Code></Ongeval>
        </Verzuim>"""

response = requests.post(url=url, headers=getRequestHeaders(datetime.datetime.utcnow()), data=xml)

# Als het aanmaken van de verzuimmelding gelukt is verwachten we hier een 201 response en het nieuwe afdeling object
print(response.status_code)
print(response.text)

Voorbeeld 3: Toekomstige verhuizing doorvoeren

In dit voorbeeld gaat een medewerker in de toekomst verhuizen en voeren we een mutatie in om op die datum de adreswijziging door te voeren, bij een succesvolle request verwachten we hier een 200 response.

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;

namespace API.Examples
{
    public class Example3
    {
        static void Main(string[] args)
        {
            string apiUser = "8700";
            string apiKey = "37d9ba90-2abf-4399-8dcc-e66fefed36d1";
            string apiKlantNummer = "8700";
            string datum = DateTime.UtcNow.ToString("dd-MM-yyyy HH:mm:ss.fff");
            string baseString = string.Format("{0}|{1}|{2}", datum.Substring(0, 10), datum.Substring(11, 12), apiKlantNummer);

            var request = WebRequest.Create("https://api-acceptatie.sdbstart.nl/api/adressen/14037/2020-12-01");
            request.Method = HttpMethod.Put.ToString();
            request.ContentType = "application/xml";
            request.Headers.Add("Timestamp", datum);
            request.Headers.Add("Klantnummer", apiKlantNummer);
            request.Headers.Add("Authentication", string.Format("{0}:{1}", apiUser, ComputeHash(apiKey, baseString)));
            request.Headers.Add("Api-Version", "2.0");

            var xml = @"<Adres xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
                            <Straatnaam>Regulusweg</Straatnaam>
                            <Postcode>2516AC</Postcode>
                            <Woonplaats>Den Haag</Woonplaats>
                            <Huisnummer>11</Huisnummer>
                         </Adres>";

            var postData = Encoding.ASCII.GetBytes(xml);
            request.ContentLength = postData.Length;
            using (var dataStream = request.GetRequestStream())
            {
                dataStream.Write(postData, 0, postData.Length);
            }

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            using (Stream responseStream = response.GetResponseStream())
            {
                StreamReader reader = new StreamReader(responseStream, Encoding.UTF8);
                var result = reader.ReadToEnd();

                XDocument xDocument = XDocument.Parse(result);
            }
        }

        private static string ComputeHash(string apiKey, string baseString)
        {
            var key = Encoding.UTF8.GetBytes(apiKey);
            string hashString;

            using (var hmac = new HMACSHA256(key))
            {
                var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(baseString));
                hashString = Convert.ToBase64String(hash);
            }

            return hashString;
        }
    }
}
import base64
import datetime
import hashlib
import hmac
import requests

def getRequestHeaders(datum):
    apiUser = "8700"
    apiKey = "37d9ba90-2abf-4399-8dcc-e66fefed36d1"
    apiKlantNummer = "8700"
    baseString = "{0}|{1}|{2}".format(datum.strftime("%d-%m-%Y"), datum.strftime("%I:%M:%S.%f")[:-3], apiKlantNummer)
    digest = hmac.new(apiKey.encode('utf-8'), msg=bytearray(baseString, 'utf-8'), digestmod=hashlib.sha256).digest()
    signature = base64.b64encode(digest).decode()

    headers = {
        "Content-Type": "application/xml",
        "Accept": "application/xml",
        "Timestamp": datum.strftime("%d-%m-%Y %I:%M:%S.%f")[:-3],
        "Klantnummer": apiKlantNummer,
        "Authentication": "{0}:{1}".format(apiUser, signature),
        "Api-Version": "2.0"
    }

    return headers

# We passen het adres van medewerker 14037 aan met een ingangsdatum van 1 december 2020
url = "https://api-acceptatie.sdbstart.nl/api/adressen/14037/2020-12-01"
xml = """<Adres xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <Straatnaam>Regulusweg</Straatnaam>
            <Postcode>2516AC</Postcode>
            <Woonplaats>Den Haag</Woonplaats>
            <Huisnummer>11</Huisnummer>
        </Adres>"""

response = requests.put(url=url, headers=getRequestHeaders(datetime.datetime.utcnow()), data=xml)

# Als het bewerken van het adres is gelukt verwachten we hier een 200 response en het aangepast adres object
print(response.status_code)
print(response.text)

Resources