Skip to content

Changelog

The API is versioned via the URL path (currently /v1/). Within a version, changes are backward compatible: we add new endpoints, fields and parameters, but we do not remove or rename existing ones and do not change their meaning. Your integration should therefore ignore unknown fields in responses. Breaking changes would only come with a new API version and would be announced here beforehand.

Current availability of the API is shown on the status page.

  • Reliable 400 for duplicate users. If the username or email address is already taken when creating or editing users, the API now always answers with 400 and the error type ValidationError, which names the affected field in field_errors; previously such conflicts could end in a 500 in rare cases. Username and email address are now also unique regardless of case: anna and Anna count as the same value. See errors and rate limiting and the API reference, section Users.
  • expand now requires the read scope of the embedded resource. Expanding a field via the query parameter expand embeds the full linked object, so the API key now also needs the Read scope of that resource, for example users:read for ?expand=user on participations. If the key lacks the scope, the API answers with 403 and the error type MissingScope, which names the required scope. See expanding resources and access scopes.
  • Idempotent requests. All POST endpoints accept the new Idempotency-Key header, which makes retries safe: resending the same request with the same key returns the stored response of the first successful execution instead of executing it again. See Idempotent requests.
  • Access scopes for API keys. Every API key now carries access scopes that control per resource what it may call: No access, Read or Read and write, plus a deliberately separate right for login links. The API reference names the required scope on every endpoint; if a key lacks one, the API answers with 403 and the new error type MissingScope, and the new endpoint GET /v1/me/ returns the scopes of the key in use. Existing keys keep full access. The key list in the admin area additionally shows when each key was last used. See access scopes.
  • Choose your page size. All list endpoints accept the new query parameter page_size (1 to 100; the default stays at 25). The URLs in next and previous keep the chosen page size. See pagination and sorting.
  • Webhooks. Schulungstool can now actively notify your systems about events for users and participations: signed POST requests with retries, a 30-day delivery log and test events, configured in the admin area. See the webhooks guide and the API reference, section Webhooks.
  • Rate limit headers on every response. Every API response now reports the current state of the rate limit in the headers X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so clients can pace their requests before running into a 429. See errors and rate limiting.
  • Request IDs and API log. Every API response carries the new header X-Request-ID; error responses repeat the ID as request_id in the body. Include it in support inquiries: it lets us look up the exact request in our logs. See errors and rate limiting. The admin area additionally keeps a 30-day log of the requests made with your API keys (metadata only, no request or response bodies), under Settings > Advanced settings > Developer options > API log.
  • New documentation site. English is now the default language, with a complete German version. The new API reference supports trying requests directly in the browser, and the schema is available for download as an OpenAPI 3.1 document (German), plus an llms.txt for AI assistants. The API itself is unchanged; all existing links to the previous documentation keep working.
  • Email notification when creating participations. Create a participation can now trigger the notification email to the user directly: set send_email_notification, optionally scheduled via email_scheduled_at.
  • Enforce a password change. The user object has the new field requires_password_change; it can be set when creating and editing users and filtered on when listing them.
  • Delta queries with updated_after. The list endpoints support the new filter updated_after: fetch only the objects changed since your last run instead of the full list; see the new guide Syncing data. Changes to group memberships now update updated_at of the affected user and group. Course ratings are the deliberate exception: they are anonymous, so they carry no timestamps and no updated_after filter.