Référence API Edu-V
Documentation d'intégration pour l'interopérabilité éducative néerlandaise
https://api.samiolearning.com/v1/eduvAperçu
Samio participe à Edu-V (version NR26) comme tableau de bord de progression pour les enseignants. Samio est afnemer (consommateur) des données d'administration scolaire — onderwijsinrichting, onderwijsdeelnemers, onderwijsmedewerkers et groupes — issues des SIS néerlandais tels que ParnasSys et ESIS, et verzender (émetteur) des résultats produits dans l'app élève gratuite de Samio. Samio n'est pas un portail : il ne reçoit ni droits d'accès, ni catalogue, ni résultats.
Rôles de Samio
| Referentiecomponent | Gegevensdienst | Direction | Release |
|---|---|---|---|
| Dienst met SSO | Identiteitsverklaring | Récepteur (ontvanger) | NR26 |
| Dashboard voortgang | Onderwijsinrichting | Consommateur (afnemer) | NR26 |
| Dashboard voortgang | Onderwijsdeelnemer | Consommateur (afnemer) | NR26 |
| Dashboard voortgang | Onderwijsmedewerkers | Consommateur (afnemer) | NR26 |
| Dashboard voortgang | Onderwijsdeelname (groepen) | Consommateur (afnemer) | NR26 |
| Dashboard voortgang | Leerresultaten | Émetteur (verzender) | NR26 |
Inbound — koppelvlakken Samio exposes
/v1/eduv/
├── .well-known/ # OAuth metadata + participation statement
├── oauth/ # Token endpoint + JWKS
├── consent/ # Consent lifecycle (supporting)
├── dpa/ # DPA verification (supporting)
└── notifications/ # Roster-change notifications (we subscribe)
Outbound — koppelvlakken Samio calls at the partner
├── GET {sis}/education|students|employees|groups # afnemer
└── POST {receiver}/results # verzenderAuthentification
Tous les endpoints nécessitent un token Bearer obtenu via OAuth 2.0 avec le profil NL GOV (authentification client private_key_jwt).
Endpoint de token
/v1/eduv/oauth/token| Paramètre | Type | Description | |
|---|---|---|---|
grant_type | string | Requis | Must be "client_credentials" |
client_assertion_type | string | Requis | urn:ietf:params:oauth:client-assertion-type:jwt-bearer |
client_assertion | string | Requis | Signed JWT (RS256, PKIoverheid key) |
scope | string | Optionnel | Space-separated scopes |
curl -X POST https://api.samiolearning.com/v1/eduv/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-d "client_assertion=eyJhbGciOiJSUzI1NiIs..." \
-d "scope=eduv.student.read eduv.association"{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "eduv.student.read eduv.association"
}Endpoint JWKS
/v1/eduv/oauth/.well-known/jwks.jsonToken Properties
Codes d'erreur
{
"error": "Human-readable error message",
"error_code": "1001",
"error_description": "Optional additional details"
}| Code | Statut HTTP | Message |
|---|---|---|
0021 | 401 | Not authorized for this scope |
0011 | 400 | Schema validation failed |
0012 | 400 | schemaVersion not supported |
0022 | 403 | Consent required for this data service |
0023 | 400 | Unknown education organisation |
0099 | 400 | Other rejection (reason in statusMessage) |
0099 | 500 | Internal server error |
Pagination
Les endpoints paginés acceptent les paramètres start et limit.
| Paramètre | Type | Par défaut | Max |
|---|---|---|---|
start | integer | 0 | — |
limit | integer | 20 | 100 |
{
"data": [ ... ],
"total": 142,
"start": 0,
"limit": 20,
"hasMore": true
}Consentement
/v1/eduv/consentList all consent records. Filter by schoolId or status.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Optionnel | Filter by school |
status | string | Optionnel | Filter: requested, granted, revoked, denied |
Réponse
{
"data": [{
"_id": "665a1b2c3d4e5f6789012345",
"schoolId": { "_id": "64a...", "name": "Basisschool De Regenboog" },
"partnerId": { "_id": "665b...", "name": "ParnasSys" },
"dataService": "eduv.student.read",
"status": "granted",
"grantedAt": "2026-03-15T10:30:00.000Z"
}]
}/v1/eduv/consent/:idGet a single consent record by ID.
/v1/eduv/consent/requestRequest consent from a school for a data service.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Requis | Target school's ObjectId |
dataService | string | Requis | Edu-V scope (e.g. eduv.student.read) |
dpaReference | string | Optionnel | Reference to DPA agreement |
Réponse
{
"message": "Consent requested",
"consent": {
"_id": "665a1b2c...",
"status": "requested",
"initiatedBy": "partner"
}
}/v1/eduv/consent/:id/confirmConfirm (grant) a consent request.
| Paramètre | Type | Description | |
|---|---|---|---|
expiresAt | ISO date | Optionnel | When the consent expires |
/v1/eduv/consent/:id/revokeRevoke a previously granted consent.
/v1/eduv/consent/check/:schoolId/:dataServiceQuick boolean check if consent is granted.
Réponse
{ "granted": true }DPA
/v1/eduv/dpa/agreementsGet DPA agreement status for a school.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolBrinCode | string | Requis | School's BRIN code |
/v1/eduv/dpa/verifyVerify a Data Processing Agreement (verwerkersovereenkomst).
| Paramètre | Type | Description | |
|---|---|---|---|
schoolBrinCode | string | Requis | School's BRIN code |
dpaReference | string | Optionnel | Agreement reference number |
agreementDate | ISO date | Optionnel | Date of agreement |
Réponse
{
"verified": true,
"partner": { "oin": "0000000123...", "name": "ParnasSys" },
"schoolBrinCode": "01AB",
"verificationType": "local"
}Éducation
/v1/eduv/education/organisationsList schools with Edu-V enabled.
| Paramètre | Type | Description | |
|---|---|---|---|
brinCode | string | Optionnel | Filter by BRIN code |
start | integer | Optionnel | Pagination offset |
limit | integer | Optionnel | Page size (max 100) |
/v1/eduv/education/organisations/:idGet a specific organisation by ID.
/v1/eduv/education/study-offeringsList study offerings (grades). Maps to Groep 1-8.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Requis | School ObjectId |
/v1/eduv/education/subject-offeringsList subject offerings. Maps Samio modules to Edu-V subjects.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Requis | School ObjectId |
Étudiants
/v1/eduv/studentsList students for a school. Consent required.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Requis | School ObjectId |
classGroupId | string | Optionnel | Filter by class group |
eckId | string | Optionnel | Filter by ECK iD |
start | integer | Optionnel | Pagination offset |
limit | integer | Optionnel | Page size (max 100) |
/v1/eduv/students/:eckIdGet a single student by ECK iD.
Employés
/v1/eduv/employeesList employees for a school. Consent required.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Requis | School ObjectId |
role | string | Optionnel | Filter: owner, admin, teacher |
start | integer | Optionnel | Pagination offset |
limit | integer | Optionnel | Page size (max 100) |
/v1/eduv/employees/:idGet a single employee by ID.
Association
/v1/eduv/association/groupsList class groups for a school.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Requis | School ObjectId |
/v1/eduv/association/groups/:groupId/studentsList students in a specific class group.
/v1/eduv/association/enrollmentsList enrollments linking students to studies and groups.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Requis | School ObjectId |
eckId | string | Optionnel | Filter by student ECK iD |
Résultats
/v1/eduv/resultsGet learning results for students.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Optionnel | Filter by school |
eckId | string | Optionnel | Filter by ECK iD |
since | ISO date | Optionnel | Results after this date |
exerciseType | string | Optionnel | math, reading, grammar, clock, science, physics |
/v1/eduv/results/summaryAggregated per-student learning summary.
| Paramètre | Type | Description | |
|---|---|---|---|
schoolId | string | Optionnel | Filter by school |
eckId | string | Optionnel | Filter by student |
since | ISO date | Optionnel | Default: 30 days ago |
Notifications
/v1/eduv/notifications/subscribeSubscribe to change notifications (webhooks).
| Paramètre | Type | Description | |
|---|---|---|---|
callbackUrl | string | Requis | URL to receive webhooks |
notificationTypes | string[] | Requis | Event types to subscribe to |
schoolId | string | Optionnel | Scope to specific school |
/v1/eduv/notifications/subscribe/:idUnsubscribe from notifications.
/v1/eduv/notifications/subscriptionsList active subscriptions.
/v1/eduv/notifications/receiveReceive inbound notification from a partner (webhook).
| Paramètre | Type | Description | |
|---|---|---|---|
notificationType | string | Requis | Event type |
objectType | string | Requis | student, employee, group, etc. |
objectId | string | Optionnel | Object identifier |
school | object | Optionnel | { brinCode } |
url | string | Optionnel | URL to fetch the updated object |
isDeleteNotification | boolean | Optionnel | Whether the object was deleted |
Scopes
Scopes OAuth 2.0 disponibles pour l'intégration Edu-V.
| Scope | APIs | Description |
|---|---|---|
eduv.consent | Consent | Manage consent lifecycle (supporting) |
eduv.dpa | DPA | Verify Data Processing Agreements (supporting) |
eduv.notifications | Notifications | Subscribe to roster-change notifications |
eduv.education | Education | Onderwijsinrichting — organisations, studies, subjects (afnemer) |
eduv.student.basic | Students | Onderwijsdeelnemer — identity and name (afnemer) |
eduv.employee.basic | Employees | Onderwijsmedewerkers — identity and name (afnemer) |
eduv.association | Association | Onderwijsdeelname — groups and enrollments (afnemer) |
eduv.result | Results | Leerresultaten — results sent by Samio (verzender) |
Modèles de données
| Champ | Type | Description |
|---|---|---|
schoolId | ObjectId → School | Target school |
partnerId | ObjectId → EduVPartner | Partner |
dataService | string | Edu-V scope |
status | enum | requested | granted | revoked | denied |
initiatedBy | enum | school | partner |
grantedAt | Date | When consent was granted |
expiresAt | Date | Consent expiry |
| Champ | Type | Description |
|---|---|---|
entitlementId | string | Unique ID |
productId | string | Product reference |
schoolId | ObjectId → School | School |
eckId | string | Student ECK iD |
entitlementType | enum | Delivery type |
status | enum | created → entitled → licensed → activated / cancelled / blocked / expired |
licensePeriod | object | { start, end } |
activationCode | string | If activated by code |
| Champ | Type | Description |
|---|---|---|
eduv.brinCode | string | BRIN school identifier |
eduv.oaId | string | Onderwijsaanbieder ID |
eduv.oinNumber | string | OIN |
eduv.boardId | string | Bestuur ID |
eduv.bestuursnummer | string | Board number |
eduv.enabled | boolean | Whether Edu-V is active |
| Champ | Type | Description |
|---|---|---|
eduv.eckId | string | ECK iD — primary Dutch student identifier |
eduv.givenName | string | First name |
eduv.familyName | string | Last name |
eduv.familyNamePrefix | string | Tussenvoegsel ("van", "de") |
eduv.externalIds | array | [{ type, value }] |
Démarrage rapide
Flux d'intégration complet de l'enregistrement du partenaire à la lecture des résultats.
Partner registers with Samio → EduVPartner record created
Partner obtains token → POST /v1/eduv/oauth/token
Partner requests consent → POST /v1/eduv/consent/request
School grants consent → POST /v1/eduv/consent/:id/confirm
Partner reads students → GET /v1/eduv/students?schoolId=...
Partner places order → POST /v1/eduv/orders
Samio creates entitlement → POST /v1/eduv/entitlement (internal)
Partner activates → POST /v1/eduv/activation-codes/request
Student uses Samio → (normal app usage)
Partner reads usage → GET /v1/eduv/usage/entitlements
Partner reads results → GET /v1/eduv/results?eckId=...
Outils pour agents (WebMCP)
Outils que les agents IA du navigateur peuvent appeler sur samiolearning.com : quatre outils en lecture seule sur chaque page publique (prix par pays, recherche de pages, fiches produit, liens d'apps) et, pour un enseignant connecté au tableau de bord, les douze mêmes outils enseignants que le serveur MCP — exécutés avec la session de l'enseignant.
WebMCP is the W3C-incubated standard (Google + Microsoft) that lets a web page register tools an AI agent running in the browser can call — the page-side counterpart of an MCP server. Every public page on samiolearning.com registers the four tools below once the page has loaded, in browsers that exposenavigator.modelContext. Other browsers pay nothing: the module is loaded at idle and only when the API exists. The tools are deliberately read-only. Nothing submits a form, starts a trial or touches payment; an agent that wants to register a school must hand the person the registration page.
samio_get_pricing_for_countryGET /v1/schools/pricing/resolve (public) · static tier table as fallbackSamio for Schools price for a country: equity-pricing tier (World Bank income group), discount, and the monthly USD price of the Homeschool, Classroom and School plans.
| Input | Type | Required | Description |
|---|---|---|---|
country | string | yes | ISO 3166-1 alpha-2 code, e.g. NL, MX, IN |
Returns
{
"country": "MX",
"pricingTier": "tier_b",
"pricingTierLabel": "Equity — Upper-middle income",
"discountPercent": 50,
"currency": "USD",
"billing": "monthly via PayPal; every plan starts with a 14-day free trial",
"plans": [
{ "id": "homeschool", "priceUsdPerMonth": 2.49, "standardPriceUsdPerMonth": 4.99, "limits": { "students": 10, "teachers": 1, "classes": 1 } },
{ "id": "classroom", "priceUsdPerMonth": 9.0, "standardPriceUsdPerMonth": 19.0, "limits": { "students": 60, "teachers": 1, "classes": 4 } },
{ "id": "school", "priceUsdPerMonth": 79.0, "standardPriceUsdPerMonth": 159.0, "limits": { "students": 1500, "teachers": "unlimited", "classes": "unlimited" } }
],
"resolvedBy": "api",
"explanation": "https://samiolearning.com/schools/equity-pricing",
"register": "https://samiolearning.com/schools/register"
}samio_find_pagein-page index of public routesFind the samiolearning.com page that answers a question. Returns up to 5 URLs with titles and descriptions, localized to the requested language where a translation exists.
| Input | Type | Required | Description |
|---|---|---|---|
query | string | yes | Free-text question or keywords |
language | string | no | en (default), es, nl, de, fr, it, pt |
audience | string | no | parents | schools | developers |
Returns
{
"query": "how do i connect google classroom",
"results": [
{ "title": "Plug Samio into your LMS in 10 minutes", "url": "https://samiolearning.com/schools/integrations", "audience": "schools", "relevance": 7, "description": "…" }
]
}samio_get_product_factsGET /facts.jsonCanonical facts about Samio Learning, Samio for Schools or Askimo (features, platforms, compliance, pricing, comparisons) plus entity disambiguation for the names "Samio" and "Askimo".
| Input | Type | Required | Description |
|---|---|---|---|
product | string | no | samio | schools | askimo — omit for all three |
Returns
{ "products": [ { "name": "Askimo", "category": "Free educational Wikipedia app for kids ages 4–12", "…": "…" } ], "entityDisambiguation": { "samio": "…", "askimo": "…" }, "source": "https://samiolearning.com/facts.json" }samio_get_app_linksstaticOfficial App Store, Google Play and web links for the Askimo or Samio Learning apps, with price and age range.
| Input | Type | Required | Description |
|---|---|---|---|
app | string | no | askimo (default) | samio |
Returns
{ "app": "Askimo", "appStore": "https://apps.apple.com/app/askimo/id6760259046", "googlePlay": "…", "web": "https://samiolearning.com/askimo", "price": "Free, no ads, no accounts", "ages": "4–12", "languages": ["en","es","nl","de","fr","it","pt"] }Dashboard tools for signed-in teachers
On /schools/dashboard/*, and only while a teacher is signed in, the page additionally registers the twelve teacher tools of the remote MCP server — same names, same input schemas — so a browser agent (Chrome, Gemini in Chrome, Edge Copilot) can do the same work without OAuth: every call runs through the dashboard's own endpoints with the teacher's session, under the same plan limits and consent checks as a click in the UI. The set is withdrawn on logout or when leaving the dashboard; public pages never see it. Generation tools return the new id with status: "generating" and the matching get_ tool polls, exactly as on the MCP server. The schemas are published in the manifest under dashboardTools.
list_my_classeslist_material_typesgenerate_teaching_materialget_teaching_materiallist_teaching_materialscreate_topic_planget_topic_planget_topic_plan_weeklist_topic_plansgenerate_challengesearch_knowledge_factsget_class_curriculum_progressDiscovery and testing
- Static manifest of the same tools: /agent-tools.json. Plain-text brief for LLMs: /llms.txt; machine-readable facts: /facts.json.
- In a WebMCP-capable browser (Chrome early preview; enable the WebMCP flag in
chrome://flags), open any samiolearning.com page and the tools appear in the browser's agent tool list after load. - Non-browser agents (an MCP server, a script) do not need WebMCP: the two network-backed tools wrap plain HTTP endpoints you can call directly, shown below.
# Country → pricing tier (public, no auth)
GET https://api.samiolearning.com/v1/schools/pricing/resolve?country=MX
→ { "success": true, "data": { "country": "MX", "pricingTier": "tier_b", "pricingTierLabel": "Equity — Upper-middle income", "pricingDiscountPercent": 50, "currency": "USD" } }
# Canonical product facts
GET https://samiolearning.com/facts.json
# Tool manifest
GET https://samiolearning.com/agent-tools.jsonStatus: the WebMCP API surface is still changing. The page registers with whichever ofregisterTool() orprovideContext() the browser exposes and silently skips registration otherwise. Tool names and schemas are stable; report problems to info@samiolearning.com.
Serveur MCP distant (Samio pour les écoles)
Un serveur MCP distant auquel tout assistant IA peut se connecter — Claude, ChatGPT, Gemini, Claude Code ou votre propre agent. Quatre outils en lecture seule sur les programmes officiels du primaire dans plus de 60 pays et les tarifs de Samio pour les écoles fonctionnent sans connexion ; treize outils enseignants (supports, plans thématiques, défis, faits vérifiés, couverture par classe) s'activent lorsqu'un enseignant relie son compte Samio pour les écoles via OAuth 2.1. Aucune donnée d'élève, sur aucun outil.
https://mcp.samiolearning.com/mcphttps://mcp.samiolearning.com/healthThe server speaks the Model Context Protocol over Streamable HTTP and is stateless: every request carries what it needs, so it works with any current MCP client. The server lists seventeen tools. The four below need no account: they read the same curriculum spine that grounds every exercise in Samio (60+ countries, seven interface languages) and the public pricing resolver. The other fourteen are teacher tools: they become usable once a teacher connects their Samio for Schools account with OAuth 2.1 and act only inside that teacher's own classes. No tool on either tier returns a student name, score or result, and the gateway behind the server stores only hashed inputs and salted IP hashes for abuse control.
list_countries_grades_subjectsList curriculum coverageGET /v1/agent/curriculum/countriesWhich countries, grades (Samio stages 1–7 with age ranges) and subjects Samio holds official national-curriculum data for. Call this first when unsure of codes.
| Input | Type | Required | Description |
|---|---|---|---|
country | string | no | Optional ISO country code (e.g. NL, ES, US) to return one country only |
Returns
{
"countries": [
{ "country": "NL", "language": "nl",
"grades": [ { "gradeLevel": 1, "gradeName": "Groep 3", "ageRange": { "min": 6, "max": 7 } }, … ],
"subjects": ["math", "reading", "grammar", "science", "physics", "computing"] },
…
]
}search_curriculum_standardsSearch curriculum standardsGET /v1/agent/curriculum/standardsOfficial national-curriculum units, skills, competencies and assessment criteria for a country + subject + grade (or age), optionally ranked by a topic query. Returns standardId/unitId for follow-up calls.
| Input | Type | Required | Description |
|---|---|---|---|
country | string | yes | ISO country code, e.g. NL, ES, DE, FR, IT, PT, GB, US |
subject | enum | yes | math | reading | grammar | clock | science | physics | history | computing |
grade | integer 1–7 | no | Samio stage (1 ≈ age 4–5 … 7 ≈ age 11–13). Give grade OR age |
age | integer 3–18 | no | Child's age; the matching grade is chosen for you |
language | string | no | Language of the curriculum text (defaults to the country's) |
query | string ≤120 | no | Free-text topic to rank units by, e.g. 'fractions', 'breuken' |
limit | integer 1–50 | no | Max units to return (default 20) |
Returns
{
"standard": { "standardId": "…", "country": "NL", "language": "nl", "gradeLevel": 1, "gradeName": "Groep 3",
"ageRange": { "min": 6, "max": 7 }, "subject": "math",
"officialName": "Rekenen/Wiskunde — Kerndoelen 23–33 (SLO)",
"source": { "name": "SLO Kerndoelen Primair Onderwijs", "url": "https://www.slo.nl/…" },
"assessmentFocus": "Automatiseren van optellen en aftrekken tot 10" },
"units": [ { "unitId": "NL:math:g1:u:rrw96f", "name": "Optellen en aftrekken tot 20", "description": "…",
"skills": [ { "skillId": "NL:math:g1:s:lm8mtk", "name": "Sommen tot 10" } ], "matchScore": 0 } ],
"competencies": [ { "competencyId": "…", "name": "Getalbegrip en getalrelaties" } ],
"criteria": [ { "criterionId": "…", "text": "Leerlingen kunnen getallen tot 20 lezen, schrijven en ordenen", "area": "comprehension" } ]
}get_curriculum_unitGet one curriculum unitGET /v1/agent/curriculum/units/:unitId?standardId=Full detail of one curriculum unit (skills) plus the standard's competencies and assessment criteria.
| Input | Type | Required | Description |
|---|---|---|---|
standardId | string | yes | standardId from search_curriculum_standards |
unitId | string | yes | unitId from search_curriculum_standards |
Returns
{ "standard": { … }, "unit": { "unitId": "…", "name": "…", "description": "…", "skills": [ … ] }, "competencies": [ … ], "criteria": [ … ] }get_pricing_for_countryGet Samio for Schools pricingGET /v1/agent/pricing?country=XX (same JSON as /v1/schools/pricing/resolve)Plan prices, purchasing-power tier and limits for a school in a given country.
| Input | Type | Required | Description |
|---|---|---|---|
country | string | yes | Two-letter ISO country code of the school, e.g. MX, NL, US |
Returns
{ "success": true, "data": { "country": "MX", "countrySource": "query", "pricingTier": "tier_b", "pricingTierLabel": "Equity — Upper-middle income", "pricingDiscountPercent": 50, "currency": "USD" } }Connect it
- Claude (claude.ai, Desktop, mobile, Cowork): Customize → Connectors → Add custom connector → paste the endpoint, authentication “none”. On Team and Enterprise plans an Owner adds it once for the organisation.
- ChatGPT: Settings → Connectors → Create (developer mode) → paste the endpoint, no authentication.
- Claude Code, Gemini CLI, the Claude API and any other MCP client: snippets below. Listings in the Claude Connectors Directory and the ChatGPT plugin directory are being prepared; until they are live the custom-connector route above is the way in.
- Teachers: the non-technical walkthrough with example prompts is the announcement article Samio for Schools now works inside Claude and ChatGPT.
# Claude Code
claude mcp add --transport http samio https://mcp.samiolearning.com/mcp
# Gemini CLI — ~/.gemini/settings.json
{ "mcpServers": { "samio": { "httpUrl": "https://mcp.samiolearning.com/mcp", "oauth": { "enabled": true } } } }
# Any MCP client / plain HTTP: list the tools (JSON-RPC over Streamable HTTP)
curl -s https://mcp.samiolearning.com/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'# Claude API — let the model call the server directly (MCP connector, beta)
POST https://api.anthropic.com/v1/messages
anthropic-beta: mcp-client-2025-11-20
{
"model": "claude-opus-5",
"max_tokens": 1024,
"mcp_servers": [ { "type": "url", "url": "https://mcp.samiolearning.com/mcp", "name": "samio" } ],
"tools": [ { "type": "mcp_toolset", "mcp_server_name": "samio" } ],
"messages": [ { "role": "user", "content": "What does the Dutch curriculum expect in maths for a 7-year-old?" } ]
}Outils enseignants (OAuth 2.1)
Teachers can connect their Samio for Schools account to Claude, ChatGPT or any MCP client and let the assistant create teaching materials, topic plans and challenges in their own classes, read Samio's verified knowledge facts and see class-level curriculum coverage. The connection is an ordinary OAuth 2.1 grant: the teacher signs in with the same credentials as the dashboard, sees exactly which permissions the app asks for, and can revoke the app at any time under Settings → Connected apps. Generation runs on the same queues, plan limits and consent checks as the dashboard, and every generated artefact carries a provenance line naming the app.
How a teacher connects
- 1Connect. Add https://mcp.samiolearning.com/mcp in Claude (Customize → Connectors) or ChatGPT (Settings → Connectors). The curriculum tools work right away; the first teacher tool call answers 401 with the resource metadata, which makes the client start the login.
- 2Samio login. The browser opens samiolearning.com/oauth/consent. The teacher signs in with their school account — e-mail and password, Google, Microsoft or the school's SSO — exactly as on the dashboard.
- 3Approve permissions. The consent screen names the app and its publisher, the school and the signed-in teacher, and lists the requested scopes in the teacher's language, with the fixed line "This app never receives student names, scores or results." Approve or Deny.
- 4Tools appear. The client exchanges the code for a one-hour access token (plus a rotating 30-day refresh token) and the fourteen teacher tools are live. Revoking under Settings → Connected apps cuts them off within seconds.
Scopes
Space-separated in scope. Every consent grants at least openid email; a request without scopes is treated as openid email classes:read. The titles below are the ones the teacher sees on the consent screen (in all seven interface languages). students:read is reserved and never issued.
| Scope | What the teacher reads | Tools |
|---|---|---|
openid email | Know who you are — Name and e-mail of the connected teacher (OIDC id_token + userinfo). Always granted. | get_my_account |
classes:read | See your classes — Class names and student counts, never student names. | list_my_classes |
materials:write | Create teaching materials in your account — Mind maps, presentations, didactic materials, study cards. | list_material_types, generate_teaching_material, get_teaching_material, list_teaching_materials |
plans:write | Create topic plans and lessons for your classes — Curriculum-grounded plans on the school's academic calendar. | create_topic_plan, get_topic_plan, get_topic_plan_week, list_topic_plans |
challenges:write | Create challenges — Drafts unless the teacher asks to publish. | generate_challenge |
knowledge:read | Read Samio's verified knowledge facts — Sourced facts that ground generated content. | search_knowledge_facts |
analytics:read | See class-level curriculum coverage — Never per-student results. | get_class_curriculum_progress |
The fourteen teacher tools
Every Tier B call sends Authorization: Bearer <access token> to the gateway; a missing or expired token answers 401 with WWW-Authenticate: Bearer resource_metadata=…, a missing scope 403 AGENT_SCOPE_MISSING (with the required scope), a revoked connection 403 AGENT_GRANT_REVOKED. Generation tools are asynchronous: they return the new id with status: "generating" and the matching get_ tool polls (typically 1–5 minutes). Per-school generation quota: 50 calls per day (429 AGENT_SCHOOL_QUOTA_EXCEEDED + Retry-After); a subscription limit answers 402 PLAN_LIMIT_REACHED.
get_my_accountWho am IThe connected teacher, their school and the scopes this connection holds. Call it first to confirm which account the assistant is acting for.
| Input | Type | Required | Description |
|---|---|---|---|
| No input. | |||
Returns
{ "user": { "id": "…", "name": "Ana Ruiz", "email": "ana@school.example", "role": "teacher" },
"school": { "id": "…", "name": "Colegio Sol", "country": "ES", "language": "es", "plan": "school" },
"scopes": ["openid", "email", "classes:read", "materials:write"], "app": { "name": "Claude" } }list_my_classesList my classesYour classes with id, name, grade, subjects, language and student count — never student names. The classId feeds every other tool.
| Input | Type | Required | Description |
|---|---|---|---|
| No input. | |||
Returns
{ "classes": [ { "classId": "…", "name": "3º A", "gradeLevel": 3, "gradeName": "3.º de Primaria", "subjects": ["math", "reading", …], "language": "es", "studentCount": 24, "academicCalendarId": "…" } ] }list_material_typesList material typesThe four material types Samio generates, with a description of each.
| Input | Type | Required | Description |
|---|---|---|---|
| No input. | |||
Returns
{ "types": [ { "type": "mindmap", "title": "Mind map", "description": "…" }, { "type": "presentation", … }, { "type": "didactic_material", … }, { "type": "study_cards", … } ] }generate_teaching_materialGenerate a teaching materialStart generating a material on a topic for a class (or a subject + grade). Same validation, plan limits and queue as the dashboard.
Asynchronous — poll get_teaching_material until status is "ready" (typically 1–5 minutes).
| Input | Type | Required | Description |
|---|---|---|---|
materialType | enum | yes | mindmap | presentation | didactic_material | study_cards |
topic | string 2–200 | yes | Topic of the material |
classId | string | no | Class to create it for — give classId OR subject + gradeLevel |
subject | enum | no | math | reading | grammar | clock | science | physics | history | computing |
gradeLevel | integer 1–7 | no | Samio stage when no classId is given |
language | string | no | Content language (defaults to the school's) |
unitId | string | no | Curriculum unit to ground on (from search_curriculum_standards) |
standardId | string | no | Standard the unit belongs to |
options | object | no | Type-specific: slideCount + theme, branches, cardCount, materialKind + includeAnswerKey, includeImages |
Returns
{ "materialId": "…", "status": "generating", "title": "Fractions — mind map", "materialType": "mindmap", "provenance": "Created with Samio's AI assistant via Claude" }get_teaching_materialGet a teaching materialStatus and, once ready, the stored content of one material (markdown or structured JSON, as the dashboard receives it).
| Input | Type | Required | Description |
|---|---|---|---|
materialId | string | yes | From generate_teaching_material or list_teaching_materials |
Returns
{ "materialId": "…", "status": "ready", "title": "…", "materialType": "presentation", "subject": "science", "classId": "…", "content": { … }, "provenance": "…", "dashboardUrl": "https://samiolearning.com/schools/dashboard/teaching-materials" }list_teaching_materialsList teaching materialsYour materials, newest first (max 50), optionally for one class.
| Input | Type | Required | Description |
|---|---|---|---|
classId | string | no | Only this class |
limit | integer 1–50 | no | Default 20 |
Returns
{ "materials": [ { "materialId": "…", "status": "ready", "title": "…", "materialType": "study_cards", "createdAt": "2026-09-21T09:12:00Z" } ] }create_topic_planCreate a topic planStart a curriculum-grounded plan for a class and subject on the school's active academic calendar. mode "auto" also generates lessons and challenges for every week.
Asynchronous — poll get_topic_plan; its progress block reports the phase and percentage (typically 1–5 minutes, longer for "auto").
| Input | Type | Required | Description |
|---|---|---|---|
classId | string | yes | Class |
subject | enum | yes | math | reading | grammar | clock | science | physics | history | computing |
mode | enum | no | plan (default) | auto |
weeks | integer 1–52 | no | Advisory — the academic calendar sets the real span |
classProfile | object | no | { studentCount, energyLevel: calm | balanced | energetic, traits[], notes } |
language | string | no | Content language |
Returns
{ "planId": "…", "status": "generating", "mode": "plan", "title": "Matemáticas 3º A 2026–2027", "provenance": "…" }get_topic_planGet a topic planStatus, progress and the week list of one plan.
| Input | Type | Required | Description |
|---|---|---|---|
planId | string | yes | From create_topic_plan or list_topic_plans |
Returns
{ "planId": "…", "status": "active", "mode": "plan", "title": "…", "subject": "math", "classId": "…", "totalWeeks": 36,
"progress": { "phase": "weeks", "percent": 100, "message": "Done" },
"weeks": [ { "weekNumber": 1, "title": "Numbers to 1000", "topics": ["Place value", "Comparing"], "status": "pending", "hasLesson": true, "materialIds": [], "challengeIds": [] } ],
"dashboardUrl": "https://samiolearning.com/schools/dashboard/topic-planner?view=detail&planId=…" }get_topic_plan_weekGet one week of a planTopics, objectives, the lesson (when generated) and the materials and challenges linked to one week.
| Input | Type | Required | Description |
|---|---|---|---|
planId | string | yes | Plan |
weekNumber | integer ≥ 1 | yes | 1-based week |
Returns
{ "planId": "…", "weekNumber": 3, "title": "…", "topics": [ … ], "objectives": [ … ], "lesson": { … } | null,
"materials": [ { "materialId": "…", "title": "…", "materialType": "mindmap", "status": "ready" } ],
"challenges": [ { "challengeId": "…", "title": "…", "status": "draft" } ] }list_topic_plansList topic plansYour plans, optionally for one class.
| Input | Type | Required | Description |
|---|---|---|---|
classId | string | no | Only this class |
Returns
{ "plans": [ { "planId": "…", "status": "active", "title": "…", "subject": "reading", "classId": "…", "totalWeeks": 36, "createdAt": "…" } ] }generate_challengeGenerate a challengeCreate a quiz with generated exercises for a class and subject. Saved as a draft the teacher reviews in the dashboard unless publish is true (max 10 exercises).
Asynchronous: exercises are generated in the background; poll get_challenge.
| Input | Type | Required | Description |
|---|---|---|---|
classId | string | yes | Class |
subject | enum | yes | math | reading | grammar | clock | science | physics | history | computing |
count | integer 1–10 | no | Exercises (default 5) |
exerciseTypes | string[] | no | Exercise types of the subject, e.g. fractions, word_problems |
difficulty | string | no | easy | medium | hard |
unitId | string | no | Curriculum unit to target |
topic | string ≤200 | no | Free-text steer |
publish | boolean | no | Default false — drafts unless the teacher asks to publish |
Returns
{ "challengeId": "…", "status": "generating", "title": "Fractions — quiz", "exerciseCount": 0, "preview": [],
"publishOnReady": false, "dashboardUrl": "https://samiolearning.com/schools/dashboard/challenges?challenge=…", "provenance": "…" }get_challengeGet one challengeStatus and preview of a challenge created with generate_challenge: generating, draft, published or failed, with the generated questions once ready.
| Input | Type | Required | Description |
|---|---|---|---|
challengeId | string | yes | challengeId from generate_challenge |
Returns
{ "challengeId": "…", "status": "draft", "title": "Fractions — quiz", "subject": "math", "classId": "…", "exerciseCount": 5,
"preview": [ { "type": "fractions", "question": "Which fraction is larger: 2/3 or 3/5?" }, … ],
"dashboardUrl": "https://samiolearning.com/schools/dashboard/challenges?challenge=…" }search_knowledge_factsSearch knowledge factsSamio's verified, sourced facts on a topic — the same facts that ground generated lessons and materials (max 20).
| Input | Type | Required | Description |
|---|---|---|---|
subject | enum | yes | math | reading | grammar | clock | science | physics | history | computing |
topic | string 2–120 | yes | Topic, e.g. photosynthesis |
language | string | no | Defaults to the school's |
limit | integer 1–20 | no | Default 10 |
Returns
{ "facts": [ { "factId": "…", "statement": "Photosynthesis converts light energy into chemical energy.", "subject": "science", "topic": "photosynthesis", "language": "en", "sources": [ { "title": "…", "url": "…" } ], "verified": true } ] }get_class_curriculum_progressClass curriculum coverageWhich official curriculum units a class has mastered, is working on or has not started, for one subject. A class rollup — never per-student rows.
| Input | Type | Required | Description |
|---|---|---|---|
classId | string | yes | Class |
subject | enum | yes | math | reading | grammar | clock | science | physics | history | computing |
Returns
{ "classId": "…", "subject": "math", "standard": { "standardId": "…", "officialName": "…", "gradeName": "…" },
"coverage": { "percent": 42, "unitsMastered": 5, "unitsInProgress": 3, "unitsPending": 4 },
"units": [ { "unitId": "…", "name": "Fractions", "status": "in_progress", "percent": 61 } ] }Discovery
# Authorization server (RFC 8414; the same document is also served OIDC-shaped for ChatGPT)
GET https://api.samiolearning.com/oauth/.well-known/oauth-authorization-server
GET https://api.samiolearning.com/.well-known/oauth-authorization-server/oauth
GET https://api.samiolearning.com/oauth/.well-known/openid-configuration
GET https://api.samiolearning.com/oauth/jwks.json
# Protected resource (RFC 9728) — what the 401 from /mcp points at
GET https://mcp.samiolearning.com/.well-known/oauth-protected-resource
→ { "resource": "https://mcp.samiolearning.com/mcp",
"authorization_servers": ["https://api.samiolearning.com/oauth"],
"scopes_supported": ["openid", "email", "classes:read", "materials:write", "plans:write", "challenges:write", "knowledge:read", "analytics:read"],
"bearer_methods_supported": ["header"],
"resource_documentation": "https://samiolearning.com/developers#mcp" }Notes for developers building a client
- Client registration: prefer a Client ID Metadata Document — your
client_idis an https URL serving JSON withredirect_uris,client_name,client_uri,logo_uri(fetched with a 3 s timeout, cached one hour). Dynamic Client Registration (RFC 7591,POST /oauth/register) is also accepted. Redirect-URI hosts must be on the allow-list (claude.ai, chatgpt.com, chat.openai.com, platform.openai.com, localhost, 127.0.0.1);http://localhostandhttp://127.0.0.1match port-agnostically (RFC 8252 §7.3). Both mechanisms produce public clients (token_endpoint_auth_method: "none"). - PKCE S256 is mandatory;
response_type=codeonly. Passresource=https://mcp.samiolearning.com/mcp(RFC 8707) — it becomes the token's audience. - Tokens:
POST /oauth/tokenaccepts form-urlencoded and JSON; access tokens are RS256 JWTs valid one hour; refresh tokens live 30 days and rotate on every use — presenting an already-rotated token revokes the whole family.POST /oauth/revokealways answers 200. - No
client_credentialsgrant: every connection is a teacher consenting on the screen above. Service-to-service access to the curriculum tools uses the app key below instead. - Same schemas in the browser: signed-in dashboard pages register the twelve teacher tools (all but
get_my_account) through WebMCP with identical names and input schemas, executed with the teacher's session — see Agent tools (WebMCP).
Prefer plain REST? The agent gateway
Every tool is a thin wrapper over one endpoint of the Samio API’s agent gateway. Developers who would rather call HTTP than MCP can use the curriculum endpoints directly with an app key sent asX-Samio-Agent-Key(request one at info@samiolearning.com). The OpenAPI 3.1 description is public.
GET https://api.samiolearning.com/v1/agent/openapi.json # public, no key
GET https://api.samiolearning.com/v1/agent/curriculum/countries
GET https://api.samiolearning.com/v1/agent/curriculum/standards?country=NL&subject=math&age=7&query=optellen
GET https://api.samiolearning.com/v1/agent/curriculum/units/{unitId}?standardId={standardId}
GET https://api.samiolearning.com/v1/agent/pricing?country=MX
# errors are { "error": "...", "code": "..." }
# 400 INVALID_COUNTRY | INVALID_SUBJECT | INVALID_GRADE | INVALID_AGE | INVALID_LANGUAGE | INVALID_QUERY | INVALID_LIMIT
# 401 AGENT_KEY_INVALID · 403 AGENT_APP_DISABLED · 404 NO_CURRICULUM | UNIT_NOT_FOUND
# 429 AGENT_QUOTA_EXCEEDED (+ Retry-After) · 503 AGENT_ACCESS_DISABLED
# Teacher endpoints (Tier B) take BOTH the app key and the teacher's OAuth bearer:
GET https://api.samiolearning.com/v1/agent/me
GET https://api.samiolearning.com/v1/agent/classes
POST https://api.samiolearning.com/v1/agent/teaching-materials # 202 { materialId, status: "generating" }
GET https://api.samiolearning.com/v1/agent/teaching-materials/{materialId}
POST https://api.samiolearning.com/v1/agent/topic-plans # 202 { planId, status: "generating" }
GET https://api.samiolearning.com/v1/agent/topic-plans/{planId}
GET https://api.samiolearning.com/v1/agent/topic-plans/{planId}/weeks/{n}
POST https://api.samiolearning.com/v1/agent/challenges
GET https://api.samiolearning.com/v1/agent/knowledge/facts?subject=&topic=
GET https://api.samiolearning.com/v1/agent/classes/{classId}/curriculum-progress?subject=
# 401 AGENT_TOKEN_INVALID · 403 AGENT_SCOPE_MISSING | AGENT_GRANT_REVOKED · 402 PLAN_LIMIT_REACHED
# 404 CLASS_NOT_FOUND | PLAN_NOT_FOUND | MATERIAL_NOT_FOUND | WEEK_NOT_FOUND · 409 GENERATION_IN_PROGRESS
# 429 AGENT_SCHOOL_QUOTA_EXCEEDED (+ Retry-After)- Quotas: 600 calls per hour per app key and 120 per hour per client IP for developer apps; the MCP endpoint itself allows 120 requests per hour per IP. Curriculum responses carry
Cache-Control: public, max-age=3600. - Audit: one row per call with the route template, status, duration, a hash of the inputs and a salted hash of the IP; no free text, no personal data, purged after 180 days.
- The tool names and input schemas are identical on the MCP server and in the gateway’s OpenAPI file, so a client can switch between the two without remapping.
Problems, feature requests or a developer key: info@samiolearning.com. The gateway can be switched off centrally; when it is, every call answers 503 AGENT_ACCESS_DISABLED.
Samio Learning — Edu-V API v1.0.0
