Portfolio API
Een beveiligde, alleen-lezen API waarmee je je voortgang, leeruitkomsten en badges op je eigen website toont. Jij bepaalt wat er gedeeld wordt.
Heb je een account? Log in en maak je eigen token, met live voorbeelden van je eigen gegevens.
https://lms.187-124-29-171.sslip.io/api/v1/portfolio
· Altijd JSON met de inhoud onder "data" en onder "meta" het moment (generated_at) en de API-versie. Datums en tijden zijn ISO 8601.
· Authenticatie: Authorization: Bearer <token>
De Portfolio API is een read-only JSON-API. Je website vraagt je gegevens op met een token en toont ze zoals jij wilt.
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/profile"
Server
Het token blijft geheim op je server, bijvoorbeeld in PHP, Node of een script dat je site bouwt (GitHub Actions). Een verzoek vanuit een browser met dit token wordt geweigerd.
Browser
Het token staat in de JavaScript van je website en is dus zichtbaar. Daarom werkt het alleen vanaf de domeinen die je opgeeft, en alleen voor de onderdelen die je kiest.
Bij een fout van de Portfolio API krijg je een JSON-object met een vaste code in "error" en een uitleg in "message".
| Status | error |
Betekenis |
|---|---|---|
| 401 | invalid_token | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | missing_scope | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 403 | origin_not_allowed | Browsertoken gebruikt vanaf een domein dat niet bij het token staat. |
| 403 | server_token_in_browser | Een servertoken gebruikt vanuit een browser. Gebruik een browsertoken. |
| 403 | blocked | De beheerder heeft de Portfolio API voor dit account geblokkeerd. |
| 403 | account_unavailable | Het account is niet actief of mag de Portfolio API niet (meer) gebruiken. |
| 404 | — | Dit endpoint of dit badge-icoon bestaat niet. De body heeft alleen "message". |
| 422 | — | Een ongeldige query-parameter, bijv. een verkeerde datum bij since. Met "message" en "errors" per parameter. |
| 429 | — | Te veel verzoeken. Wacht het aantal seconden uit de header Retry-After. |
| 304 | — | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Het startpunt: je weergavenaam, welke onderdelen dit token mag lezen, en de URL's van die onderdelen. Handig om je token te testen.
https://lms.187-124-29-171.sslip.io/api/v1/portfolio
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| display_name | string | Je weergavenaam uit je portfolio-instellingen, anders je naam. | Sanne de Vries |
| scopes | array<string> | De onderdelen die dit token mag lezen: profile, outcomes, progress, events en/of work. | ["profile","progress"] |
| endpoints | object<string, url> | Per onderdeel dat dit token mag lezen de volledige URL, plus de tijdlijn. | {"profile":"{api}/profile","badges":"{api}/badges","timeline":"{api}/timeline"} |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Je weergavenaam en de URL's van de onderdelen die dit token mag lezen. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"display_name": "Sanne de Vries",
"scopes": [
"profile",
"progress"
],
"endpoints": {
"profile": "https://lms.187-124-29-171.sslip.io/api/v1/portfolio/profile",
"badges": "https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges",
"timeline": "https://lms.187-124-29-171.sslip.io/api/v1/portfolio/timeline"
}
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/profile
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| display_name | string | Je weergavenaam uit je portfolio-instellingen, anders je naam. | Sanne de Vries |
| avatar_url | url|null | Link naar je profielfoto. null als je geen foto hebt of "Profielfoto tonen" uit staat. | null |
| avatar_emoji | string|null | De emoji-avatar die je in de shop koos, of null. | 🦊 |
| title | string | De titel die je naast je naam draagt. | Bug hunter |
| member_since | date|null | Sinds wanneer je een account hebt. | 2025-09-01 |
| education | array | Je opleidingen: waar je nu studeert of mee bent gediplomeerd. Leeg als je zelf een account maakte zonder school (een cursist) — die heeft geen klas. | |
| education[].qualification | string | Naam van de opleiding (het kwalificatiedossier). | Software Developer |
| education[].crebo | string|null | Het crebonummer van de opleiding. | 25998 |
| education[].cohort | string|null | Je cohort, meestal het startjaar. | 2025 |
| education[].year | integer|null | Het leerjaar van je klas. | 2 |
| education[].status | string | studying (studeert nog) of graduated (gediplomeerd). | studying |
| education[].graduated_at | date|null | Datum van diplomering, anders null. | null |
| education[].school | string|null | Naam van de school. null tenzij "School tonen" aan staat. | null |
| level | integer | Je level, op basis van je XP. | 7 |
| total_xp | integer | Het totaal aan ervaringspunten dat je verdiende. | 1840 |
| level_progress_percent | integer | Hoe ver je bent naar het volgende level, 0 t/m 100. | 64 |
| xp_to_next_level | integer|null | XP die je nog nodig hebt voor het volgende level; null op het hoogste level. | 160 |
| current_streak | integer | Aantal dagen op rij dat je nu actief bent. | 4 |
| longest_streak | integer | Je langste reeks actieve dagen ooit. | 12 |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Je profiel: naam, avatar, opleiding, level en streaks. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"display_name": "Sanne de Vries",
"avatar_url": null,
"avatar_emoji": "🦊",
"title": "Bug hunter",
"member_since": "2025-09-01",
"education": [
{
"qualification": "Software Developer",
"crebo": "25998",
"cohort": "2025",
"year": 2,
"status": "studying",
"graduated_at": null,
"school": null
}
],
"level": 7,
"total_xp": 1840,
"level_progress_percent": 64,
"xp_to_next_level": 160,
"current_streak": 4,
"longest_streak": 12
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/profile"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/profile", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/profile', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
<div id="portfolio-profile">Laden…</div>
<script>
(async () => {
const doel = document.getElementById("portfolio-profile");
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/profile", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) { doel.textContent = "Portfolio niet beschikbaar."; return; }
const { data } = await response.json();
doel.textContent = "";
const h = document.createElement("h2");
h.textContent = data.display_name;
const p = document.createElement("p");
p.textContent = `${data.title} · level ${data.level} · ${data.total_xp} XP`;
doel.append(h, p);
for (const opleiding of data.education) {
const li = document.createElement("p");
li.textContent = `${opleiding.qualification} (${opleiding.status === "graduated" ? "gediplomeerd" : "studeert"})`;
doel.append(li);
}
})();
</script>
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| level | integer | Je level, op basis van je XP. | 7 |
| total_xp | integer | Het totaal aan ervaringspunten dat je verdiende. | 1840 |
| level_progress_percent | integer | Hoe ver je bent naar het volgende level, 0 t/m 100. | 64 |
| xp_to_next_level | integer|null | XP die je nog nodig hebt voor het volgende level; null op het hoogste level. | 160 |
| current_streak | integer | Aantal dagen op rij dat je nu actief bent. | 4 |
| longest_streak | integer | Je langste reeks actieve dagen ooit. | 12 |
| totals | object | Aantal behaalde badges per tier. | |
| totals.bronze | integer | Aantal bronzen badges. | 5 |
| totals.silver | integer | Aantal zilveren badges. | 2 |
| totals.gold | integer | Aantal gouden badges. | 1 |
| totals.total | integer | Aantal badges in totaal. | 8 |
| badges | array | Je behaalde badges, nieuwste eerst. | |
| badges[].key | string | Vaste code van de badge; ook te gebruiken voor het icoon. | story.10 |
| badges[].name | string | Naam van de badge. | Verhalenverteller |
| badges[].description | string|null | Waarvoor je de badge krijgt. | Tien user stories afgerond. |
| badges[].tier | string | bronze, silver of gold. | silver |
| badges[].icon | string | Naam van het icoon (Heroicons). | book-open |
| badges[].subject | string|null | Bij een badge voor één onderdeel (bijv. een course): de naam daarvan. Anders null. | null |
| badges[].earned_at | datetime | Wanneer je de badge behaalde. | 2026-03-02T14:05:00+01:00 |
| badges[].icon_url | url | Het icoon als SVG, direct te gebruiken in een img-tag. Voeg ?color=hex toe voor een kleur. | {api}/badges/story.10/icon.svg |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Je behaalde badges met aantallen per tier. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"level": 7,
"total_xp": 1840,
"level_progress_percent": 64,
"xp_to_next_level": 160,
"current_streak": 4,
"longest_streak": 12,
"totals": {
"bronze": 5,
"silver": 2,
"gold": 1,
"total": 8
},
"badges": [
{
"key": "story.10",
"name": "Verhalenverteller",
"description": "Tien user stories afgerond.",
"tier": "silver",
"icon": "book-open",
"subject": null,
"earned_at": "2026-03-02T14:05:00+01:00",
"icon_url": "https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges/story.10/icon.svg"
}
]
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
<div id="portfolio-badges">Laden…</div>
<script>
(async () => {
const doel = document.getElementById("portfolio-badges");
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) { doel.textContent = "Portfolio niet beschikbaar."; return; }
const { data } = await response.json();
doel.textContent = "";
for (const badge of data.badges) {
const img = document.createElement("img");
img.src = badge.icon_url + "?color=0886a8";
img.width = 40;
img.alt = badge.name;
img.title = `${badge.name} (${badge.tier})`;
doel.append(img);
}
})();
</script>
Het icoon van een badge als SVG. Geen token nodig: zet de URL uit icon_url direct in een img-tag.
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges/{key}/icon.svg
Query-parameters
| Veld | Type | Voorbeeld | Betekenis |
|---|---|---|---|
color |
string | 0886a8 | Kleur van het icoon als hex zonder #, standaard 27272a. |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Het icoon als SVG, klaar voor een img-tag. |
| 404 | Dit endpoint of dit badge-icoon bestaat niet. De body heeft alleen "message". |
Voorbeeldcode
<img src="https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges/story.10/icon.svg?color=0886a8" width="48" height="48" alt="Badge">
curl "https://lms.187-124-29-171.sslip.io/api/v1/portfolio/badges/story.10/icon.svg?color=0886a8" -o badge.svg
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/outcomes
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| qualifications | array | Je opleidingen met hun leeruitkomsten. Leeg zonder klas (bijv. een cursist zonder school). | |
| qualifications[].name | string | Naam van de opleiding (het kwalificatiedossier). | Software Developer |
| qualifications[].crebo | string|null | Het crebonummer van de opleiding. | 25998 |
| qualifications[].levels | array | Per niveau (1, 2 en 3) hoeveel leeruitkomsten er zijn en hoeveel je er aantoonde en behaalde. | |
| qualifications[].levels[].level | integer | Het niveau: 1, 2 of 3. | 2 |
| qualifications[].levels[].total | integer | Aantal leeruitkomsten op dit niveau. | 18 |
| qualifications[].levels[].demonstrated | integer | Aantal dat je hebt aangetoond (in de praktijk laten zien, nog niet afgetekend), inclusief behaald. | 11 |
| qualifications[].levels[].achieved | integer | Aantal dat je echt hebt behaald: afgetekend na een criteriumgericht interview. | 6 |
| qualifications[].core_tasks | array | De kerntaken van de opleiding. | |
| qualifications[].core_tasks[].code | string|null | Code van de kerntaak, bijv. B1-K1. | B1-K1 |
| qualifications[].core_tasks[].name | string | Naam van de kerntaak. | Realiseert software |
| qualifications[].core_tasks[].work_processes | array | De werkprocessen binnen deze kerntaak. | |
| qualifications[].core_tasks[].work_processes[].code | string|null | Code van het werkproces, bijv. B1-K1-W2. | B1-K1-W2 |
| qualifications[].core_tasks[].work_processes[].name | string | Naam van het werkproces. | Realiseert (onderdelen van) software |
| qualifications[].core_tasks[].work_processes[].levels | array | Per niveau (1, 2 en 3) hoeveel leeruitkomsten er zijn en hoeveel je er aantoonde en behaalde. | |
| qualifications[].core_tasks[].work_processes[].levels[].level | integer | Het niveau: 1, 2 of 3. | 2 |
| qualifications[].core_tasks[].work_processes[].levels[].total | integer | Aantal leeruitkomsten op dit niveau. | 18 |
| qualifications[].core_tasks[].work_processes[].levels[].demonstrated | integer | Aantal dat je hebt aangetoond (in de praktijk laten zien, nog niet afgetekend), inclusief behaald. | 11 |
| qualifications[].core_tasks[].work_processes[].levels[].achieved | integer | Aantal dat je echt hebt behaald: afgetekend na een criteriumgericht interview. | 6 |
| qualifications[].core_tasks[].work_processes[].achieved | array | De leeruitkomsten die je binnen dit werkproces hebt behaald. | |
| qualifications[].core_tasks[].work_processes[].achieved[].text | string | De tekst van de leeruitkomst. | Schrijft leesbare, geteste code |
| qualifications[].core_tasks[].work_processes[].achieved[].type | string | Soort: criterium, hoofd (hoofdleeruitkomst), niveau of techniek. | niveau |
| qualifications[].core_tasks[].work_processes[].achieved[].level | integer|null | Het niveau (1 t/m 3) bij een niveauleeruitkomst, anders null. | 2 |
| qualifications[].core_tasks[].work_processes[].achieved[].achieved_at | datetime|null | Wanneer je deze leeruitkomst behaalde. | 2026-02-11T09:30:00+01:00 |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Je leeruitkomsten per opleiding, met wat je hebt aangetoond en behaald. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"qualifications": [
{
"name": "Software Developer",
"crebo": "25998",
"levels": [
{
"level": 2,
"total": 18,
"demonstrated": 11,
"achieved": 6
}
],
"core_tasks": [
{
"code": "B1-K1",
"name": "Realiseert software",
"work_processes": [
{
"code": "B1-K1-W2",
"name": "Realiseert (onderdelen van) software",
"levels": [
{
"level": 2,
"total": 18,
"demonstrated": 11,
"achieved": 6
}
],
"achieved": [
{
"text": "Schrijft leesbare, geteste code",
"type": "niveau",
"level": 2,
"achieved_at": "2026-02-11T09:30:00+01:00"
}
]
}
]
}
]
}
]
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/outcomes"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/outcomes", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/outcomes', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
<div id="portfolio-outcomes">Laden…</div>
<script>
(async () => {
const doel = document.getElementById("portfolio-outcomes");
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/outcomes", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) { doel.textContent = "Portfolio niet beschikbaar."; return; }
const { data } = await response.json();
doel.textContent = "";
for (const opleiding of data.qualifications) {
const h = document.createElement("h3");
h.textContent = opleiding.name;
doel.append(h);
for (const niveau of opleiding.levels) {
const label = document.createElement("div");
label.textContent = `Niveau ${niveau.level}: ${niveau.achieved} van ${niveau.total} behaald`;
const balk = document.createElement("progress");
balk.max = niveau.total || 1;
balk.value = niveau.achieved;
doel.append(label, balk);
}
}
})();
</script>
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/progress
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| courses | array | De courses waarvoor je bent ingeschreven. | |
| courses[].name | string | Naam van de course of het project. | Laravel Advanced |
| courses[].description | string|null | Korte omschrijving. | Bouw een API met Laravel. |
| courses[].status | string | in_progress (bezig) of completed (afgerond). | in_progress |
| courses[].percentage | integer | Hoe ver je bent, 0 t/m 100, over sprints en stories samen. | 75 |
| courses[].sprints | object | Afgeronde sprints tegenover het totaal. | |
| courses[].sprints.done | integer | Aantal afgerond. | 3 |
| courses[].sprints.total | integer | Aantal in totaal. | 4 |
| courses[].stories | object | Afgeronde user stories tegenover het totaal. | |
| courses[].stories.done | integer | Aantal afgerond. | 12 |
| courses[].stories.total | integer | Aantal in totaal. | 16 |
| courses[].completed_at | datetime|null | Wanneer je het afrondde; null als het nog niet af is. | null |
| projects | array | De projecten waarin je in een team zit. | |
| projects[].name | string | Naam van de course of het project. | Webshop voor de bakker |
| projects[].description | string|null | Korte omschrijving. | Een webshop in teamverband. |
| projects[].role | string|null | Je rol in het team, bijv. Scrum master, of null. | Scrum master |
| projects[].team_size | integer | Aantal leden in je team. | 4 |
| projects[].status | string | in_progress (bezig) of completed (afgerond). | in_progress |
| projects[].percentage | integer | Hoe ver je bent, 0 t/m 100, over sprints en stories samen. | 75 |
| projects[].sprints | object | Afgeronde sprints tegenover het totaal. | |
| projects[].sprints.done | integer | Aantal afgerond. | 3 |
| projects[].sprints.total | integer | Aantal in totaal. | 4 |
| projects[].stories | object | Afgeronde user stories tegenover het totaal. | |
| projects[].stories.done | integer | Aantal afgerond. | 12 |
| projects[].stories.total | integer | Aantal in totaal. | 16 |
| projects[].completed_at | datetime|null | Wanneer je het afrondde; null als het nog niet af is. | null |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Je courses en projecten met hun voortgang. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"courses": [
{
"name": "Laravel Advanced",
"description": "Bouw een API met Laravel.",
"status": "in_progress",
"percentage": 75,
"sprints": {
"done": 3,
"total": 4
},
"stories": {
"done": 12,
"total": 16
},
"completed_at": null
}
],
"projects": [
{
"name": "Webshop voor de bakker",
"description": "Een webshop in teamverband.",
"role": "Scrum master",
"team_size": 4,
"status": "in_progress",
"percentage": 75,
"sprints": {
"done": 3,
"total": 4
},
"stories": {
"done": 12,
"total": 16
},
"completed_at": null
}
]
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/progress"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/progress", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/progress', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
<div id="portfolio-progress">Laden…</div>
<script>
(async () => {
const doel = document.getElementById("portfolio-progress");
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/progress", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) { doel.textContent = "Portfolio niet beschikbaar."; return; }
const { data } = await response.json();
doel.textContent = "";
for (const course of [...data.courses, ...data.projects]) {
const label = document.createElement("div");
label.textContent = `${course.name} — ${course.percentage}%`;
const balk = document.createElement("progress");
balk.max = 100;
balk.value = course.percentage;
doel.append(label, balk);
}
})();
</script>
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/learning-paths
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| learning_paths | array | De leerpaden van je klas. Leeg zonder klas (bijv. een cursist zonder school). | |
| learning_paths[].name | string | Naam van het leerpad. | Leerpad Software Developer |
| learning_paths[].qualification | string|null | De opleiding waar het leerpad bij hoort, of null. | Software Developer |
| learning_paths[].completed | integer | Aantal afgeronde onderdelen. | 5 |
| learning_paths[].total | integer | Aantal onderdelen in het leerpad. | 9 |
| learning_paths[].items | array | De onderdelen, in de volgorde van het leerpad. | |
| learning_paths[].items[].name | string | Naam van de course of het project. | Git basis |
| learning_paths[].items[].type | string | course of project. | course |
| learning_paths[].items[].status | string | dicht (nog niet beschikbaar), geblokkeerd (eerst iets anders afronden), open (kan beginnen), bezig, wacht_op_team of afgerond. | afgerond |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | De leerpaden van je klas met de stand van elk onderdeel. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"learning_paths": [
{
"name": "Leerpad Software Developer",
"qualification": "Software Developer",
"completed": 5,
"total": 9,
"items": [
{
"name": "Git basis",
"type": "course",
"status": "afgerond"
}
]
}
]
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/learning-paths"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/learning-paths", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/learning-paths', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/events
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| days | array | Evenementdagen waarop je aanwezig was. | |
| days[].event | string | Naam van het evenement. | Techdag 2026 |
| days[].date | date | De datum. | 2026-04-14 |
| days[].checked_in_at | datetime|null | Wanneer je binnenkwam, als dat is geregistreerd. | 2026-04-14T08:52:00+02:00 |
| sessions | array | Workshops waarbij je aanwezig was. | |
| sessions[].event | string | Naam van het evenement. | Techdag 2026 |
| sessions[].workshop | string | Titel van de workshop. | Testen met Pest |
| sessions[].description | string|null | Omschrijving van de workshop. | Schrijf je eerste tests. |
| sessions[].date | date | De datum. | 2026-04-14 |
| sessions[].start_time | time | Begintijd (UU:MM). | 10:00 |
| sessions[].end_time | time | Eindtijd (UU:MM). | 11:30 |
| speaker_sessions | array | Workshops die je zelf gaf. | |
| speaker_sessions[].event | string | Naam van het evenement. | Techdag 2026 |
| speaker_sessions[].workshop | string | Titel van de workshop. | Testen met Pest |
| speaker_sessions[].description | string|null | Omschrijving van de workshop. | Schrijf je eerste tests. |
| speaker_sessions[].date | date | De datum. | 2026-04-14 |
| speaker_sessions[].start_time | time | Begintijd (UU:MM). | 10:00 |
| speaker_sessions[].end_time | time | Eindtijd (UU:MM). | 11:30 |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Evenementen en workshops die je bijwoonde of zelf gaf. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"days": [
{
"event": "Techdag 2026",
"date": "2026-04-14",
"checked_in_at": "2026-04-14T08:52:00+02:00"
}
],
"sessions": [
{
"event": "Techdag 2026",
"workshop": "Testen met Pest",
"description": "Schrijf je eerste tests.",
"date": "2026-04-14",
"start_time": "10:00",
"end_time": "11:30"
}
],
"speaker_sessions": [
{
"event": "Techdag 2026",
"workshop": "Testen met Pest",
"description": "Schrijf je eerste tests.",
"date": "2026-04-14",
"start_time": "10:00",
"end_time": "11:30"
}
]
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/events"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/events", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/events', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
Links naar goedgekeurd werk en je gekoppelde git-repo's. Alleen gevuld als "Werklinks delen" aan staat.
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/work
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| shared | boolean | true als "Werklinks delen" aan staat; anders false en zijn de lijsten leeg. | true |
| deliverables | array | Goedgekeurd opgeleverd werk met een link, nieuwste eerst. | |
| deliverables[].type | string | course_sprint, course_story of project_task. | course_sprint |
| deliverables[].title | string | Titel van de sprint, story of taak. | Sprint 2: REST API |
| deliverables[].context | string|null | De course of het project waar het bij hoort. | Laravel Advanced |
| deliverables[].url | url | Link naar het opgeleverde werk, bijv. een branch. | https://github.com/sanne/api/tree/sprint-2 |
| deliverables[].delivered_at | datetime|null | Wanneer het werk werd goedgekeurd of afgerond. | 2026-03-20T16:00:00+01:00 |
| repositories | array | De git-repo's die je aan je inschrijvingen koppelde. | |
| repositories[].label | string|null | De naam die je de repo gaf. | Mijn API |
| repositories[].provider | string | Waar de repo staat, bijv. github of gitlab. | github |
| repositories[].url | url | Link naar de repo. | https://github.com/sanne/api |
| repositories[].branch | string|null | De gevolgde branch. | main |
| repositories[].commits | integer | Aantal commits dat de LMS heeft ontvangen. | 87 |
| repositories[].last_commit_at | datetime|null | Moment van de laatste commit. | 2026-03-21T11:12:00+01:00 |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Je opgeleverde werk en repo's, of "shared": false zonder de instelling. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 403 | Het token mag dit onderdeel niet lezen. Maak een token met dat onderdeel. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"shared": true,
"deliverables": [
{
"type": "course_sprint",
"title": "Sprint 2: REST API",
"context": "Laravel Advanced",
"url": "https://github.com/sanne/api/tree/sprint-2",
"delivered_at": "2026-03-20T16:00:00+01:00"
}
],
"repositories": [
{
"label": "Mijn API",
"provider": "github",
"url": "https://github.com/sanne/api",
"branch": "main",
"commits": 87,
"last_commit_at": "2026-03-21T11:12:00+01:00"
}
]
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/work"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/work", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/work', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
Wat je recent hebt bereikt, nieuwste eerst, over alle onderdelen die het token mag lezen. Met since haal je alleen wat nieuw is.
https://lms.187-124-29-171.sslip.io/api/v1/portfolio/timeline
Query-parameters
| Veld | Type | Voorbeeld | Betekenis |
|---|---|---|---|
since |
datetime | 2026-03-01T00:00:00+01:00 | Alleen items na dit moment. |
limit |
integer | 20 | Maximaal aantal items, 1 t/m 100 (standaard 20). |
Velden in de response
| Veld | Type | Betekenis | Voorbeeld |
|---|---|---|---|
| items | array | Wat je hebt bereikt, nieuwste eerst. | |
| items[].type | string | badge_earned, outcome_achieved, course_completed, project_completed, workshop_attended, workshop_given of work_delivered. | badge_earned |
| items[].title | string | Wat er bereikt is: de badge, leeruitkomst, course, workshop of het werk. | Verhalenverteller |
| items[].detail | string|null | Extra informatie, bijv. de tier van een badge of het evenement van een workshop. Kan null zijn. | silver |
| items[].at | datetime | Wanneer het gebeurde. | 2026-03-02T14:05:00+01:00 |
| has_more | boolean | true als er meer items zijn dan limit. | false |
Mogelijke responses
| Status | Betekenis |
|---|---|
| 200 | Wat je recent hebt bereikt, nieuwste eerst. |
| 401 | Geen token, een onbekend token, of een token dat verlopen of ingetrokken is. |
| 304 | Geen fout: niets veranderd sinds je ETag (If-None-Match). Gebruik je opgeslagen antwoord. |
Voorbeeld met alle velden
{
"data": {
"items": [
{
"type": "badge_earned",
"title": "Verhalenverteller",
"detail": "silver",
"at": "2026-03-02T14:05:00+01:00"
}
],
"has_more": false
},
"meta": {
"generated_at": "2026-09-18T12:36:18+02:00",
"api_version": "v1"
}
}
Voorbeeldcode
curl -H "Authorization: Bearer lmsp_JOUW_TOKEN" \
-H "Accept: application/json" \
"https://lms.187-124-29-171.sslip.io/api/v1/portfolio/timeline"
// Alleen met een browsertoken: een servertoken hoort niet in je website.
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/timeline", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) {
const fout = await response.json().catch(() => ({}));
throw new Error(fout.message ?? `Portfolio API: ${response.status}`);
}
const { data } = await response.json();
console.log(data);
<?php
// Met een servertoken. Zet het token in een omgevingsvariabele, niet in je code.
$token = getenv('PORTFOLIO_TOKEN') ?: 'lmsp_JOUW_TOKEN';
$context = stream_context_create(['http' => [
'header' => "Authorization: Bearer $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
]]);
$body = file_get_contents('https://lms.187-124-29-171.sslip.io/api/v1/portfolio/timeline', false, $context);
$antwoord = json_decode($body, true);
if (isset($antwoord['error'])) {
exit('Portfolio API: '.$antwoord['message']);
}
$data = $antwoord['data'];
<div id="portfolio-timeline">Laden…</div>
<script>
(async () => {
const doel = document.getElementById("portfolio-timeline");
const response = await fetch("https://lms.187-124-29-171.sslip.io/api/v1/portfolio/timeline", {
headers: { Authorization: "Bearer lmsp_JOUW_TOKEN", Accept: "application/json" },
});
if (!response.ok) { doel.textContent = "Portfolio niet beschikbaar."; return; }
const { data } = await response.json();
doel.textContent = "";
const lijst = document.createElement("ul");
for (const item of data.items) {
const li = document.createElement("li");
li.textContent = `${new Date(item.at).toLocaleDateString()} — ${item.title}`;
lijst.append(li);
}
doel.append(lijst);
})();
</script>
Portfolio API
Heb je een account? Log in en maak je eigen token, met live voorbeelden van je eigen gegevens.