← Pyetje të shpeshta

Pyetjet e shpeshta janë shkruar në anglisht dhe holandisht; shfaqen në anglisht.

Pyetje të shpeshta

Profile server API

FAQ › Profile server › API · Nederlands

What is it?

The profile server (server/profile, axum) answers HTTP requests from four kinds of clients:

  1. The EUZOS PC (the kernel): a plain-text line protocol (key=value lines, first line a status word), with a signed profile.
  2. The EUZOS phone app: JSON under /api/app/v2/, with bearer tokens.
  3. Workplace servers (EUZOS Server): the line protocol under /api/workplace/v1/.
  4. Agents and admins: JSON agent APIs for DriverEnricher and BrowserFeedback (admin key), HTML pages (the web fallback, registration, the admin page).

Next to it, the download server download.euzos.eu (server/download) checks download tokens for Caddy.

Every route has an English path. The old Dutch paths (from before 27-10-2026) are still served by server/profile/src/legacy.rs with their exact old wire format, because production PCs, workplace servers and the phone app still call them.

How do I…?

…find the handler for a path?

The router is euzos_profileserver::router in server/profile/src/lib.rs; it merges admin_page::router() (admin_page/mod.rs) and legacy::router() (legacy.rs). The tables below give the handler as crate::module::fn for every route.

…call the API from a PC?

Use the line protocol: GET/POST with ?token=<pc token> (64 hex) where needed, a text/plain body of key=value lines, and read the first line of the response (ok, fout, wacht, verlopen, afgemeld, …). The PC first gets a PC token by logging in (QR code via /api/session/{id}, or /api/pc/login, /api/pc/entra, /api/pc/reconnect).

…call the API from the app?

Send JSON with Authorization: Bearer <token>: an eza_… token from POST /api/app/v2/sign-in (password sign-in, 15 minutes, in memory) to pair a phone and manage devices, or an ezd_… device token from pairing (valid until revoked) to view and confirm login requests and log out PCs.

…use the agent APIs?

Set admin_token_file in the configuration, then send Authorization: Bearer <admin key> to /api/driverenricher/v2/… or /api/browserfeedback/v2/…. Without that file these APIs answer 404.

…know whether a feature is switched off?

Many routes answer 404 (fout\nonbekend pad\n) when their feature is off in the configuration: /api/pc/login (pc_password = no), /api/pc/entra (no entra_client_id), the vault (no vault_key_file), /api/apps/token (no download_key_file), report endpoints (reports = no), agent APIs (no admin_token_file), /beheer (admin_page = no).

Details

The PC line protocol

  • Content type text/plain; charset=utf-8.
  • First line: the status word. ok (success), fout (error, followed by a text line or reden=<code>), wacht (waiting for the phone), verlopen (expired, with reden=), afgemeld (logged out, with reden=), beschikbaar / niet_beschikbaar (app catalogue).
  • Then key=value lines. Unknown keys are skipped by both sides; a known key twice is an error.
  • The status words, keys, query parameter names (pc=, voorkeur=, intrekken=, token=) and values are the same on the English and the legacy routes and stay Dutch: they are a wire format that the kernel checks.
  • Errors from these endpoints have the form fout\nreden=<code>\n (some older ones fout\n<text>\n), with codes such as te_vaak (+ wacht=<s>), ongeldig, te_groot, database, druk, onjuist, andere_site.

The signed profile

After a successful login (QR approval, password, Microsoft, reconnect) the PC gets, in this order: ok, versie=2 (versie=1 for the old 8.5 format without PC secret), omgeving=, server=, sessie=, pc= (hash of the PC secret), soort= (zakelijk/prive), naam= (the e-mail address), weergavenaam=, kleur= (6 hex), taal=, the settings lines (see below), optionally beheer= (admin policy), item= lines (files), optionally methode=wachtwoord|microsoft, optionally the offline lines (offline-dagen=, offline-token=, account=), token= (the PC token, 64 hex), uitgegeven=, verloopt=, sleutel-id=, and last handtekening= (Ed25519). Everything before handtekening= is signed with the prefix EUZOS-profiel-v2\0 (pc::profile_response_with). The PC checks it with the public key from \EOS\FW\PROFILE.TXT (key=, legacy sleutel=).

item= lines: item=<map|bestand>\t<name>\t<count>\t<when> (at most 64).

Settings the PC writes back

POST /api/profile/settings takes key=value lines from an allow list (settings.rs), for example balk=boven|onder, thema=donker|licht|automatisch, toetsenbord=…, schermtoetsenbord=…, touchpad, mouse and touch options, screensaver=, screensaver-wachttijd=, vergrendelen=, recent=, taal=, kleur=, browser-zoekmachine=, browser-favoriet=, browser-engine=euzos|chromium, browser-site=, mail-account=, display= (per external monitor: on/off, extend/mirror, left/right, resolution, main display; legacy scherm= still accepted), display-layout= (the display profiles: window places per screen set-up; docs/DISPLAY.md), desktop-background=default|<32 hex>,<fill|fit|center> (Settings › Background; the picture itself goes sealed to /api/profile/background, see below). The keys and values stay Dutch (pc protocol), except screensaver-animatie=, which takes logo|aurora|stars|clock (legacy sterren|klok still accepted). They come back signed in the next profile. Never a password or a token.

Desktop background (/api/profile/background)

With the pc token, only when the vault is on (otherwise 404, like an older server): GET /api/profile/background (ok + id=, size=, changed= if there is one), PUT /api/profile/background/<32 hex> (the blob sealed by the pc, at most 1 MiB; the id must be the one desktop-background= names, otherwise 409; 413 above the limit, checked before the body is read), GET /api/profile/background/<32 hex> (the blob, or 204) and DELETE /api/profile/background. One per profile; another id or default in the setting deletes it. Schema 20 (backgrounds). Deploy the server before the kernels. Details: docs/PROFILE-PROTOCOL.md, "Desktop background on the server".

JSON (app and agents)

  • App errors: {"error": "<text>"} or {"error": "<text>", "code": "<code>"} with codes such as te_vaak, onjuist, niet_aangemeld, verlopen, intern, and the account status codes for 403.
  • On the legacy routes (/api/app/v1/…, the unversioned agent paths) a middleware (legacy::translate) renames the JSON field names and query parameters: Dutch → English in the request, English → Dutch in the response. Values (codes, statuses, texts) are not translated.

Signed messages

MessageSigned byLines
EUZOS-koppelen-v1 (pairing)phone chip key (ECDSA P-256)omgeving=, server=, gebruiker=, uitdaging=, sleutel= (SHA-256 of the public key)
EUZOS-inloggen-v1 (PC login)phone chip keyomgeving=, server=, sessie=, pc=, pcip=, profiel=, tijd=, uitdaging=, apparaat=
EUZOS-beheer-v1 (admin page login)phone chip keyomgeving=, server=, sessie=, beheerip=, gebruiker=, tijd=, uitdaging=, apparaat=
EUZOS-koppelcode-v1— (hash)the 6-digit code PC and phone both show: SHA-256 over the prefix, session id and hash of the PC secret
EUZOS-profiel-v2server signing key (Ed25519)the signed profile above
EUZOS-werkplek-aanmelden-v1workplace key (Ed25519)omgeving=, server=, sleutel=, uitdaging=, tijd=, lan=, tls=, naam=, handtekening=
EUZOS-downloadtoken-v1download key (Ed25519)the token ezdl1.<kid>.<app>.<version>.<exp>.<jti>.<account>.<signature>

Token prefixes

eza_ (app password sign-in), ezd_ (paired phone), eze_ (workplace server, 1 hour, memory only), ezdl1. (download token, 2 hours); the PC token is 64 hex without prefix.

Common protections

  • Body limit: 16 KiB by default; settings 2 × 48 KiB; BrowserFeedback reports a little over 512 KB (read by the handler itself); the desktop background 1 MiB (read by the handler itself; a Content-Length above it is refused before reading).
  • CSRF (lib::same_site): any non-GET/HEAD/OPTIONS request with Sec-Fetch-Site other than same-origin/none, or a foreign Origin, is refused with 403 fout\nreden=andere_site\n. The PC and app send neither header.
  • Response headers (lib::security): Cache-Control: no-store, Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Server: euzos-profielserver.
  • Client IP: behind Caddy the last address of X-Forwarded-For counts, only from localhost and only with the proxy secret header X-Euzos-Proxy.
  • Admin domain gate (admin_page::gate): on the admin domain only /beheer…, /b/<id> and /.well-known/… exist (/ redirects to /beheer); on the profile domain /beheer… redirects (308) to the admin domain until 26-10-2027, afterwards 404.
  • Unknown path: 404 fout\nonbekend pad\n.

Rate limits (main ones)

Limits are per network (IPv4 address, IPv6 /64, and IPv6 /48 at 4×) plus per token/account/PC. Exceeding them gives 429.

AreaLimit (from the code)
Session polls60/min per PC (session + PC secret, or PC token), 1200/min per network; running sessions at most 60 per network, 10,000 in total (hard limit 20,000)
Settings30 per 5 minutes per PC token
PC password loginper account and per PC: 3 free attempts, block after 10; per network 10/30; per /48 40/120; increasing delay, block 15 min (up to 4 h)
App sign-in10 failures per 15 minutes per IP and per user name
Vault30 per 5 minutes per PC token, 60 per account
App catalogue30/min per network
Download tokens20/hour per account, 60/hour per network
Entra login30/min per network
Offline reconnect120/min per network, 10 per token
Workplace30/min per network for challenge/sign-in; 30 download tokens/hour per workplace; 120 admin proofs/hour per account; registration 10/min per network and 10/hour per account
Admin page login10 per network and 5 per account per 15 minutes
Mail1/min and 5/day per address; 200/hour overall

Reference

Columns: Method, Path (English), Legacy path (Dutch, served by legacy.rs; "—" = same path or none), Auth, Request, Response, Handler (euzos_profileserver::…).

PC login and session (line protocol)

MethodPathLegacy pathAuthRequestResponseHandler
GET/api/session/{id}/api/sessie/{id}PC secret (?pc=<32 hex>), session bound to the PC's IP{id} 32 hex; query pc=, voorkeur=zakelijk|prive, intrekken=1 (QR rotation)wacht + code=<6 digits> [+ gezien=1]; verlopen + reden= (ingetrokken, gebruikt, geen profiel, …); or the signed profile once. Errors: 400, 403 andere pc, 429, 503pc::session
GET/api/files/api/bestandenPC token (?token=)—ok + item= lines; 401 ongeldig tokenpc::files
GET/api/status— (same)PC token—ok + verloopt=<unix>; or afgemeld + reden= (onbekend, afgemeld, verlopen); 503 on a database error (never afgemeld). Also triggers the Entra re-check.pc::status
GET/api/logout/api/afmeldenPC token—always okpc::logout
POST/api/pc/login/api/pc/inloggennone (throttled); off with pc_password = nolines email=, wachtwoord=, sessie=, pc= (32 hex), pc-id= (32 hex), voorkeur= (optional)signed profile with methode=wachtwoord (+ offline lines); errors reden=ongeldig, te_vaak (+wacht=), onjuist (401), account + status=<code> (403), geen_profiel (403), te_groot, druk, databasepc_login::login
POST/api/pc/entra— (same)Microsoft ID token; off without entra_client_idlines id_token=, sessie=, pc=, pc-id=, nonce= (optional), taal= (optional)signed profile with methode=microsoft; errors reden=ongeldig, te_groot, te_vaak, token, verlopen, sleutels, tenant, domein, account, drukentra::login
POST/api/pc/reconnect/api/pc/herverbindoffline tokenlines offline-token= (64 hex), sessie=, pc=new signed profile (new reconnect token); 403 reden= ingetrokken, verlopen, geblokkeerd, gewist, uit, onbekend (the PC logs out and wipes its offline vault); 400, 413, 429, 503offline::reconnect
POST/api/pc/register/api/pc/registreernone; only with registration_openlines email=, wachtwoord=, voornaam=, achternaam=, woonplaats= (all required), taal=nl|en (optional)201 ok + email=<address> (always, no enumeration); 400 reden=ongeldig|email_ongeldig|wachtwoord_kort; 403 gesloten; 413; 429 te_vaak; 503 drukpc_registration::register
POST/api/pc/verify/api/pc/verifieernonelines email=, code= (6 digits), wachtwoord=200 ok; 400 code_onjuist; 401 wachtwoord_onjuist; 409 email_in_gebruik; 429; 503pc_registration::verify
POST/api/pc/resend/api/pc/opnieuwnoneline email=always 200 ok; 400 ongeldig; 429 te_vaakpc_registration::resend
GET/api/preference/{id}/api/voorkeur/{id}nonesession idone line: zakelijk, prive or empty (the button chosen on the PC; for the web page)web::preference

Settings and vault (PC token)

MethodPathLegacy pathAuthRequestResponseHandler
POST/api/profile/settings/api/profiel/instellingenPC tokenkey=value settings lines (allow list)ok; 400 fout\n<reason>; 401; 413 te groot; 429; 503pc::settings
GET/api/profile/background—PC token; off without vault_key_file—ok + id=, size=, changed= if this profile has a picturebackground::info
PUT/api/profile/background/{id}—PC tokenthe blob sealed by the pc (at most 1 MiB)ok; 400 reason=invalid, 401, 404, 408, 409 reason=not_current (not the id of desktop-background=), 413 reason=too_large (before reading), 429, 503background::put
GET/api/profile/background/{id}—PC token—200 the blob as application/octet-stream, or 204 nonebackground::fetch
DELETE/api/profile/background—PC token—ok (also if nothing was there)background::delete
GET/api/vault/api/kluisPC token; off without vault_key_file—ok + soort=<kind> per secret (no content)vault::list
PUT/api/vault/{kind}/api/kluis/{kind}PC tokenraw bytes = the secretok; 400, 401, 403 (opslag-sleutel only by the server), 404, 409 (too many, or lager: a lower generation), 413, 429, 503vault::set
GET/api/vault/{kind}/api/kluis/{kind}PC token—200 secret as application/octet-stream, or 204 none; opslag-sleutel is created on first fetchvault::fetch
DELETE/api/vault/{kind}/api/kluis/{kind}PC token—ok (also if nothing was there); opslag-sleutel: 403 since 28-10-2026 (PS-15: crypto-shredding only with /api/app/v2/vault/wipe or the CLI)vault::delete_secret

Vault kinds (allow list, wire values): opslag-sleutel (storage key, 32 bytes, server-created), ms-refresh:<32 hex> (a Microsoft refresh token), opslag-versie:<32 hex> (generation of a profile directory, 8 bytes, only goes up). At most 9 secrets per user and profile kind, 8 KiB each; AEAD ChaCha20-Poly1305 under the server key.

Apps and download tokens

MethodPathLegacy pathAuthRequestResponseHandler
GET/api/apps— (same)none (public)—ok, then per app naam=, versie=, grootte=, sha256=, beschrijving=<url> (1–4), pakket=<url> (0–4); downloadbasis=<url> when tokens are on; werkplek=/werkplek-tls= lines for PCs in a workplace server's networkapps::list
GET/api/apps/{name}— (same)noneapp namebeschikbaar + the app's lines, or niet_beschikbaar + the name (always 200)apps::one
POST/api/apps/token— (same)PC token; off without download_key_filelines app=, versie= (must be the catalogue version)ok, token=ezdl1.…, verloopt=<unix>, duur=<s>download_token::fetch

Workplace servers (line protocol)

MethodPathLegacy pathAuthRequestResponseHandler
POST/api/workplace/v1/challenge/api/werkplek/v1/uitdagingregistered workplace keysleutel=<64 hex>ok, uitdaging= (60 s, one-time)workplace::challenge
POST/api/workplace/v1/sign-in/api/werkplek/v1/aanmeldensignature with the workplace keythe signed message EUZOS-werkplek-aanmelden-v1 (see above)ok, werkplek=<id>, token=eze_… (1 hour), verloopt=, servertijd=, downloadsleutel=<kid> <hex> (if tokens are on), organisatie= (if set)workplace::sign_in
POST/api/workplace/v1/download-token/api/werkplek/v1/downloadtoken?token=eze_…app=, versie=like /api/apps/tokenworkplace::download_token
POST/api/workplace/v1/admin-proof/api/werkplek/v1/beheerbewijsPC token of an admin; registered PC; same organisationwerkplek=<id>, pcsleutel=<64 hex>ok, naam=, lan=, tls=, then the proof lines and handtekening= (valid 10 minutes). Errors: 404 werkplek_onbekend/pc_onbekend; 403 werkplek_ingetrokken, werkplek_zonder_organisatie, pc_ingetrokken, andere_organisatie, geen_beheerder; 429workplace_admin::proof
GET/api/workplace/v1/admin-list/api/werkplek/v1/beheerlijstPC token—ok, then per server werkplek=<id>\t<name>\t<lan>\t<actief|niet actief>\t<installed by>\t<date>\t<external address>workplace_admin::list
POST/api/workplace/v1/register/api/werkplek/v1/registreerPC token of an admin (from the server installer)sleutel=<64 hex>, naam=, organisatie=<id> (only with more than one), extern=<address> (optional)ok, werkplek=<id>, organisatie=<id>, organisatie_naam=; errors 401 token, 403 geen_beheerder, 409 kies_organisatie (+ organisatie=<id>\t<name> lines; not for a server admin since 28-10-2026: registered without organization, organisatie= empty + organisatie_kiezen=1), 409 al_geregistreerd, 400 ongeldig, 429 te_vaakworkplace_registration::register

Reports: DriverEnricher and BrowserFeedback

MethodPathLegacy pathAuthRequestResponseHandler
POST/api/driverenricher/report/api/driverenricher/meldingPC token; off with reports = noreport in the DriverEnricher line format (at most 64 KiB, 128 devices; read with euzos_driverenricher::read)ok, nieuw=<n>, apparaten=<n>; 400, 401, 404, 408, 413, 429, 503driverenricher::report
GET/api/driverenricher/v2/list/api/driverenricher/lijstadmin keyquery status=, max=JSON devices list (most PCs first)driverenricher::list
GET/api/driverenricher/v2/device/{id}/api/driverenricher/apparaat/{id}admin key—JSON of one device, with sample, models, after_driver; free text under report_text with "source": "onbetrouwbaar"driverenricher::device
POST/api/driverenricher/v2/device/{id}/status/api/driverenricher/apparaat/{id}/statusadmin keyJSON {"status": "nieuw|in_behandeling|driver|niet_ondersteund", "version": "v150", "notes": "…"} (version required with driver)the updated devicedriverenricher::status
POST/api/browserfeedback/report/api/browserfeedback/meldingPC token; off with reports = noheader in line format plus an optional PNG (≤ 512 KB) (euzos_browserfeedback::read)ok, id=<number>browserfeedback::report
GET/api/browserfeedback/v2/list/api/browserfeedback/lijstadmin keyquery status=, category= (legacy categorie=), max=JSON reports (newest first, without screenshot)browserfeedback::list
GET/api/browserfeedback/v2/{id}/api/browserfeedback/{id}admin key—JSON of one report, with image_png_base64 if any; address_publicbrowserfeedback::details
GET/api/browserfeedback/v2/{id}/image.png/api/browserfeedback/{id}/afbeelding.pngadmin key—the PNG; 404 if nonebrowserfeedback::image
POST/api/browserfeedback/v2/{id}/status/api/browserfeedback/{id}/statusadmin keyJSON {"status": "nieuw|in_behandeling|opgelost|niet_op_te_lossen|dubbel", "version": "0.6.1", "notes": "…"} (version required with opgelost)the updated reportbrowserfeedback::status

Agent JSON field names (English / legacy Dutch): error/fout, notice/let_op, devices/apparaten, reports/meldingen, report_text/melding_tekst, source/bron, description/omschrijving, sample/voorbeeld, samples/voorbeelden, models/modellen, after_driver/na_driver, id, key/sleutel, kind/soort, pcs, accounts, first/eerste, last/laatste, last_build/laatste_build, status, version/versie, notes/notities, changed/gewijzigd, max, pc, account, time/tijd, build, arch, browser, language/taal, address/adres, address_public/adres_publiek, complete/volledig, category/categorie, viewport_w/viewport_b, viewport_h, zoom, http_status, load_time/laadtijd, render_time/opmaaktijd, bytes, comment/opmerking, errors/fouten, has_image/heeft_afbeelding, image_bytes/afbeelding_bytes, image_png_base64/afbeelding_png_base64 (legacy::AGENT). This is the rename table of the legacy middleware, not a per-response schema; which of these fields a given response carries: (to verify) per handler.

Phone app (JSON, /api/app/v2/)

MethodPathLegacy pathAuthRequestResponseHandler
GET/api/app/v2/server/api/app/v1/servernone—environment, server, key_id, public_key, web_fallback, registration_openapp::server
POST/api/app/v2/sign-in/api/app/v1/aanmeldennone (throttled)user (e-mail; email also accepted), passwordtoken (eza_…), expires, user, challenge, environment, server; 401 onjuist, 403 status code, 429 te_vaakapp::sign_in
POST/api/app/v2/logout/api/app/v1/afmeldeneza_ (forgets that sign-in)—ok (always)app::logout
POST/api/app/v2/register/api/app/v1/registreernone; only with registration_openuser (or email), password, display_name?, language?, first_name?, last_name?, city?201 user, status: "niet_geverifieerd"app::register
POST/api/app/v2/verify/api/app/v1/verifieernone{email, code, password} or {token, password}ok, userapp::verify
POST/api/app/v2/verify/resend/api/app/v1/verifieer/opnieuwnoneemail (or user)ok (always)app::verify_resend
POST/api/app/v2/email/api/app/v1/emaileza_newok (mail sent to the new address)app::email_change
POST/api/app/v2/devices/challenge/api/app/v1/apparaten/uitdagingeza_—challenge (new pairing challenge)app::pair_challenge
POST/api/app/v2/devices/api/app/v1/apparateneza_name, platform (ios|android), public_key (base64 P-256), signature (base64, over EUZOS-koppelen-v1), attestation? (base64, ≤ 8 KiB)201 device, device_token (ezd_…), environment, server; at most 10 devicesapp::pair
GET/api/app/v2/devices/api/app/v1/apparateneza_ or ezd_—devices: [{device, name, platform, created, last, revoked, this_device}]app::devices
DELETE/api/app/v2/devices/{id}/api/app/v1/apparaten/{id}eza_ (any own device) or ezd_ (only itself: unpair)—okapp::revoke
GET/api/app/v2/session/{id}/api/app/v1/sessie/{id}ezd_—session, environment, server, pc, pc_ip, phone_ip, same_network, code, preference, created, expires, challenge, device, user, profiles [{kind, name, display_name, color}]; 410 verlopenapp::session
POST/api/app/v2/session/{id}/confirm/api/app/v1/sessie/{id}/bevestigezd_ + chip signatureprofile, time, signature (base64, over EUZOS-inloggen-v1)ok, pc_ip, profileapp::confirm
POST/api/app/v2/session/{id}/reject/api/app/v1/sessie/{id}/weigerezd_—okapp::reject
GET/api/app/v2/pc-sessions/api/app/v1/pcsessieseza_ or ezd_—pc_sessions: [{ref, kind, pc_ip, device, created, expires, logged_out, method}]app::pc_sessions
POST/api/app/v2/pc-sessions/{ref}/logout/api/app/v1/pcsessies/{ref}/afmeldeneza_ or ezd_—ok (remote logout of that PC; also revokes its offline vault)app::pc_logout
POST/api/app/v2/vault/wipe—eza_ (signed in with the password){email?, profile?, confirm}{ok, deleted}: wipe the vault (incl. opslag-sleutel) of one profile or both; only the owner or an admin of the account's organization; 400 bevestig/ongeldig, 401, 403 geen_recht, 404 (vault off), 429vault::wipe_vault
GET/api/app/v2/admin/{id}/api/app/v1/beheer/{id}ezd_—kind: "beheer", session, environment, server, user, admin_ip, phone_ip, same_network, text, code, created, expires, challenge, device; 410 verlopenadmin_page::app::request
POST/api/app/v2/admin/{id}/confirm/api/app/v1/beheer/{id}/bevestigezd_ + chip signaturetime, signature (base64, over EUZOS-beheer-v1)ok, admin_ipadmin_page::app::confirm
POST/api/app/v2/admin/{id}/reject/api/app/v1/beheer/{id}/weigerezd_—okadmin_page::app::reject

Legacy JSON field names of the app API (legacy::APP): error/fout, environment/omgeving, key_id/sleutel_id, public_key/publieke_sleutel, web_fallback/web_terugval, registration_open/registratie_open, expires/verloopt, user/gebruiker, password/wachtwoord, display_name/weergavenaam, language/taal, first_name/voornaam, last_name/achternaam, city/woonplaats, new/nieuw, challenge/uitdaging, name/naam, signature/handtekening, attestation/attestatie, device/apparaat, device_token/apparaat_token, devices/apparaten, this_device/dit_apparaat, created/gemaakt, last/laatst, revoked/ingetrokken, session/sessie, phone_ip/telefoon_ip, same_network/zelfde_netwerk, preference/voorkeur, profiles/profielen, profile/profiel, kind/soort, color/kleur, time/tijd, pc_sessions/pcsessies, logged_out/afgemeld, method/methode, admin_ip/beheer_ip, text/tekst. Unchanged: code, ok, server, token, email, status, platform, pc, pc_ip, ref.

Web pages (HTML; user-visible paths stay as they are)

MethodPathAuthRequestWhatHandler
GET/none—start pageweb::begin
GET/l/{id}none—the phone page of a QR login without the app (only with web_fallback)web::phone_page
POST/l/{id}/aanmeldennoneform user, passwordsign in on the web pageweb::sign_in
POST/l/{id}/bevestigweb cookieform action=login|reject, profile=zakelijk|priveapprove or reject the PC loginweb::confirm
POST/uitweb cookieform backlog out on the phone pageweb::logout
GET/v/{token}link from the mail—e-mail confirmation pageweb::verification_page
POST/v/{token}link from the mailform passwordconfirm the addressweb::verify
GET/registreernone—registration form (or "closed")web::register_page
POST/registreernone; registration_openform first_name, last_name, city, email, password, repeatregister (same steps as the app)web::register
GET/.well-known/apple-app-site-associationnone—iOS universal links (/l/*, /v/* open the app)web::apple
GET/.well-known/assetlinks.jsonnone—Android app linksweb::android

Admin page (HTML, admin domain only)

See Admin page. Session cookie __Host-euzos_beheer (15 minutes idle, 4 hours max, bound to the IP), CSRF token in every form, POST only from the own site.

MethodPathWhatHandler
GET/beheer, /beheer/login form (form fields email, password, csrf) or dashboardadmin_page::fetch_begin
GET/beheer/{page}pages: accounts (?q= search), accounts/<id>, pcsessies, telefoons, apps, downloadtokens, log, statusadmin_page::fetch → pages::show
POST/beheer/inloggenlogin with e-mail and password, then the QR/confirmation in the appadmin_page::login::login
POST/beheer/annuleercancel a waiting loginadmin_page::login::cancel
POST/beheer/uitlog outadmin_page::send_core
POST/beheer/accounts/nieuwcreate an accountpages::account_new
POST/beheer/accounts/<id>/<action>blokkeer, deblokkeer, wachtwoord, email, beheer, wis, wis-bevestig (two-step delete, 5 minutes)pages::account_action
POST/beheer/pcsessies/afmeldenlog out a PC (ref, back)pages::pc_logout
POST/beheer/telefoons/intrekrevoke a phone (id, back)pages::phone_revoke
POST/beheer/apps/zetadd/replace a catalogue entrypages::app_set
POST/beheer/apps/wisremove an app (name)pages::app_delete
POST/beheer/downloadtokens/intrekrevoke a download token (jti, comment)pages::token_revoke
GET/b/{id}the link in the admin QR code (opens the EUZOS app)admin_page::login::link_page

Download server (download.euzos.eu)

server/download (euzos-download) runs on localhost behind Caddy's forward_auth. Caddy asks it for every request and passes X-Forwarded-Method, X-Forwarded-Uri, Authorization and X-Forwarded-For; on 200 Caddy serves the file.

MethodPath (as forwarded)AuthResponse
GET, HEAD/<app>/<version>/<file>Authorization: Bearer ezdl1.…200 allowed; otherwise fout\nreden=<code>\n: 401 geen_token, handtekening, verlopen; 403 ingetrokken, ander_pad; 404 pad (other method or path)

A token is valid if its kid is known (public keys in the key file), the signature matches, app and version equal the path, it has not expired and is not valid too far into the future, and its jti is not on the blocklist (euzos_download::check).

Legacy routes (old → new)

Policy (docs/ENGLISH.md, rule 3): the profile server API has English routes and JSON fields; the old Dutch routes keep their exact old wire format in legacy.rs until every production PC, workplace server and the phone app has moved. The line protocol is byte-identical on both; the JSON APIs are the same handlers with the field names translated. Cross-checked against legacy::router:

MethodOld (legacy)New
GET/api/sessie/{id}/api/session/{id}
GET/api/bestanden/api/files
GET/api/status/api/status (same, no legacy route)
GET/api/afmelden/api/logout
POST/api/pc/inloggen/api/pc/login
POST/api/pc/entra/api/pc/entra (same, no legacy route)
POST/api/pc/herverbind/api/pc/reconnect
POST/api/pc/registreer/api/pc/register
POST/api/pc/verifieer/api/pc/verify
POST/api/pc/opnieuw/api/pc/resend
POST/api/profiel/instellingen/api/profile/settings
GET/api/kluis/api/vault
PUT/GET/DELETE/api/kluis/{soort}/api/vault/{kind}
GET/POST/api/apps, /api/apps/token, /api/apps/{naam}same (no legacy route)
POST/api/werkplek/v1/uitdaging/api/workplace/v1/challenge
POST/api/werkplek/v1/aanmelden/api/workplace/v1/sign-in
POST/api/werkplek/v1/downloadtoken/api/workplace/v1/download-token
POST/api/werkplek/v1/beheerbewijs/api/workplace/v1/admin-proof
GET/api/werkplek/v1/beheerlijst/api/workplace/v1/admin-list
POST/api/werkplek/v1/registreer/api/workplace/v1/register
POST/api/driverenricher/melding/api/driverenricher/report
GET/api/driverenricher/lijst/api/driverenricher/v2/list
GET/api/driverenricher/apparaat/{id}/api/driverenricher/v2/device/{id}
POST/api/driverenricher/apparaat/{id}/status/api/driverenricher/v2/device/{id}/status
POST/api/browserfeedback/melding/api/browserfeedback/report
GET/api/browserfeedback/lijst/api/browserfeedback/v2/list
GET/api/browserfeedback/{id}/api/browserfeedback/v2/{id}
GET/api/browserfeedback/{id}/afbeelding.png/api/browserfeedback/v2/{id}/image.png
POST/api/browserfeedback/{id}/status/api/browserfeedback/v2/{id}/status
GET/api/voorkeur/{id}/api/preference/{id}
GET/api/app/v1/server/api/app/v2/server
POST/api/app/v1/aanmelden/api/app/v2/sign-in
POST/api/app/v1/afmelden/api/app/v2/logout
POST/api/app/v1/registreer/api/app/v2/register
POST/api/app/v1/verifieer/api/app/v2/verify
POST/api/app/v1/verifieer/opnieuw/api/app/v2/verify/resend
POST/api/app/v1/email/api/app/v2/email
GET/POST/api/app/v1/apparaten/api/app/v2/devices
POST/api/app/v1/apparaten/uitdaging/api/app/v2/devices/challenge
DELETE/api/app/v1/apparaten/{id}/api/app/v2/devices/{id}
GET/api/app/v1/sessie/{id}/api/app/v2/session/{id}
POST/api/app/v1/sessie/{id}/bevestig/api/app/v2/session/{id}/confirm
POST/api/app/v1/sessie/{id}/weiger/api/app/v2/session/{id}/reject
GET/api/app/v1/pcsessies/api/app/v2/pc-sessions
POST/api/app/v1/pcsessies/{ref}/afmelden/api/app/v2/pc-sessions/{ref}/logout
GET/api/app/v1/beheer/{id}/api/app/v2/admin/{id}
POST/api/app/v1/beheer/{id}/bevestig/api/app/v2/admin/{id}/confirm
POST/api/app/v1/beheer/{id}/weiger/api/app/v2/admin/{id}/reject

The kernel still calls the old routes (/api/sessie/, /api/profiel/instellingen, /api/driverenricher/melding, /api/browserfeedback/melding, /api/werkplek/v1/…; docs/ENGLISH.md).

See also

  • CLI · PC login · Phone app · Apps and downloads · Admin page
  • docs/PROFILE-PROTOCOL.md (the full protocol)
  • docs/WORKPLACE-PROTOCOL.md · docs/WORKPLACE.md
  • docs/DRIVERENRICHER.md · docs/BROWSERFEEDBACK.md
  • docs/DOWNLOADSERVER.md · docs/OFFLINE-LOGIN.md · docs/WORK-LOGIN.md
  • docs/ENGLISH.md ("Profile server API")