Initialize the API client with a session for HTTP cookie management.
Creates a new requests.Session with proper headers configured for the Schulportal Hessen web application. Sets up default User-Agent and Accept headers to mimic browser behavior.
The session persists cookies across requests, enabling automatic session handling after successful login.
After initialization, call login() with valid credentials to authenticate before making other API calls.
Retrieve available apps/modules for the logged-in user.
Fetches the user's personalized list of available modules from the startseite.php AJAX endpoint. This includes navigation items like Kalender, Nachrichten, Mein Unterricht, etc.
Dict[str, Any] A dictionary containing: - success (bool): Whether the request succeeded - data (Dict): The raw response with folders and entries - folders: List of folder groupings with name, logo, color - entries: List of available apps/modules - till: Timestamp (cache validity)
RequestsException If the HTTP request fails.
api.get_apps() {'success': True, 'data': { 'error': '0', 'folders': [{'name': 'Start', 'logo': 'fa fa-newspaper-o', 'farbe': 'faebcc'}], 'entrys': [{'Name': 'Kalender', 'Farbe': '168647', 'Logo': 'fa fa-calendar'}], 'till': 1765299123 }}
Follow redirects for a URL and return the final destination.
Manually follows redirects step by step. For OAuth/OIDC flows (vidis, oauth, openid) the auth-callback URL (containing the OAuth code parameter) is returned so the user's browser can receive auth cookies directly from the target application. Simple redirects are resolved to their final destination. Falls back to the original URL on any error.
url : str The URL to resolve. timeout : int Request timeout in seconds.
str The final URL after redirects, or the auth-callback URL for OAuth flows, or the original URL if resolution fails.
Return the logged-in user's available modules with resolved URLs.
This helper reads the raw app list returned by :meth:get_apps and
normalizes each entry into a compact structure that is easier to use in
client code. Relative links are converted to absolute URLs and portal
tracking redirects are resolved to their final destinations.
List[Dict[str, str]] A list of modules containing: - name: Display name of the module - url: Absolute access URL (portal wrapper) - direct_url: Resolved final URL after redirects - color: Module color value from the portal - logo: Icon class for the module - folders: Folder/group metadata attached to the module - target: Link target, usually "_self" - usable: Whether the module is supported by this package - usage: List of API method names to use with the module
If the user is not logged in or the app list cannot be loaded, an empty list is returned.
Fetch the authenticated user's available Oberstufenwahl campaigns.
Returns campaign IDs, titles, participation windows, and links to the corresponding form. It does not submit or modify any election data.
Fetch and normalize one dynamic election form. The result contains personal fields, selection blocks, options, and the constraints needed to validate the form before submission.
Submit one election after validating the answers. The caller must pass
confirmed=True; without explicit confirmation this method performs no
upstream POST.
Return the current session cookies as a plain dictionary.
Dict[str, str] Mapping of cookie names to values for the active HTTP session.
Fetch the conversation list for the authenticated user.
Args: get_type: Message filter. Common values are "All", "visibleOnly", and "unvisibleOnly". last: Pagination marker used for incremental fetches.
Returns: Dict containing the decrypted conversation list and the reported total count.
Fetch the full message thread for a conversation.
Args:
conversation_id: Encrypted conversation identifier returned by
:meth:nachrichten_get_headers.
last: Pagination marker for older messages.
Returns: Dict containing the decrypted message payload and nested replies.
Search message recipients by name or partial name.
Args: query: Search term used to look up users in the recipient picker.
Returns: Dict containing the matching users.
Send a new Nachricht using the portal's encrypted payload format.
Args: message_data: Dictionary with at least these keys: recipients: List of recipient ids such as ["l-{recipient_id}"] subject: Message subject text body: Message body text
Returns: Dict with success status and the returned message id when sending succeeds.
Reply to an existing message conversation.
Args: conversation_id: Conversation uniqid to reply to. body: Reply message content. to: Recipient selector ("all" or a user id).
Returns: Dict with success status and response details.
Fetch the Mein Unterricht overview with current entries.
Returns:
Dict containing recent lesson entries, all course folders in courses,
and their counts. An empty parse is fetched once more before it is accepted.
Login responses and empty pages without the overview heading and activity
table return success: false and are not cached as empty course lists.
Fetch the detailed page for a single course folder.
Args: course_id: Course/book id from the portal's data-book attribute.
Returns: Dict containing course metadata, entries, attendance information, and attached files.
Fetch a linked Mein Unterricht entry by URL.
Args: url: Relative path or absolute URL for the linked entry.
Returns: Dict containing the fetched content and its detected content type.
Fetch the weekly Mein Unterricht view.
Returns: Dict containing the HTML response for the weekly class overview.
Fetch the Mein Unterricht submissions/assignments view.
Returns: Dict containing the HTML response for the submissions page.
Mark or unmark a homework entry as completed.
Args: course_id: Course/book id for the homework entry. entry_id: Entry id for the homework item. done: True to mark as done, False to mark as not done.
Returns: Dict containing the requested state and whether the update succeeded.
Download an attached file using the authenticated session.
Args: url: Relative or absolute file URL extracted from course entries.
Returns: Dict containing filename metadata and binary content.
Fetch and parse the calendar overview page.
Returns: Dict containing calendar metadata, categories, groups, and export links.
Fetch calendar events with the same filters as the web UI.
Args: year: School year selector, where 0 is the current year. start: Calendar start mode such as "year", "month", "week", or "day". category: Category id filter. search: Free-text search term. target: Target-group filter. view_id: Optional calendar view id. If omitted, the current default view is used.
Returns: Dict containing the parsed events, filter metadata, and raw payload.
Fetch a single calendar event using the portal's getEvent action.
Args: event_id: Internal event id. view_id: Optional calendar view id. If omitted, the default view from the overview page is used.
Returns: Dict containing the parsed event payload and the applied filters.
Fetch the substitution plan (vertretungsplan.php).
Args: include_raw: Include the raw HTML response in the payload.
Returns: Dict containing the parsed substitution days and metadata.
Fetch the timetable (stundenplan.php).
Returns: Dict containing timetable data for all and personal views.
The response also contains ``days`` with the table's localized weekday
headings and ``hours`` with the start and end time of each lesson.
Fetch the root folder for the file storage (dateispeicher.php).
Fetch files and folders for a specific dateispeicher node.
Search files in the dateispeicher by name.
Fetch targeted file distributions for the authenticated user. Returns each distribution's title, provenance, date, unread state, files, and related links.
Stream a file from dateiverteilung.php through the authenticated portal
session. Download URLs are restricted to the configured Schulportal origin,
the Dateiverteilung endpoint, and known download actions.
Fetch study groups and exam data (lerngruppen.php).
Fetch the authenticated user's profile data.
Returns: Dict containing the lowercased profile fields extracted from benutzerverwaltung.php.
Fetch and parse the complete public school directory.
Returns: Dict containing all districts and their schools.
Fetch the schools for one district by id.
Args: district_id: District id such as "7".
Returns: Dict containing the matching district and its schools.
Search the public school list by school name.
Args: school_name: School name or partial name to search for.
Returns: Dict containing the matching schools and the total count.
Login to DSBmobile to access substitution plans.
Args: username: DSBmobile username or school identifier (e.g. {username}). password: DSBmobile password (e.g. {password}).
Returns: Dict with success status and session cookie data.
Fetch substitution plan iframe URLs after login.
Args: username: DSBmobile username or school identifier (e.g. {username}). password: DSBmobile password (e.g. {password}).
Returns: Dict with plan iframe URLs.
Fetch and parse the substitution plan table from DSBmobile.
Args: username: DSBmobile username or school identifier (e.g. {username}). password: DSBmobile password (e.g. {password}). plan_index: Which iframe plan URL to parse (default: 0). plan_url: Explicit plan URL to fetch (overrides plan_index).
Returns: Dict with the plan URL, title, last-updated timestamp, parsed tables, and optional raw HTML.
Logout from Schulportal Hessen
Login to Schulportal Hessen
Validate a given session ID (sid)
Login using set creds in .env
Close the session
Login using credentials stored in a .env file.
Args: env_path: Optional path to the .env file. If omitted, looks for .env in the current working directory.
Returns: Dict with the login result or an error description.
Login to Schulportal Hessen
Args: school_id: The school ID (e.g., "1234") username: Username in format firstname.lastname password: User password
Returns: Dict containing login status and any error messages
Example: >>> api = SchulportalHessenAPI() >>> result = api.login("1234", "firstname.lastname", "password123") >>> print(result)
Logout from Schulportal Hessen
Returns: Dict containing logout status
Validate the current session by checking if the apps endpoint is accessible
Returns: Dict containing validation status
Fetch the calendar overview page and extract its metadata.
Retrieves the calendar configuration including available categories, groups, and user-specific settings.
Dict[str, Any] Dictionary containing: - success (bool): Whether request succeeded - page_title (str): Page heading - calendar (Dict): Configuration with first_id, can_write, key, etc. - categories (List[Dict]): Available event categories - groups (List[Dict]): Available groups - export_links (List[Dict]): Export options (iCal, PDF, etc.)
RequestsException If the HTTP request fails.
api.kalender_get_overview() {'success': True, 'calendar': {'first_id': 'v-123', 'can_write': False}, 'categories': [{'id': 20, 'name': 'Sonstige Termine', 'color': '#2e2e2e'}], 'groups': [], 'export_links': [{'label': 'als PDF', 'url': '...'}]}
Fetch calendar events using the same POST contract as the web UI.
Retrieves calendar events with filtering options matching the SPH web interface functionality.
year : int, optional School year: 0 = current year, 1 = next year. start : str, optional Calendar start mode. Options: "year", "month", "week", "day". category : str, optional Filter by category ID (from kalender_get_overview categories). search : str, optional Free-text search filter (matches title, location, description). target : str, optional Target group filter (Zielgruppe). view_id : str, optional Specific calendar view ID. If omitted, uses default view.
Dict[str, Any] Dictionary containing: - success (bool): Whether request succeeded - events (List[Dict]): Event objects with id, title, category, description, start, end, all_day, editable, etc. - count (int): Number of events returned - categories (List[Dict]): Available categories - groups (List[Dict]): Available groups - filters (Dict): The filters used for this query
api.kalender_get_events(year=0, start="month", category="20") {'success': True, 'events': [{'id': '{event_id}', 'title': '{title}', 'category': 20, ...}], 'count': 1, 'categories': [...], 'filters': {...}}
Fetch a single calendar event via the same getEvent POST action as the UI.
Args: event_id: Internal event id. view_id: Selected calendar view id. If omitted, the current default view is used.
Returns: Dict with success status and the parsed event payload.
Login to DSBmobile to access substitution plans.
DSBmobile (https://www.dsbmobile.de) is a separate platform from Schulportal Hessen that provides substitution/replacement plan information for schools.
This method establishes a separate session for DSBmobile and stores the authentication cookies for subsequent calls.
username : str DSBmobile username, typically in format "{school_id}{username}" or just the school identifier depending on school configuration. password : str DSBmobile password.
Dict[str, Any] Dictionary containing: - success (bool): Whether login succeeded - session_cookie (str): The DSBMobile session cookie value - session_id (str): ASP.NET session ID - response_url (str): Final redirect URL
RequestsException If the HTTP request fails.
DSBmobile uses different credentials than SPH. The username is typically provided by the school administration. This is a completely separate system from Schulportal Hessen.
api.dsb_login("F1234", "mypassword") {'success': True, 'session_cookie': 'abc123...', 'session_id': 'def456...'}
Fetch plan URLs from GetData XHR endpoint after login.
Args: username: DSBmobile username or school identifier (e.g. {username}). password: DSBmobile password (e.g. {password}).
Returns: Dict with a list of plan URLs.
Example: >>> api.dsb_get_plan_urls("{username}", "{password}") {"success": True, "plan_urls": ["{plan_url}", ...]}
Fetch and parse the substitution plan table from DSBmobile.
Args: username: DSBmobile username or school identifier. password: DSBmobile password. plan_index: Which plan URL to parse (default: 0). plan_url: Explicit plan URL to fetch (overrides plan_index). include_raw: Include raw HTML in response. klasse: Filter results by class name (e.g. "05A", "10C").
Returns: Dict with the plan URL, title, parsed tables, and optional raw HTML.
Example: >>> api.dsb_get_substitution_plan("{username}", "{password}") {"success": True, "plan_url": "{plan_url}", "tables": [...]}
Filter tables to only include rows matching the given class name.
POST /account/deletion/recover accepts { "access_token": "...", "refresh_token": "..." }
from the session that requested deletion. It returns Cache-Control: no-store.
An expired access token is accepted only to verify this recovery identity; it
cannot authorize account deletion or an account export.
The route holds the account lifecycle lock until any ongoing deletion settles.
If the original refresh session survives, it rotates that session's JWT ID and
returns { "state": "cancelled", "access_token": "...", "expires_in": 3600 }.
Every DELETE rechecks its original JWT ID after taking the same lock, so a delayed
request cannot erase an account that recovery has restored. Retrying recovery
with the saved original tokens is safe if its response was interrupted.
{ "state": "revoked" } means the saved session no longer exists or has expired.
It authorizes clearing local data for that obsolete session; it does not assert
that the user's Schulportal account or all server-side account data was deleted.
A missing or mismatched refresh token for a surviving session returns HTTP 409;
a forged recovery identity returns HTTP 401. These responses do not authorize
browser cleanup.
Deploy the API change before the UI change. The UI persists recovery identity before sending DELETE and preserves it until browser cleanup finishes. For attachments cached before ownership tracking, the UI re-fetches course metadata and retries the download, establishing access through the authenticated course response without granting access based on possession of a file hash.
Administrator JWTs now have a jti backed by a short-lived admin_sessions
record in auth.db. Deleting an account revokes its administrator sessions as
well as its regular sessions. A fresh login creates a new session; it does not
reactivate old JWTs. The administrator allowlist still applies.
Existing stateless administrator JWTs from before this migration are rejected. Administrators must sign in again after deploying this change. The JWT lifetime and admin frontend response schema remain unchanged. Session metadata expires and is purged on lookup, login and periodic cleanup.
Stale /apps, /modules and /benutzer caches are revalidated through tracked
account tasks. Pending refresh tasks hold identifiers, not private clients;
running upstream fetches hold a shared lifecycle lease so deletion waits for them.
Completed refresh work is not retained. Revoked regular JWTs release the
lifecycle lock before downstream request-body parsing.
Ordinary authenticated requests, profile/message refresh tasks, attachment downloads, notifications and WhatsApp work can run concurrently for one account. Login, recovery and deletion take exclusive access and wait for active work to finish; a waiting exclusive operation blocks new shared entrants. Slow upstream or AI calls therefore do not serialize page requests, while deletion still waits for private upstream work before closing clients and removing data. Message refresh jobs also carry the original session ID instead of bound private clients, validate it when starting, and discard completed payloads.
Authenticated admin handlers also hold shared actor lifecycle access. They revalidate the persisted admin session after entering; deletion waits for already-running handlers, and handlers waiting behind deletion reject the revoked session. User-detail routes acquire actor and target access in a stable order, and nested audit helpers reuse those leases. Admin login keeps its exclusive lifecycle context.
Account export and deletion read refresh-session metadata without decrypting the stored Schulportal password. Rotating the encryption key or corrupting a stored ciphertext therefore does not prevent a user from exporting LANIS-held data or deleting the account.
Account export and deletion include submitted feedback from feedback.db.
Deletion also clears the deleted administrator's identity from completion
actions on other users' reports. Admin credential reveal and step-up routes
are removed; user detail exposes only credential availability and timestamps.