Handleiding

Hiretime SSO Documentatie

Welkom bij de ontwikkelaars- en integratiedocumentatie van Hiretime. Deze handleiding legt stap voor stap uit hoe u uw leerling- en medewerkersgegevens (SIS / HRIS) veilig kunt synchroniseren met ons platform via de Open Education API (OEAPI v6.0) of het Edu-V (v1.1.0) protocol.

1. Overzicht & Architectuur

Via SSO stelt Hiretime onderwijsinstellingen en zakelijke klanten in staat om studenten, docenten, klassen en groepslidmaatschappen vooraf in ons systeem klaar te zetten. Hierdoor worden gebruikers direct bij hun eerste Single Sign-On (SSO) login aan de juiste organisatie en klassen gekoppeld.

Belangrijkste voordelen:

  • Geautomatiseerde synchronisatie van student- en docentgegevens.
  • Ondersteuning voor standaard onderwijsprotocollen (OEAPI v6.0 en Edu-V v1.1.0).
  • Ondersteuning voor asynchrone batchverwerking voor optimale schaalbaarheid.
  • Veilige asymmetrische sleutelauthenticatie (EdDSA / Ed25519 JWT tokens).

2. Authenticatie & JWT Signing

Wanneer er een API Key voor uw organisatie wordt aangemaakt in het Hiretime Dashboard, ontvangt u een uitnodigingsmail met uw Company ID (cid), Key ID (kid), de vereiste claims en een beveiligde eenmalige link om uw Private Key in te zien. Om veiligheidsredenen wordt de private key nooit per e-mail verzonden en kan deze slechts éénmalig worden bekeken. Sla de private key direct veilig op in uw eigen sleutelbeheer (KMS / Key Vault).

Authenticatiestappen:

  1. Genereer een JWT (JSON Web Token): Onderteken elk API-verzoek met een cryptografisch JWT token op basis van het EdDSA (Ed25519) algoritme met behulp van uw private key.
  2. Verplichte Claims in de JWT Payload:
    • cid: Uw unieke Company ID (bijv. oXpL0ex3Q1jaPvo8eeLQ).
    • kid: Uw unieke Key ID (bijv. oZBTEYDs4ArVv33dNoQa).
    • iss: Uw e-mailadres (de issuer, zoals vermeld in de uitnodigingsmail).
    • exp: Expiratietijd (aanbevolen: maximaal 15 minuten tot 1 uur geldig).
    • iat: Tijdstip van uitgifte (Unix epoch timestamp in seconden).
  3. Stuur de Authorization Header mee:

    Voeg het gegenereerde token toe aan het HTTP-verzoek via de header:

    Authorization: Bearer <uw_gegenereerde_jwt_token>

Voorbeeld JWT Header & Payload

JWT Header
{
  "alg": "EdDSA",
  "typ": "JWT"
}
JWT Payload
{
  "cid": "oXpL0ex3Q1jaPvo8eeLQ",
  "kid": "oZBTEYDs4ArVv33dNoQa",
  "iss": "ict-beheerder@school.nl",
  "iat": 1726000000,
  "exp": 1726003600
}

Voorbeeld JWT Generatie Code (Node.js & Python)

Node.js (met ingebouwde 'crypto' module)
const crypto = require('crypto');
const fs = require('fs');

// 32-byte raw Ed25519 private key in base64
const privateKeyB64 = fs.readFileSync('./private_key.pem', 'utf8').trim();
const rawPrivKey = Buffer.from(privateKeyB64, 'base64');
const pkcs8Header = Buffer.from('302e020100300506032b657004220420', 'hex');
const pkcs8Key = Buffer.concat([pkcs8Header, rawPrivKey]);

const privateKey = crypto.createPrivateKey({
  key: pkcs8Key,
  format: 'der',
  type: 'pkcs8'
});

function base64Url(input) {
  return Buffer.from(input).toString('base64url');
}

const now = Math.floor(Date.now() / 1000);
const header = { alg: 'EdDSA', typ: 'JWT' };
const payload = {
  cid: 'YOUR_COMPANY_ID',
  kid: 'YOUR_KEY_ID',
  iss: 'YOUR_EMAIL_ADDRESS',
  iat: now,
  exp: now + 3600
};

const encodedHeader = base64Url(JSON.stringify(header));
const encodedPayload = base64Url(JSON.stringify(payload));
const dataToSign = Buffer.from(`${encodedHeader}.${encodedPayload}`);
const signature = crypto.sign(null, dataToSign, privateKey);
const token = `${encodedHeader}.${encodedPayload}.${base64Url(signature)}`;

console.log('Authorization: Bearer ' + token);
Python (PyJWT & cryptography)
import base64
import time
import jwt
from cryptography.hazmat.primitives.asymmetric import ed25519

# 32-byte raw Ed25519 private key in base64
with open('private_key.pem', 'r') as f:
    raw_key = base64.b64decode(f.read().strip())

private_key = ed25519.Ed25519PrivateKey.from_private_bytes(raw_key)

now = int(time.time())
payload = {
    'cid': 'YOUR_COMPANY_ID',
    'kid': 'YOUR_KEY_ID',
    'iss': 'YOUR_EMAIL_ADDRESS',
    'iat': now,
    'exp': now + 3600
}

token = jwt.encode(payload, private_key, algorithm='EdDSA')
print(f"Authorization: Bearer {token}")

3. Ondersteunde Protocollen (OEAPI & Edu-V)

De Hiretime API biedt volledige ondersteuning voor twee vooraanstaande standaarden op het gebied van onderwijs- en persoonsgegevensuitwisseling:

Open Education API (OOAPI / OEAPI v6.0)

OEAPI is de landelijke SURF-standaard voor hoger- en mbo-onderwijs in Nederland. Het protocol maakt gebruik van gestandaardiseerde datamodellen voor personen (studenten/docenten), groepen (klassen) en memberships (groepskoppelingen).

In de Hiretime API stuurt u verzoeken naar de OEAPI endpoints met payloads die direct matchen met de OEAPI schema's:

  • Personen: personId, email, givenName, surname.
  • Groepen: groupId, name (meertalige array).
  • Memberships: personId, groupId, role, state.

4. API Endpoints & Request Structuur

Alle API-verzoeken dienen verzonden te worden naar de basishost https://sso.hiretime.nl met Content-Type application/json.

Generieke Request Wrapper: Elk import endpoint verwacht een JSON object met companyId, optioneel syncId (voor sessietracering) en een data array met de specifieke entiteiten:

{
  "companyId": "YOUR_COMPANY_ID",
  "syncId": "optional-uuid-v4-session-id",
  "data": [ ... ]
}

OEAPI Endpoints (v6.0)

Methode Endpoint Omschrijving
POST /v1/oeapi/imports/students Importeer of synchroniseer studenten via OEAPI persoonsstructuur.
POST /v1/oeapi/imports/teachers Importeer of synchroniseer docenten via OEAPI persoonsstructuur.
POST /v1/oeapi/imports/classes Importeer groepen / klassen via OEAPI groepsstructuur.
POST /v1/oeapi/imports/memberships Importeer koppelingsgegevens tussen personen en groepen.

Voorbeeld Request Payload (OEAPI Studenten):

POST /v1/oeapi/imports/students
Authorization: Bearer <jwt_token>
Content-Type: application/json

{
  "companyId": "oXpL0ex3Q1jaPvo8eeLQ",
  "syncId": "c0a80101-0000-1000-8000-000000000001",
  "data": [
    {
      "personId": "s1234567",
      "givenName": "Jan",
      "surname": "De Vries",
      "email": "j.devries@student.roc.nl"
    },
    {
      "personId": "s7654321",
      "givenName": "Sanne",
      "surname": "Bakker",
      "email": "s.bakker@student.roc.nl"
    }
  ]
}

Edu-V Endpoints (v1.1.0)

Methode Endpoint Omschrijving
POST /v1/eduv/imports/students Importeer studenten via Edu-V Students API formaat.
POST /v1/eduv/imports/teachers Importeer medewerkers/docenten via Edu-V Employees API formaat.
POST /v1/eduv/imports/classes Importeer groepen via Edu-V Group structuur.
POST /v1/eduv/imports/memberships Importeer groepslidmaatschappen of lesroostertoewijzingen.

Voorbeeld Request Payload (Edu-V Studenten):

POST /v1/eduv/imports/students
Authorization: Bearer <jwt_token>
Content-Type: application/json

{
  "companyId": "oXpL0ex3Q1jaPvo8eeLQ",
  "syncId": "c0a80101-0000-1000-8000-000000000001",
  "data": [
    {
      "userMasterIdentifier": "eck-1001",
      "userIds": [
        { "userId": "s1234567", "userIdType": "studentNumber" }
      ],
      "givenName": "Jan",
      "familyName": "De Vries",
      "email": "j.devries@student.roc.nl",
      "status": "active",
      "dateCreated": 1768464000000000,
      "dateLastModified": 1778500800000000
    }
  ]
}

Voorbeeld Request Payload (Edu-V Docenten):

POST /v1/eduv/imports/teachers
Authorization: Bearer <jwt_token>
Content-Type: application/json

{
  "companyId": "oXpL0ex3Q1jaPvo8eeLQ",
  "syncId": "c0a80101-0000-1000-8000-000000000001",
  "data": [
    {
      "userIds": [
        { "userId": "emp_9901", "userIdType": "employeeNumber" }
      ],
      "givenName": "Maria",
      "familyName": "Jansen",
      "email": "m.jansen@roc.nl",
      "status": "active",
      "dateCreated": 1768464000000000,
      "dateLastModified": 1778500800000000
    }
  ]
}

Opmerking over Edu-V velden: Het veld status ('active') is verplicht. Tijdstempels zoals dateCreated en dateLastModified worden meegegeven als Unix microseconden (UTC) of ISO 8601 strings. Identificaties binnen userIds maken gebruik van userIdType (bijv. studentNumber of employeeNumber).

5. Job Status & Error Monitoring

Aangezien importbatches asynchroon verwerkt worden, geeft het import endpoint direct een ImportJob reactie terug met een unieke jobId en de initiële status pending.

Response Voorbeeld op Import Call:

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

{
  "jobId": "job_987654321_abc",
  "companyId": "oXpL0ex3Q1jaPvo8eeLQ",
  "status": "pending",
  "totalRecords": 2,
  "processedRecords": 0,
  "errors": [],
  "createdAt": "2026-09-10T09:00:00.000Z"
}

Status Opvragen

Gebruik onderstaand endpoint om de voortgang van de import job te monitoren:

GET /v1/imports/status/<jobId>
Authorization: Bearer <jwt_token>

Mogelijke statussen:

  • pending: De job is in de wachtrij geplaatst.
  • processing: De records worden verwerkt en gesynchroniseerd.
  • completed: De import is succesvol afgerond.
  • failed: Er is een fout opgetreden tijdens het verwerken.

Fouten Inzien

Indien een job status failed heeft of er specifieke recordfouten zijn opgetreden, vraagt u de foutenreeks op via:

GET /v1/imports/status/<jobId>/errors
Authorization: Bearer <jwt_token>

Voorbeeld Error Response:

{
  "jobId": "job_987654321_abc",
  "errors": [
    "Record 1 (s1234567): Ongeldig e-mailadres formaat.",
    "Record 2 (s7654321): Groep 'grp_99' niet gevonden."
  ]
}

6. Specificaties & Ondersteuning

Raadpleeg de officiële specificaties voor aanvullende details over het OEAPI en Edu-V afsprakenstelsel:

Hulp of Vragen?

Ons technische ondersteuningsteam staat voor u klaar bij het opzetten en testen van uw SSO import koppeling.

E-mail: support@hiretime.nl