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.

De voorbeelden hieronder tonen fictieve gegevens. Log in om precies te zien wat de API voor jou teruggeeft.
Basis-URL: 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>
Aan de slag

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.

Download portfolio.html
Fouten

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.
Overzicht

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'];
Profiel & badges

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
Niet in het antwoord: je e-mailadres, klas, mentor en coins. De weergavenaam, profielfoto en school volgen je instellingen onder "Wat deel ik?".

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
Leeruitkomsten

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
Niet in het antwoord: wie een leeruitkomst afvinkte, feedback en losse beoordelingen. "Aangetoond" staat er alleen als telling in. Ben je zelf begonnen zonder school (een cursist), dan volg je geen kwalificatiedossier en is "qualifications" een lege lijst.

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>
Voortgang

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
Niet in het antwoord: feedback van docenten, cijfers en afwijzingen.

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
Leerpaden hangen aan een klas. Ben je zelf begonnen zonder school (een cursist), dan heb je geen klas en is "learning_paths" een lege lijst — je courses en projecten staan wel gewoon bij /progress.

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'];
Evenementen

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
Niet in het antwoord: inschrijvingen waar je niet aanwezig was, en wie de aanwezigheid registreerde.

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'];
Opgeleverd werk

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
Alleen goedgekeurd werk met een http(s)-link. Nooit webhook-gegevens of e-mailadressen uit commits. Zonder "Werklinks delen" is "shared" false en zijn de lijsten leeg.

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'];
Tijdlijn

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
Alleen onderdelen die het token mag lezen, en niets in de toekomst.

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.