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:
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 |
---|---|
User | De gebruiker waarmee je de WebAPI aanroept, deze wordt gebruikt om je te identificeren. |
Key | De sleutel die gebruikt wordt in het authenticatie mechanisme. |
Klantnummer | Het 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 |
---|---|
GET | Het ophalen van één of meerdere objecten. |
PUT | Updaten van een bestaand object. |
POST | Aanmaken van een nieuw object. |
DELETE | Het verwijderen van een bestaand object. |
Alle requests naar onze WebAPI moeten voorzien zijn van de volgende request headers.
Attribuut | Omschrijving |
---|---|
Timestamp | De huidige tijd in het volgende formaat dd-MM-yyyy HH:mm:ss.fff |
Klantnummer | Het klantnummer waarvoor de request van toepassing is |
Authentication | Dit is een combinatie van je WebAPI User en de berekende authenticatie hash |
Api-Version | Dit 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:
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.
Hiermee bouwen we de volgende base string:
09-05-2014|11:40:05.126|8700
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);
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 |
---|---|
200 | OK |
201 | Created |
400 | Bad Request (ongeldige data of verkeerde formaat) |
401 | Unauthorized (ongeldige HASH) |
404 | URL not found |
405 | Method not allowed |
500 | Internal server error |
Bij elke request valideren wij de inhoud, eventuele fouten of validatiemeldingen koppelen wij terug. Wij maken onderscheid tussen 2 type 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>
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>
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>
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)
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)
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)