# EduBase API Documentation *Auto-generated on 2026-10-31* --- ## Table of Contents - [Welcome to the EduBase Developer Help](#Welcome-to-the-EduBase-Developer-Help) - [Introduction to EduBase API](#Introduction-to-EduBase-API) - [Authentication](#Authentication) - [API: Classes endpoints](#API:-Classes-endpoints) - [API: Exams endpoints](#API:-Exams-endpoints) - [API: Integrations endpoints](#API:-Integrations-endpoints) - [API: Metrics endpoints](#API:-Metrics-endpoints) - [API: Organizations endpoints](#API:-Organizations-endpoints) - [API: Content permissions endpoints](#API:-Content-permissions-endpoints) - [API: Quiz Plays endpoints](#API:-Quiz-Plays-endpoints) - [API: Quiz questions endpoints](#API:-Quiz-questions-endpoints) - [API: Quizes endpoints](#API:-Quizes-endpoints) - [API: Content tagging endpoints](#API:-Content-tagging-endpoints) - [API: Users endpoints](#API:-Users-endpoints) - [Contact](#Contact) - [Legal](#Legal) --- ## Welcome to the EduBase Developer Help This documentation will **help you integrate your software or service with EduBase**, using API calls and webhooks. If these guides get too technical and you feel stuck, please contact us. !!! tip You will most likely **need an EduBase account** to follow the documentation. Therefore it is recommended to [register](https://www.edubase.net/page/register) and get familiar with EduBase before continuing. ### What is EduBase? If you are not yet familiar with our services you might want to visit our [homepage](https://www.edubase.net) or the EduBase Quiz product [introduction page](https://www.edubasequiz.com/en/). !!! summary "Briefly, in two sentences" EduBase helps you set up your entire online learning with speed and unrivaled flexibility. Online exams, videos, SCORM, all your materials in just one system. _Easy to use. No installation needed. Looks great on all devices. Fast & reliable support. AI-powered._ ### LTI integration EduBase also **supports LTI protocols** (1.0, 1.1 and 1.3/Advantage). If you want to integrate your LMS with EduBase Quiz system, we recommend using the LTI integration which provides a standardized solution to embed practice Quizes and exams into LMS courses and contents. Our integration also sends results back right into the LMS (if feature is supported/enabled), so there are no manual steps involved! Get started today with an LTI integration! To **learn more about this type of integration** and quickly get started, please visit [our EduAppCenter page](https://www.eduappcenter.com/apps/1082). ### See EduBase Quiz in action Curious how Quiz tests look like? **Launch our demo set** by clicking the image to get started: [![Start EduBase Quiz demo set](images/index-game.webp){: style="width:100%"}](https://eduba.se/+EduBaseQuizDemo/EN) ### Get started with MCP This documentation is for developers who want to integrate their software or service with EduBase using API calls and webhooks. If you want to automate your workflow using AI, you can also use our [MCP server](https://github.com/EduBase/MCP) ([Model Context Protocol](https://modelcontextprotocol.io)) to interact with EduBase without writing code, even from your favorite LLM applications like ChatGPT or Claude. We also offer pre-built agent skills that you can use right away in compatible tools. Visit our public [repository](https://github.com/EduBase/skills) to learn more about these and get started. --- ## API ### Introduction to EduBase API #### Prerequisites To access **EduBase API services**, you need the following: * an EduBase account * registered API application (connected to the user) * application ID * application secret #### Obtain API credentials To obtain your API application ID and secret key, login to EduBase with your account and go to the [Integrations](https://www.edubase.net/content/integrations) page. Create an API application or view your existing one. If you lack of permissions to create integrations (or just API integrations), please [contact us](../../contact/). !!! warning Do not share API credentials with anyone. #### Endpoints Endpoints are constructed from the following informations: * `https://` * EduBase domain name (e.g `www.edubase.net`) * `/api` * *optional: version specification `/vXX`* * endpoint path (e.g `/test:app`) Putting them all together, a request URL is created: ``` text https://www.edubase.net/api/v1/test:app ``` !!! warning Some endpoints require special features to be enabled (either on user or global level). Additionally, endpoints may also be protected by policies. Please contact us if you repeatedly receive authorization errors (e.g 403/406 response status code). **Endpoints reference** is available in its dedicated section from the navigation panel, split into several pages. Each page contains a list of endpoints with their HTTP method, their input and output parameters, and other relevant information. **Beware of similar endpoints grouped together** under the same subtitle, always look around to find the one you need! #### Versions A call version can be specified for each request separately. There are 2 possible ways: * recommended: in the endpoint URL * /vXX where **XX** can only be numeric * as call argument * add version=**XX** argument to API call, **XX** can be a numeric value or a string If requested endpoint does not support the selected version (or the version does not even exist), an error will be thrown. #### Methods The API service supports **GET**, **POST**, **PUT**, **PATCH** and **DELETE** requests. However, not all endpoints implement all of them. Please refer to the endpoint's specification before calling it. #### Arguments !!! warning Do not forget to send *app* and *secret* API credentials. They are **always required**. Endpoints may require or allow to send additional input parameters. These can be sent in: * as REQUEST parameter * as POST data * in the request body ##### Data types * **string** * **string{m,M}** means minimum `m`, maximum `M` characters * **string{N}** is a shorthand for `{N,N}` * **string[email]** means a valid email address (in local-part@domain format, e.g `john@example.com`) * **string[phone]** means a valid phone number (in +prefix number format, e.g `+36 301234567`) * **string[url]** means a valid URL (in protocol://address format, e.g `https://www.example.com`) * **string[json]** means a valid JSON string (e.g `{"key": "value"}`) * **integer**: a whole number, represented as string * **float**: a decimal number, represented as string (with `.` as decimal separator) * **boolean**: `true`/`false` string * **date**: `YYYY-MM-DD` formatted string * **datetime**: `YYYY-MM-DD HH:ii:ss` formatted string, in UTC !!! info JSON-encoded outputs will represent data with their native type (if supported by JSON), instead of strings. For example a boolean will be represented as boolean. ###### Special data types * **list{*type*}**: a list (array) of *type* elements * **hash**: a hash (associative array) of named elements * ***type*/null**: *type* or NULL if not set #### Response codes and output ##### Response API service may **return status, headers and data**: * HTTP response status code * EduBase-API-Error header (with an error description) * EduBase-API-Error-Code header (with an error categorization code, containing only uppercase letters, digits and underscores) * plain text * JSON-encoded text (Content-Type is set to application/json) ##### Possible HTTP response codes * **200**: success * **304**: request was valid, but nothing has changed (the requested values are already set) * **400**: incorrect endpoint is called or request could not be served temporarily * **401**: invalid/missing credentials or assume token is invalid * **403**: endpoint is found, but arguments are not properly set or permission is denied for requested action * **405**: HTTP request method is not supported * **406**: incorrect version is selected, endpoint is not available for current API client application or configured limit has been reached * **429**: too many requests, configured rate limit has been exceeded * **500**: call is valid, but there was an unexpected error while processing it * **501**: endpoint and arguments might be correct, but feature is not yet implemented * **503**: service is temporarily unavailable (e.g maintenance mode is active), try again later !!! bug "Debugging" For debugging purposes EduBase API returns **EduBase-API-Error and EduBase-API-Error-Code headers** in most cases with more detailed error description than the status code. Refer to that message to resolve the issue or contact us for additional help. #### Language Some calls rely on the context language. This defaults to the caller (or assumed) user's language, but can be overridden by sending the **EduBase-Language** header containing the desired language code (in ISO 639-1 format). Invalid or inactive languages will be ignored. #### Rate limiting API applications may be rate limited (calls/second or calls/minute). Please get in touch with us to learn more about active limits. #### Notation Endpoints in this documentation will be formatted the following way: * **endpoint path** * **request method and path** *^^input^^ * mandatory fields are marked with ==[!]== * watch out for data types *^^output^^ * JSON if not specified explicitly * might be empty if an error occured #### Recommended tools For API development and testing we recommend the following applications: * [Insomnia](https://insomnia.rest) * [Postman](https://www.getpostman.com) !!! tip As always, you can also use **curl**! #### Webhooks The EduBase platform also supports **webhooks** to notify external systems about specific events that happen. Webhooks can be configured for organizations, and they can be triggered by various events. You can find more information about webhooks in the [Webhooks documentation](../../webhooks/introduction/), or the [events reference page](../../webhooks/events/). ### Authentication **All endpoints require a valid application ID and secret**. They should be passed as arguments: * **app**: this is the application identification string * **secret**: the application secret string As these are always required, they will not be listed under endpoint inputs. Alternatively, you can use the `Authorization` header with the value `Bearer {app}:{secret}` or dedicated `EduBase-API-App` and `EduBase-API-Secret` headers. !!! warning App ID and secret key should be sent as strings. Not sending these will result in an error. #### Permissions API applications operate in the name of their owner (the registrator). All permissions will be inherited from this EduBase account, meaning that an API call will only succeed if that account has proper permissions set. Also, all operations will be registered to this user. #### Assume user To perform actions as a different user, [request an assume token](../endpoints/users/#userassume) and submit received assume token with each request: * **assume**: received assume token !!! warning Assume tokens are short-lived, you may need to request new tokens periodically. However, it is recommended to revoke assume tokens after performing all steps. #### Testing it It is advised to test basic functionality and correctness of calls before making advanced requests. For this reason, EduBase API service offers a testing endpoint that has no effect on any real data. ``` bash curl -d "app={app}&secret={secret}" https://www.edubase.net/api/test:app curl -H "EduBase-API-App: {app}" -H "EduBase-API-Secret: {secret}" https://www.edubase.net/api/test:app ``` !!! tip If you are developing an integration for a custom EduBase instance, do not forget to replace the domain name! This should return the following (or similar) data, with **HTTP 200** status code: ``` json {"version":"default","language":"en","app":"{app}","user":"{user}","status":true} ``` --- ## API Endpoints ### API: Classes endpoints #### /classes ##### GET /classes !!! abstract "" List owned and managed classes. ^^Input^^: * *string* **search**: optional search string to filter results * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **classes** * *string* **class**: class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *string* **title**: title of the class #### /class ##### GET /class !!! abstract "" Get/check class. ^^Input^^: * ==[!]== *string* **class**: class identification string ^^Output^^: * *string* **class**: class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *string* **title**: title of the class * *datetime/null* **start**: start date and time (if set) * *datetime/null* **end**: end date and time (if set) #### /class:assignments ##### GET /class:assignments !!! abstract "" List all assignments in a class. ^^Input^^: * ==[!]== *string* **class**: class identification string ^^Output^^: * *list* **assignments** * *string* **assignment**: assignment identification string * *string* **title**: title of the assignment * *string* **link**: link to the assignment page * *string* **status**: assignment and submission state * *INACTIVE*: assignment is not active * *ACTIVE*: assignment is currently active and a new submission can be started * *STARTED*: uploaded files but not submitted * *SUBMITTED*: assignment is submitted * *GRADED*: submitted and also graded * *datetime* **starts**: when the assignment submission starts * *datetime* **ends**: when the assignment submission ends #### /class:members ##### GET /class:members !!! abstract "" List all members in a class. ^^Input^^: * ==[!]== *string* **class**: class identification string ^^Output^^: * *list* **members** * *string* **user**: user identification string * *string* **name**: name of the member * *boolean* **active**: active membership (approved and not expired) ##### POST /class:members !!! abstract "" Assign user(s) to a class. Updates memberships if already member of the class. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **users**: comma-separated list of user identification strings * *integer/datetime* **expires**: expiry in days or `YYYY-MM-DD HH:ii:ss` * *boolean* **notify**: notify users * default: false ##### DELETE /class:members !!! danger "" Remove user(s) from a class. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **users**: comma-separated list of user identification strings #### /classes:members ##### POST /classes:members !!! abstract "" Assign user(s) to class(es). Updates memberships if already member of a class. ^^Input^^: * ==[!]== *string* **classes**: comma-separated list of class identification strings * ==[!]== *string* **users**: comma-separated list of user identification strings * *integer/datetime* **expires**: expiry in days or `YYYY-MM-DD HH:ii:ss` * *boolean* **notify**: notify users * default: false #### /user:classes ##### GET /user:classes !!! abstract "" List all classes a user is member of. ^^Input^^: * ==[!]== *string* **user**: user identification string ^^Output^^: * *list* **classes** * *string* **class**: class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *string* **title**: title of the class * *string* **link**: link to the class page * *boolean* **active**: active membership (approved and not expired) ##### POST /user:classes !!! abstract "" Assign user to class(es). Updates membership if already member of a class. ^^Input^^: * ==[!]== *string* **user**: user identification string * ==[!]== *string* **classes**: comma-separated list of class identification strings * *integer/datetime* **expires**: expiry in days or `YYYY-MM-DD HH:ii:ss` * *boolean* **notify**: notify user * default: false ##### DELETE /user:classes !!! danger "" Remove user from class(es). ^^Input^^: * ==[!]== *string* **user**: user identification string * ==[!]== *string* **classes**: comma-separated list of class identification strings --- ### API: Exams endpoints #### /exams ##### GET /exams !!! abstract "" List owned and managed exams. ^^Input^^: * *string* **search**: optional search string to filter results * *boolean/null* **active**: optional filter to only include active exams (if true) or inactive exams (if false) * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **exams** * *string* **exam**: exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *string* **title**: title of the exam * *boolean* **active**: exam is active #### /exam ##### GET /exam !!! abstract "" Get/check exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *string* **title**: title of the exam * *string* **description**: description of the exam (only present if set for the exam) * *string* **language**: language of the exam * *string* **quiz**: Quiz identification string (the Quiz set the exam is attached to) * *boolean* **active**: exam is active * *string* **status**: exam status * *INACTIVE*: exam is inactive but will be activated later * *ACTIVE*: exam is active and new tests can be started * *PAUSED*: exam is active but new tests cannot be started * *REVIEW*: exam is active but already entered review period * *EXPIRED*: exam is inactive * *boolean* **secure**: exam can only be started with the Safe Exam Browser * *datetime* **start**: start date and time * *datetime* **end**: end date and time * *datetime/null* **deadline**: latest date and time the exam can be started at (if set for the exam) * *string[url]* **seb_launch_url**: Safe Exam Browser launch URL (only present if *secure* is true) * *string[url]* **seb_config_url**: Safe Exam Browser configuration file URL (only present if *secure* is true) ##### POST /exam !!! abstract "" Create a new exam from an existing Quiz set. ^^Input^^: * *string* **language**: desired exam language * ==[!]== *string* **title**: title of the exam * *string{1,64}* **id**: external unique exam identifier * *string* **copy_settings**: optional exam identification string to copy settings from * *boolean* **keep_certificate_settings**: whether to keep certificate settings from the copied exam * default: false * *string* **type**: type of the exam * possible values: exam / championship / homework / survey * exam: regular exam * championship: exam with championship features enabled * homework: homework assignment, can be paused and continued during the exam period * survey: survey (optionally anonymous) with no grading * default: exam * ==[!]== *string* **quiz**: the Quiz set (specified using the Quiz identification string) the exam is attached to * *string* **organization**: organization identification string to assign the exam to * only an organization of the API application owner can be used * always the organization of the user for organizational members * ==[!]== *datetime* **start**: exam start time * ==[!]== *datetime* **end**: exam end time ^^Output^^: * *string* **exam**: exam identification string ##### PATCH /exam !!! abstract "" Update the details of an existing exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *string{1,255}* **title**: title of the exam * *string{0,64}* **id**: external unique exam identifier * send an empty value to remove the current identifier * *string* **language**: language of the exam * *string* **description**: description of the exam * basic HTML formatting is kept, scripts are removed * send an empty value to remove the current description * *string* **organization**: organization identification string to move the exam to * only an organization of the API application owner can be used * only the owner of the exam (or an administrator) can change the organization * send *none* (or an empty value) to remove the exam from its current organization * always the organization of the user for organizational members * *datetime* **start**: exam start time * can only be changed until the first result arrives * *datetime* **end**: exam end time * *datetime/string* **deadline**: latest date and time the exam can be started at * should be within the exam period * send *none* (or an empty value) to remove the current deadline ##### DELETE /exam !!! abstract "" Remove/archive exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string #### /exam:settings ##### GET /exam:settings !!! abstract "" Get the settings of an exam. Surveys are never graded and their results cannot be viewed later, so the grading and the results viewing settings are not returned for them. The columns of the results export cannot be configured for surveys either, only the format and the ordering of the exported file. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *boolean* **nonblocking**: exam accounts are exclusively assigned to the exam and cannot be assigned to other exams during the exam period * *boolean* **pausable**: tests can be paused and continued later during the exam period * *boolean* **questionless**: the test has no questions, the examinees complete it right away by starting it (only present if not a survey or championship) * *integer/null* **timelimit**: time limit of the whole test in seconds * *null*: the setting of the Quiz set is used * *0*: overridden, there is no time limit * *integer/null* **roundtime**: time limit of a single question in seconds (turn based Quiz sets only) * *null*: the setting of the Quiz set is used * *0*: overridden, there is no time limit * *boolean* **freeze_round**: the current round of the exam is frozen, no new round can be started * *boolean* **archive**: the users who already have a result can be archived * *string/null* **grading**: grading of the exam * the code of the grading preset in use, *custom* for a manually configured grading, or *none* when grading is disabled * *null*: the setting of the Quiz set is used * *integer* **grading_threshold**: threshold of the grading in percentage (only present if a threshold is configured for the exam) * *string* **view_results**: when the examinees can see their results * *after*: right after the test is submitted, until the end of the exam * *always*: any time during the exam period, the examinees can log back in to see their results * *datetime*: after the test is submitted, and again from the start of the results viewing period * *datetime_blind*: only in the results viewing period, the solutions and the details of the evaluation are hidden right after the test * *manual*: only after the result is published separately, test by test * *none*: never * *datetime/null* **view_results_start**: start of the results viewing period (only present if *view_results* is *datetime* or *datetime_blind*) * *string/null* **view_results_identifier**: label of the custom user data field the examinees can look up their results with (only present if *view_results* is *datetime* or *datetime_blind*) * *string[url]* **results_url**: URL where examinees can look up their own results with the identifier (only present if the identifier based results page is available) * *boolean* **results_page**: the examinees are redirected to the results page after the test * *boolean* **hide_ingame_results**: the results are hidden while the test is taken * *boolean* **hide_points**: the points are hidden during the test and on the results page * *boolean* **hide_grade**: the grade is hidden on the results page * *boolean* **show_in_lasthour**: the results are only shown in the last hour of the exam * *string* **export_format**: format of the exported results file * *csv*: semicolon separated values * *xlsx*: Excel 2007+ workbook * *string* **export_sort**: ordering of the examinees in the exported results file * *default*: the same order they are listed in on the users page of the exam * *name*: by the name of the examinees * *boolean* **export_stats**: the statistics of every question are exported * *boolean* **export_answers**: the answers given by the examinees are exported * *boolean* **export_points**: the points scored on every question are exported * *boolean* **export_suspicion**: the suspicious test taking indicator is exported (never exported when the Suspicion Score is disabled for the exam) * *boolean* **export_finished_only**: only the examinees who finished their test are exported * *boolean* **export_custom_fields**: the user data fields filled in by the examinees are exported (see [GET /exam:fields](#get-examfields)) * when the exam starts a journey and *export_journey* is enabled, the fields requested by the exams along the journey are exported too * *boolean* **export_skills**: the skill results of the examinees are exported (only exported when the Quiz set of the exam has skills) * *boolean* **export_gender**: the gender of the registered examinees is exported * *boolean* **export_journey**: the journey status of the examinees is exported (only exported when the exam starts a journey, see [GET /exam:journey](#get-examjourney)) * *boolean* **export_attendance**: the attendance of the examinees is exported * read only, the attendance is configured together with its source on the interface ##### POST /exam:settings !!! abstract "" Change individual settings of an exam. The columns of the export (every setting from *export_stats* on) cannot be configured for surveys, only the *export_format* and the *export_sort* settings are available for them. The *export_attendance* setting is read only, the attendance is configured together with its source on the interface. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *boolean* **nonblocking**: exam accounts are exclusively assigned to the exam and cannot be assigned to other exams during the exam period * always true for homeworks and surveys * cannot be disabled when the exam already has results, or when some tests are currently paused * *boolean* **pausable**: allow tests to be paused and continued later during the exam period * only available for non-blocking exams, always true for homeworks * cannot be disabled when some tests are currently paused * automatically disabled when *nonblocking* is disabled * *boolean* **questionless**: the questions of the Quiz set are not shown, the examinees complete the test right away by starting it * only available if not a survey or championship * cannot be changed anymore when the current round already has results, until a new round is started (see [POST /exam:round](#post-examround)) * *integer/string* **timelimit**: time limit of the whole test in seconds, overriding the setting of the Quiz set * *0*: there is no time limit * *default*: use the setting of the Quiz set * *integer/string* **roundtime**: time limit of a single question in seconds, overriding the setting of the Quiz set * only available for turn based (TURNS mode) Quiz sets * *0*: there is no time limit * *default*: use the setting of the Quiz set * *string* **grading**: grading of the exam * the code of a grading preset (see [GET /quiz:grading-presets](../quizes/#get-quizgrading-presets)), *none* to disable grading, or *default* to use the setting of the Quiz set * not available for surveys, they are never graded * *number* **grading_threshold**: threshold of the grading in percentage, between 0 and 100 * only available for grading presets with a configurable threshold (of the *go-nogo-custom* type) * can be changed without selecting the grading preset again * *boolean* **freeze_round**: freeze the current round of the exam, so the Quiz set cannot be replaced and no new round can be started * only available for the managers of the exam * *boolean* **archive**: allow archiving the users who already have a result * only available for the managers of the exam * *string* **view_results**: when the examinees can see their results * possible values: after / always / datetime / datetime_blind / manual / none * after: right after the test is submitted, until the end of the exam * always: any time during the exam period, the examinees can log back in to see their results * datetime: after the test is submitted, and again from the start of the results viewing period * datetime_blind: only in the results viewing period, the solutions and the details of the evaluation are hidden right after the test * manual: only after the result is published separately, test by test * none: never * not available for surveys * default: after * *datetime* **view_results_start**: start of the results viewing period * only available with the *datetime* and *datetime_blind* types, where it is mandatory * has to be between the start and the end of the exam * no further tests and no new rounds can be started once the period has begun * *string* **view_results_identifier**: label of the unique free text custom user data field the examinees can look up their results with * only available with the *datetime* and *datetime_blind* types * the field has to be a unique free text (*text*, *email* or *phone*) custom field of the exam, see [GET /exam:fields](#get-examfields) * send *none* (or an empty value) to disable the identifier based results page * *boolean* **results_page**: redirect the examinees to the results page after the test * can only be disabled with the *datetime_blind*, *manual* and *none* types * *boolean* **hide_ingame_results**: hide the results while the test is taken * *boolean* **hide_points**: hide the points during the test and on the results page * always enabled with the *none* type * *boolean* **hide_grade**: hide the grade on the results page * can only be enabled together with *hide_points* * always enabled with the *none* type * *boolean* **show_in_lasthour**: only show the results in the last hour of the exam * only available with the *after* type * *string* **export_format**: format of the exported results file * possible values: csv / xlsx * csv: semicolon separated values * xlsx: Excel 2007+ workbook * default: xlsx * *string* **export_sort**: ordering of the examinees in the exported results file * possible values: default / name * default: the same order they are listed in on the users page of the exam * name: by the name of the examinees * default: default * *boolean* **export_stats**: export the statistics of every question * default: false * *boolean* **export_answers**: export the answers given by the examinees * default: false * *boolean* **export_points**: export the points scored on every question * default: false * *boolean* **export_suspicion**: export the suspicious test taking indicator * never exported when the Suspicion Score is disabled in the settings of the exam * default: false * *boolean* **export_finished_only**: only export the examinees who finished their test * default: false * *boolean* **export_custom_fields**: export the user data fields filled in by the examinees (see [GET /exam:fields](#get-examfields)) * when the exam starts a journey and *export_journey* is enabled, the fields requested by the exams along the journey are exported too (fields with the same label share a column, with the value given last) * default: true * *boolean* **export_skills**: export the skill results of the examinees * only exported when the Quiz set of the exam has skills * default: true * *boolean* **export_gender**: export the gender of the registered examinees * needs special privileges to enable * default: false * *boolean* **export_journey**: export the journey status of the examinees * only exported when the exam starts a journey, see [GET /exam:journey](#get-examjourney) * default: true ^^Output^^: The updated settings of the exam are returned, the same way as with [GET /exam:settings](#get-examsettings). ##### PUT /exam:settings !!! abstract "" Replace the complete configuration of an exam with the configuration of another exam. Exam specific data and state (automatic account generation, rounds, notifications, login settings and previously used patterns) is never copied. Branding, and the attached learning materials (course, SCORM package and video) of the target exam are kept as well. The *questionless* setting is only copied if not a survey or championship, and it is kept unchanged while the current round of the target exam already has results. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the target exam) * ==[!]== *string* **source**: exam identification string to copy the settings from * must be different from the target exam * *boolean* **keep_certificate_settings**: whether to copy the certificate settings as well * default: false ^^Output^^: * *string* **exam**: exam identification string * *string* **source**: exam identification string the settings were copied from ##### PATCH /exam:settings !!! abstract "" Copy only those settings from another exam that are not configured yet in the target exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the target exam) * ==[!]== *string* **source**: exam identification string to copy the settings from * must be different from the target exam * *boolean* **keep_certificate_settings**: whether to copy the certificate settings as well * default: false ^^Output^^: * *string* **exam**: exam identification string * *string* **source**: exam identification string the settings were copied from #### /exam:fields ##### GET /exam:fields !!! abstract "" Get the user data fields of an exam. These are the built-in name, email address and phone number fields, and the custom fields the examinees fill in before they start their test. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *boolean/string* **name**: the full name of the examinee is requested * *false*: the field is not used * *true*: the field is used with its default label * otherwise the custom label of the field * *boolean/string* **email**: the email address of the examinee is requested * *false*: the field is not used * *true*: the field is used with its default label * otherwise the custom label of the field * *boolean/string* **phone**: the phone number of the examinee is requested * *false*: the field is not used * *true*: the field is used with its default label * otherwise the custom label of the field * *list* **fields**: the custom fields of the exam * *string* **label**: label of the field, shown to the examinee * *string* **type**: type of the field * *boolean* **required**: the field has to be filled in * *string* **description**: description shown under the field (if set) * *string* **icon**: Font Awesome icon class name of the field (if set) * *list{string}* **options**: the selectable options (*select* fields only) * *object* **filtering**: short labels the examinees are grouped and filtered with in the exam manager and the reports, keyed by option (*select* fields only, if set) * *number* **from**: lowest selectable value (*from-to* fields only) * *number* **to**: highest selectable value (*from-to* fields only) * *number* **step**: difference between the selectable values (*from-to* fields only, if set) * *string* **min**: earliest allowed value (*date* and *datetime* fields only, if set) * *now*: the current date (or date and time) * otherwise a fixed date as YYYY-MM-DD (*date* fields), or a date and time as YYYY-MM-DD HH:MM in UTC (*datetime* fields) * *string* **max**: latest allowed value (*date* and *datetime* fields only, if set), the same way as *min* * *integer* **minlength**: minimum length of the value (free text fields only, if set) * *integer* **maxlength**: maximum length of the value (free text fields only, if set) * *string* **pattern**: regular expression the value has to match (free text fields only, if set) * *boolean* **unique**: the value has to be unique within the exam (free text fields only, if set) ##### POST /exam:fields !!! abstract "" Change the built-in user data fields of an exam, and replace the complete list of its custom fields. The *view_results_identifier* setting of the exam is cleared when the selected custom field is not a unique free text field anymore. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *boolean/string{1,255}* **name**: request the full name of the examinee * *true*: use the field with its default label * *false*: do not use the field, its other settings are kept * any other value is used as the label of the field * *boolean/string{1,255}* **email**: request the email address of the examinee * *true*: use the field with its default label * *false*: do not use the field, its other settings are kept * any other value is used as the label of the field * *boolean/string{1,255}* **phone**: request the phone number of the examinee * *true*: use the field with its default label * *false*: do not use the field, its other settings are kept * any other value is used as the label of the field * *list* **fields**: the custom fields of the exam, in the order they are shown to the examinee * the submitted list replaces the current one, so send every custom field that should be kept * ==[!]== *string* **label**: label of the field, shown to the examinee * the label identifies the field, so it has to be unique within the exam * the settings of the previous field with the same label are used as the defaults * *string* **type**: type of the field * possible values: text / email / phone / select / number / from-to / date / datetime / yes-no / yes / no / true-false / gender * text: free text * email: email address * phone: phone number * select: one of the given options * number: number * from-to: number selected from a range * date: calendar day, stored as YYYY-MM-DD * datetime: date and time, entered in the timezone of the examinee and stored as YYYY-MM-DD HH:MM in UTC * yes-no: yes or no * yes: yes only, the examinee has to accept it * no: no only * true-false: true or false * gender: male, female or other * default: text * *boolean* **required**: the field has to be filled in * default: true * *string* **description**: description shown under the field * *string* **icon**: Font Awesome icon class name of the field * format: *fa-{style} fa-{name}*, where style is solid, regular, light, thin or brands * example: *fa-regular fa-id-card* * *list{string}* **options**: the selectable options * mandatory for *select* fields, empty options are dropped * *object* **filtering**: short labels the examinees are grouped and filtered with in the exam manager and the reports, keyed by option * only used for *select* fields, and only the options of the field can be used as keys * the previously stored labels are kept when it is not specified * *number* **from**: lowest selectable value * mandatory for *from-to* fields, has to be lower than *to* * *number* **to**: highest selectable value * mandatory for *from-to* fields * *number* **step**: difference between the selectable values * only used for *from-to* fields, and it cannot be 0 * *string* **min**: earliest allowed value * only used for *date* and *datetime* fields * *now* for the current date (or date and time) when the field is filled in, or a fixed value: YYYY-MM-DD for *date* fields, YYYY-MM-DD HH:MM in UTC for *datetime* fields * *string* **max**: latest allowed value * only used for *date* and *datetime* fields, the same way as *min* * cannot be earlier than a fixed *min* * *integer* **minlength**: minimum length of the value, between 1 and 255 * only used for the free text (*text*, *email* and *phone*) fields * *integer* **maxlength**: maximum length of the value, between 1 and 255 * only used for the free text (*text*, *email* and *phone*) fields * *string{1,255}* **pattern**: regular expression the value has to match * only used for the free text (*text*, *email* and *phone*) fields * has to be a valid expression, it is matched against the whole value * needs special privileges to set, the previously stored pattern is kept for everyone else * *boolean* **unique**: require the value to be unique within the exam * only used for the free text (*text*, *email* and *phone*) fields * default: false ^^Output^^: The new fields of the exam are returned, the same way as with [GET /exam:fields](#get-examfields). ##### DELETE /exam:fields !!! danger "" Remove every custom field of an exam. The built-in name, email address and phone number fields are kept, use the *POST* method to turn those off. The values the examinees already entered are kept, but they are not shown anymore. The *view_results_identifier* setting of the exam is cleared as well. ^^Input^^: * ==[!]== *string* **exam**: exam identification string #### /exam:status ##### GET /exam:status !!! abstract "" Get the status of an exam, showing whether new tests can be started. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *boolean* **active**: exam is active * *boolean* **status**: new tests can be started * *datetime/null* **modified**: date and time the status was changed at * *datetime/null* **scheduled**: date and time starting new tests is automatically disabled at ##### POST /exam:status !!! abstract "" Enable or disable starting new tests on an exam. The exam itself stays active, only the start of further tests is controlled. The status can only be changed while the exam is active and has not closed yet. New tests cannot be started in the results viewing period. Disabling new tests also cancels the scheduled automatic disabling of the exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *boolean* **status**: allow new tests to be started ^^Output^^: The new status of the exam is returned, the same way as with [GET /exam:status](#get-examstatus). #### /exam:round ##### GET /exam:round !!! abstract "" Get the current round of an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *integer* **round**: index of the current round * *datetime/null* **started**: date and time the current round was started at * *boolean* **results**: the current round already has results * *boolean* **frozen**: the current round is frozen, no new round can be started ##### POST /exam:round !!! danger "" Start a new round of the exam. The running tests are closed, the results of the previous round are detached from the exam accounts and the generated accounts are reset, so the exam can be taken again by another group of examinees. A new round cannot be started when the exam is locked, archived, the current round is frozen (see the *freeze_round* setting of [POST /exam:settings](#post-examsettings)), or the results viewing period of the exam has already begun (see the *view_results_start* setting of [POST /exam:settings](#post-examsettings)). ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *boolean* **force**: start a new round even if the current one has no results yet * default: false * *boolean* **notify**: notify the assigned users about the exam * default: false ^^Output^^: The new round of the exam is returned, the same way as with [GET /exam:round](#get-examround). #### /exam:skills ##### GET /exam:skills !!! abstract "" Get skills defined in the Quiz set used by an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *list* **skills** * *string* **identifier**: skill identifier * *string* **title**: title of the skill * *string* **description**: skill description (if set) #### /exam:branding ##### GET /exam:branding !!! abstract "" Get exam branding configuration. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *boolean* **enabled**: whether branding is configured and enabled * *string* **type**: type of branding image * possible values: foreground / background * foreground: image is used as a logo * background: image is used as a cover * only present if branding is enabled * *string* **color**: branding color (only present if branding is enabled) ##### POST /exam:branding !!! abstract "" Configure or update exam branding. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *string* **type**: branding image type * possible values: foreground / background * default: foreground * ==[!]== *string* **image**: branding image * a URL, base64-encoded data (optionally as a data URL), or the identifier of an image uploaded to the filebin * supported formats: PNG, JPEG, WebP * ==[!]== *string* **color**: branding color * possible values: branding / red / blue / yellow / green / purple / gray ##### DELETE /exam:branding !!! danger "" Remove branding from an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string #### /exam:certificates ##### GET /exam:certificates !!! abstract "" Get the certificate settings of an exam. Certificates are only issued for the successful results, so they need a grading that decides whether the test was successful (see the *certificates* flag in [GET /quiz:grading-presets](../quizes/#get-quizgrading-presets)). Only the state of the certificates is returned when they are not enabled for the exam. When the certificates are configured by a preset, the settings of the preset are merged into the returned configuration. The certificates issued for the individual results can be looked up with [GET /exam:certificates:user](../plays/#get-examcertificatesuser). ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *boolean* **enabled**: certificates are issued for the exam * the configuration itself is only returned when the certificates are enabled * *boolean* **configured**: the certificates are configured, so they can be issued * *boolean* **supervisors_only**: only the supervisors can issue the certificates, the examinees cannot get their own ones * *boolean* **email_send**: the certificates are sent to the examinees in email automatically * *string/null* **preset**: identification string of the certificate preset configuring the certificates (see [GET /exam:certificates:presets](#get-examcertificatespresets)) * *null*: the certificates are configured manually * *string* **language**: language of the certificates * *string* **title**: title of the certificates * *string* **text1**: text displayed under the data of the examinee * the multi line texts (*text1*, *text2* and *notes*) are returned with basic HTML formatting and line breaks * *string* **text2**: text displayed under the results, above the date of the test * *string* **text_date**: text displayed before the date of the test * *string* **notes**: notes displayed at the bottom of the certificates * *boolean* **show_result**: the result of the test is displayed * *boolean* **show_serial**: the certificates get a serial number * *boolean* **show_birthdate_birthplace**: the birth date and the birth place of the examinee are displayed * *boolean* **show_custom**: custom user data is displayed * *boolean* **show_expiration**: the certificates expire and their validity is displayed * *boolean* **show_signature**: signatures are displayed * *boolean* **show_custom_branding**: custom branding (logo and organization name) is displayed * *boolean* **hide_timezone**: the timezone information is hidden * *boolean* **download_automatically**: the certificate is downloaded automatically when the examinee opens the results page * *number/null* **grading_threshold**: threshold of the certificates in percentage, overriding the threshold of the grading * *null*: the threshold of the grading is used * *string/null* **timezone**: timezone of the dates on the certificates * *null*: the timezone of the exam is used * *string[]* **custom_fields**: custom user data displayed as extra data (only present if *show_custom* is enabled) * the custom user data fields of the exam are identified by their label (see [GET /exam:fields](#get-examfields)), the data fields of the user accounts by their *customer_* prefixed name * *string[]* **custom_user_fields**: custom user data displayed together with the data of the examinee (only present if *show_custom* is enabled) * *string* **result_format**: format of the displayed result (only present if *show_result* is enabled) * possible values: points-percent-time / points-percent / points / percent * *string* **serial_format**: format of the serial numbers (only present if *show_serial* is enabled) * possible values: prefix-year-index / prefix-date-index / prefix-index * *string* **serial_separator**: separator between the parts of the serial numbers (only present if *show_serial* is enabled) * *string* **serial_prefix**: prefix of the serial numbers (only present if *show_serial* is enabled) * *integer* **serial_index_digits**: minimum length of the index in the serial numbers (only present if *show_serial* is enabled) * *integer* **expiration**: validity period of the certificates in days (only present if *show_expiration* is enabled) * *boolean/integer* **renew**: whether the certificates can be renewed (only present if *show_expiration* is enabled) * *false*: the certificates cannot be renewed * *true*: the certificates can be renewed any time * *integer*: the number of days the renewal is available from, relative to the expiry (*0* is the day of the expiry, negative values open the renewal before it) * *boolean* **manual_renewal**: the renewal has to be approved by a supervisor (only present if *show_expiration* is enabled) * *boolean* **automatic_renewal_approval**: the renewal is approved automatically, but it stays revocable (only present if *show_expiration* is enabled) * *string[]* **expiration_notification_emails**: email addresses notified about the expiring certificates (only present if *show_expiration* is enabled) * *boolean* **signature_user**: a signature line is displayed for the examinee (only present if *show_signature* is enabled) * *boolean* **signature_left**: the signature of the first organizer is displayed (only present if *show_signature* is enabled) * *string* **signature_left_line1**: name of the first organizer (only present if *show_signature* is enabled) * *string* **signature_left_line2**: second line of the first signature (only present if *show_signature* is enabled) * *string* **signature_left_line3**: third line of the first signature (only present if *show_signature* is enabled) * *string* **signature_left_line4**: fourth line of the first signature (only present if *show_signature* is enabled) * *boolean* **signature_left_image**: the uploaded image of the first signature is displayed (only present if *show_signature* is enabled) * *boolean* **signature_right**: the signature of the second organizer is displayed (only present if *show_signature* is enabled) * *string* **signature_right_line1**: name of the second organizer (only present if *show_signature* is enabled) * *string* **signature_right_line2**: second line of the second signature (only present if *show_signature* is enabled) * *string* **signature_right_line3**: third line of the second signature (only present if *show_signature* is enabled) * *string* **signature_right_line4**: fourth line of the second signature (only present if *show_signature* is enabled) * *boolean* **signature_right_image**: the uploaded image of the second signature is displayed (only present if *show_signature* is enabled) * *boolean* **signature_stamp**: the uploaded stamp is displayed between the signatures (only present if *show_signature* is enabled) * *string* **branding_line1**: name of the organization (only present if *show_custom_branding* is enabled) * *string* **branding_line2**: second line of the branding (only present if *show_custom_branding* is enabled) * *string* **branding_line3**: third line of the branding (only present if *show_custom_branding* is enabled) * *string* **branding_line4**: fourth line of the branding (only present if *show_custom_branding* is enabled) * *boolean* **branding_image**: the uploaded logo is displayed (only present if *show_custom_branding* is enabled) * *boolean* **branding_edubase**: the EduBase logo is displayed (only present if *show_custom_branding* is enabled) ##### POST /exam:certificates !!! abstract "" Change individual certificate settings of an exam. Every setting other than *enabled* requires the certificates to be enabled for the exam, and the items displayed on the certificates have to be switched on before their own settings can be configured (for example *show_serial* before the *serial_* settings). The certificates configured by a preset can only be replaced completely, see [PUT /exam:certificates](#put-examcertificates). The settings cannot be changed while the exam is locked. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *boolean* **enabled**: issue certificates for the exam * needs the certificate feature to be enabled for the user * not available for surveys, and needs a grading that decides whether the test was successful (see the *certificates* flag in [GET /quiz:grading-presets](../quizes/#get-quizgrading-presets)) * the configuration is kept when the certificates are disabled * *boolean* **supervisors_only**: only allow the supervisors to issue the certificates, so the examinees cannot get their own ones * *boolean* **email_send**: send the certificates to the examinees in email automatically * needs the certificate email sending feature to be enabled on the instance * *string* **language**: language of the certificates * only the languages EduBase is translated to are available * default: language of the exam * *string{1,80}* **title**: title of the certificates * certificates are only issued once they have a title * *string{0,320}* **text1**: text displayed under the data of the examinee * the multi line texts (*text1*, *text2* and *notes*) support basic formatting (bold, italic, underline, subscript, superscript) and line breaks * *string{0,1280}* **text2**: text displayed under the results, above the date of the test * *string{0,80}* **text_date**: text displayed before the date of the test * *string{0,320}* **notes**: notes displayed at the bottom of the certificates * *boolean* **show_result**: display the result of the test * *boolean* **show_serial**: give a serial number to the certificates * *boolean* **show_birthdate_birthplace**: display the birth date and the birth place of the examinee * *boolean* **show_custom**: display custom user data * *boolean* **show_expiration**: expire the certificates and display their validity * *boolean* **show_signature**: display signatures * *boolean* **show_custom_branding**: display custom branding (logo and organization name) * *boolean* **hide_timezone**: hide the timezone information * *boolean* **download_automatically**: download the certificate automatically when the examinee opens the results page * *number/string* **grading_threshold**: threshold of the certificates in percentage, between 0 and 100 * it overrides the threshold of the grading, certificates are only issued above it * *default*: use the threshold of the grading * *string* **timezone**: timezone of the dates on the certificates * *default*: use the timezone of the exam * *string[]* **custom_fields**: custom user data to display as extra data * requires *show_custom*, at most three fields can be displayed * the custom user data fields of the exam are identified by their label (see [GET /exam:fields](#get-examfields)), the data fields of the user accounts by their *customer_* prefixed name * send an empty list to remove every field * *string[]* **custom_user_fields**: custom user data to display together with the data of the examinee * the same way as with *custom_fields* * *string* **result_format**: format of the displayed result * requires *show_result* * possible values: points-percent-time / points-percent / points / percent * points-percent-time: points, result in percentage and the time spent with the test * points-percent: points and result in percentage * points: points only * percent: result in percentage only * default: points-percent-time * *string* **serial_format**: format of the serial numbers * requires *show_serial*, and it is mandatory when the serial numbers are configured for the first time * possible values: prefix-year-index / prefix-date-index / prefix-index * prefix-year-index: prefix, the year of the test and the index * prefix-date-index: prefix, the date of the test and the index * prefix-index: prefix and index, written together * none of the *serial_* settings can be changed anymore once a certificate is issued for the exam * *string* **serial_separator**: separator between the parts of the serial numbers * requires *show_serial* * possible values: a slash (/), a dash (-) or an empty value * always empty with the *prefix-index* format * default: slash * *string{0,16}* **serial_prefix**: prefix of the serial numbers * requires *show_serial* * the serial numbers are only unique within the exam, so a unique prefix is recommended * *integer* **serial_index_digits**: minimum length of the index in the serial numbers, between 1 and 16 * requires *show_serial* * default: 4 * *integer* **expiration**: validity period of the certificates in days, between 0 and 9999 * requires *show_expiration*, and it is mandatory when the expiration is configured for the first time * *boolean/integer* **renew**: whether the certificates can be renewed * requires *show_expiration* * *false*: the certificates cannot be renewed * *true*: the certificates can be renewed any time * *integer*: the number of days the renewal is available from, relative to the expiry (*0* is the day of the expiry, negative values open the renewal before it, at most the validity period earlier) * default: false * *boolean* **manual_renewal**: the renewal has to be approved by a supervisor * requires *renew* * *boolean* **automatic_renewal_approval**: approve the renewal automatically, but keep it revocable * requires *manual_renewal* * *string[]* **expiration_notification_emails**: email addresses to notify about the expiring certificates * requires *show_expiration* * the examinees of the exam are notified automatically about their renewable certificates, these notifications are sent to the specified addresses only * send an empty list to remove every address * *boolean* **signature_user**: display a signature line for the examinee * requires *show_signature* * *boolean* **signature_left**: display the signature of the first organizer * requires *show_signature* * the lines of the signature are kept when it is disabled * *string{1,40}* **signature_left_line1**: name of the first organizer * mandatory when the signature is displayed * *string{0,50}* **signature_left_line2**: second line of the first signature * *string{0,50}* **signature_left_line3**: third line of the first signature * *string{0,50}* **signature_left_line4**: fourth line of the first signature * *boolean/string* **signature_left_image**: image of the first signature * a new image replaces the current one: a URL, base64-encoded data (optionally as a data URL), or the identifier of an image uploaded to the filebin * supported formats: JPEG, WebP, PNG * the white background of the signatures and the stamp is removed automatically, but transparent PNG images are recommended * *true*: display the image that is already uploaded * *false*: keep the uploaded image, but do not display it * *boolean* **signature_right**: display the signature of the second organizer * the same way as with *signature_left* * *string{1,40}* **signature_right_line1**: name of the second organizer * mandatory when the signature is displayed * *string{0,50}* **signature_right_line2**: second line of the second signature * *string{0,50}* **signature_right_line3**: third line of the second signature * *string{0,50}* **signature_right_line4**: fourth line of the second signature * *boolean/string* **signature_right_image**: image of the second signature * the same way as with *signature_left_image* * *boolean/string* **signature_stamp**: stamp displayed between the signatures * the same way as with *signature_left_image* * *string{1,80}* **branding_line1**: name of the organization * requires *show_custom_branding*, and it is mandatory when the branding is displayed * *string{0,80}* **branding_line2**: second line of the branding * *string{0,80}* **branding_line3**: third line of the branding * *string{0,80}* **branding_line4**: fourth line of the branding * *boolean/string* **branding_image**: logo of the branding * the same way as with *signature_left_image* * *boolean* **branding_edubase**: display the EduBase logo * it can only be removed with the matching feature enabled ^^Output^^: The updated certificate settings of the exam are returned, the same way as with [GET /exam:certificates](#get-examcertificates). ##### PUT /exam:certificates !!! abstract "" Replace the complete certificate configuration of an exam with a preset or with the configuration of another exam. The certificates have to be enabled for the exam, and its settings cannot be changed while it is locked. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the target exam) * *string* **preset**: certificate preset identification string (see [GET /exam:certificates:presets](#get-examcertificatespresets)) * the settings of the preset are applied to the exam, and they are kept up to date with the preset * *string* **source**: exam identification string to copy the certificate configuration from * must be different from the target exam, and its certificates have to be configured manually (not by a preset) * the images (signatures, stamp and logo) are copied as well ^^Output^^: The updated certificate settings of the exam are returned, the same way as with [GET /exam:certificates](#get-examcertificates). ##### DELETE /exam:certificates !!! danger "" Stop issuing certificates for an exam. The configuration itself is kept, so the certificates can be enabled again later with [POST /exam:certificates](#post-examcertificates). The already issued certificates are not revoked. The settings cannot be changed while the exam is locked. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: The updated certificate settings of the exam are returned, the same way as with [GET /exam:certificates](#get-examcertificates). #### /exam:certificates:presets ##### GET /exam:certificates:presets !!! abstract "" List the certificate presets that can be used on exams. The public presets, the owned presets and the presets of the organizations of the user are returned. ^^Input^^: * *string* **exam**: exam identification string * only the presets that can be used on this exam are listed * the language of the exam is used for the ordering of the presets * *string* **language**: optional language to filter the results with ^^Output^^: * *string* **exam**: exam identification string (only present if an exam is specified) * *list* **presets** * *string* **preset**: certificate preset identification string * *string* **title**: title of the certificate preset * *string* **language**: language of the certificate preset * *boolean* **public**: the preset is a public preset available for everyone * *boolean* **own**: the preset is a custom preset owned by the user * *string/null* **organization**: organization identification string of the preset * *null*: the preset is not bound to an organization * *boolean* **current**: the certificates of the exam are configured by this preset #### /exam:users ##### GET /exam:users !!! abstract "" List all users on an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *list* **users** * *string* **user**: user identification string * *string* **name**: name of the examinee ##### POST /exam:users !!! abstract "" Assign user(s) to an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **users**: comma-separated list of user identification strings ##### DELETE /exam:users !!! danger "" Remove user(s) from an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **users**: comma-separated list of user identification strings #### /exam:users:generate ##### POST /exam:users:generate !!! abstract "" Generate exam accounts from patterns and assign them to an exam. !!! info "Patterns" The following placeholders can be used in the patterns: * *{index}*: the index of the generated account * *{random:N}*: a random alphanumeric string of *N* characters (unambiguous characters only in passwords) * *{number:N}*: a random numeric string of *N* characters * *{name}* and *{Name}*: the name of the account (only when *names* is specified), as given and capitalized * *{username}*: the generated username (in the *name* pattern only) Patterns are stored with the exam and reused as defaults for the next call. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *string{1,64}* **username**: username pattern * defaults to the pattern used previously for this exam * mandatory if the exam has no previously used pattern * *string{1,64}* **password**: password pattern * defaults to the pattern used previously for this exam * mandatory if the exam has no previously used pattern * *string{1,128}* **name**: full name pattern * defaults to the pattern used previously for this exam * mandatory if the exam has no previously used pattern * *list{string}/string* **names**: list of names to generate the accounts for * either a list, or a comma or newline separated string * the number of accounts generated is the number of names specified, *count* is ignored * *list{string}/string* **emails**: list of email addresses for the generated accounts * either a list, or a comma or newline separated string * only used together with *names*, the number of items must match the number of names * invalid addresses are replaced with a generated one * *integer* **count**: number of accounts to generate * between 1 and 500 * ignored when *names* is specified * default: 1 * *integer* **start**: index to start the generation at * default: the index after the previously generated accounts (1 for the first call) ^^Output^^: * *string* **exam**: exam identification string * *list* **users** * *string* **user**: user identification string * *string* **username**: username of the generated account * *string* **name**: full name of the generated account * *string/null* **password**: password of the generated account (if available) * *list{string}* **failed**: usernames of the accounts that could not be created (only present if there are any) * *list{string}* **duplicate**: usernames that were skipped because the same account was being generated by a parallel request at the same time (only present if there are any) #### /exam:autologin ##### GET /exam:autologin !!! abstract "" Get the automatic login configuration of an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string ^^Output^^: * *string* **exam**: exam identification string * *boolean* **autologin**: automatic login is enabled * *string* **pin**: PIN code of the exam (only present if *autologin* is true and a PIN code is set) * *string[url]* **url**: login URL of the exam * only present if *autologin* is true * the Safe Exam Browser launch URL will be used if the exam is secure, otherwise the shortlink will be used if it is set, and the standard login URL will be used if neither of the two is available * *string[url]* **results_url**: URL where examinees can look up their own results (only present if *autologin* is true and results can be viewed with an identifier) * *boolean* **autoadd**: new exam accounts are generated automatically on demand (only present if *autologin* is true) * *boolean* **autojoin**: already registered users can join the exam automatically (only present if *autologin* is true) * *boolean* **autojoin_limited**: automatic joining is limited to the assigned users (only present if *autojoin* is true) ##### POST /exam:autologin !!! abstract "" Enable or reconfigure automatic login for an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *string* **se**: shortlink for the login URL * at least 3 characters long * a + sign will be prefixed automatically, and the shortlink is case-insensitive * send an empty value to remove the current shortlink * needs the shortlink feature to be enabled, and cannot be used with secure (Safe Exam Browser) exams * *boolean* **autoadd**: generate new exam accounts automatically on demand * needs special privileges to set! * *boolean* **autojoin**: allow already registered users to join the exam automatically * needs special privileges to set! * *boolean* **autojoin_limited**: limit automatic joining to the users assigned to the exam * only used when *autojoin* is enabled * needs special privileges to set! * default: false ^^Output^^: The automatic login configuration of the exam is returned, the same way as with [GET /exam:autologin](#get-examautologin). ##### DELETE /exam:autologin !!! danger "" Disable automatic login for an exam. The PIN code and the shortlink of the exam are removed as well. ^^Input^^: * ==[!]== *string* **exam**: exam identification string #### /exam:journey ##### GET /exam:journey !!! abstract "" Get the status of the journey started by an exam: the number of users at the steps, their statuses, and the outcomes of the finished journeys. Only the organizational members visible for the current user are counted. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the starting exam) ^^Output^^: * *string* **exam**: exam identification string * *string* **journey**: title of the journey (the title of the exam, unless a custom title is set) * *boolean* **active**: the journey is enabled, users enter it when they start the exam * *datetime/null* **published**: date and time the journey was last published at * *null*: the journey is not published yet * *integer* **users**: number of users on the journey * *hash* **status**: number of users by their status * *integer* **active**: on a step, working on its content * *integer* **waiting**: waiting before they can continue (including the users waiting for an approval at a *gate* step) * *integer* **error**: stuck on a step that cannot be completed (e.g. its content was removed) * *integer* **completed**: finished the journey * *integer* **stopped**: their journey was stopped by an educator * *list* **outcomes**: outcomes of the finished (completed or stopped) journeys * *string/null* **outcome**: the outcome * *integer* **users**: number of users finishing with the outcome * *list* **steps**: steps of the journey (every node except the decisions and actions), in the order of their markers * *string* **step**: key of the step (node) * *string* **marker**: marker letter of the step (A, B, ..., Z, AA, ...), ordered by their position on the canvas * *string* **type**: type of the step * possible values: start / exam / class / quiz / bundle / wait / gate / end * *string* **title**: title of the step (the title of its content, unless a custom label is set) * *hash/null* **content**: content of the step (the starting exam for the *start* step, null if not selected or not available anymore, always null for *bundle* steps) * *string* **type**: type of the content * possible values: exam / class / quiz * *string* **exam** / **class** / **quiz**: identification string of the content (keyed by its type) * *string/null* **id**: external unique identifier of the content (if set) * *string* **title**: title of the content * *list* **contents**: contents of the bundle, the same way as *content* (only present for *bundle* steps, the contents not available anymore are left out) * *string* **outcome**: outcome given by the step (only present for *end* steps) * *integer* **users**: number of users currently at the step * *hash* **status**: number of users currently at the step by their status (the same way as above) * *integer* **passed_users**: number of users who already passed the step (not present for *end* steps) * *integer* **passed_visits**: number of times the step was passed, including the repeated visits (not present for *end* steps) * *float/null* **result_average**: average result of the completed step in percentage (not present for *end* steps) * *integer/null* **time_spent_average**: average time spent at the step in seconds (not present for *end* steps) #### /exam:journey:user ##### GET /exam:journey:user !!! abstract "" Get the journey of a user started by an exam: the current step, the history of the visited steps and the manual changes made by the educators. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the starting exam) * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **exam**: exam identification string * *string* **journey**: title of the journey * *string* **user**: user identification string * *string/null* **status**: status of the journey of the user * possible values: active / waiting / error / completed / stopped * *null*: the user has not entered the journey yet (users enter the journey by starting the exam), no other fields are returned * *string/null* **outcome**: outcome of the journey (only set if *status* is *completed* or *stopped*) * *string* **error**: code of the error (only present if *status* is *error*) * possible values: content-invalid / content-archived / content-locked / content-closed / class-not-progressive / class-membership-missing / class-membership-expired / attempts-exhausted / enroll-failed / error * *integer* **run**: index of the current run of the journey (increased when the journey is restarted) * *hash/null* **step**: the current step of the user (null if the journey is finished) * the same fields as the items of *steps* of [GET /exam:journey](#get-examjourney), without the user counts * *datetime* **time_start**: date and time the step was entered at * *datetime* **wait_until**: date and time the waiting ends at (only present if *status* is *waiting* and the end is known) * *boolean* **awaiting_approval**: the user waits for an educator to approve or decline them at a *gate* step (only present if true) * *datetime* **deadline**: date and time the deadline of the current step ends at (only present if *status* is *active* and the step has a deadline) * *hash* **bundle**: progress of the user at the current *bundle* step (only present if *status* is *active* and the user is at a *bundle* step) * *integer* **required**: number of contents to complete * *integer* **completed**: number of contents already completed * *list* **items**: contents of the bundle, the same way as *content* of the steps, together with: * *boolean* **completed**: the content is already completed * *datetime* **time_start**: date and time the user entered the journey at * *datetime/null* **time_end**: date and time the user finished the journey at * *integer* **steps_completed**: number of the different exam, class, Quiz set and bundle steps completed in the current run (the start step included, the steps left by their deadline are not counted) * *integer* **steps_total**: number of the exam, class, Quiz set and bundle steps of the journey (the start step included) * *integer/null* **steps_remaining**: estimated number of steps still to be completed on the shortest way to the end * a lower bound, the decisions may lead to a longer path * *null*: the journey is not running, or no end can be reached from the current step * *hash* **flags**: flags and counters set for the user by the *flag* and *counter* actions of the journey, keyed by their name (only present if there are any) * *list* **history**: the visited steps of every run, in the order they were entered * *integer* **run**: index of the run * *integer* **visit**: index of the visit of the step within the run * *string* **step**, **marker**, **type**, **title**, **content**, **contents**, **outcome**: the step, the same way as with [GET /exam:journey](#get-examjourney) * *string* **status**: status of the visit * possible values: active / waiting / completed / expired / moved / stopped / error * *expired*: the step was left on its *timeout* output, because it was not completed before its deadline * *datetime* **time_start**: date and time the step was entered at * *datetime/null* **time_end**: date and time the step was left at * *integer/null* **time_spent**: time spent taking the test of the step in minutes * *float* **result**: result in percentage (only present if available) * *boolean* **successful**: the test was successful (only present if available) * *string* **grade**: grade of the test (only present if available) * *string* **play**: Quiz play identification string of the test completing the step (only present if available) * *hash* **skills**: skill results of the test, keyed by the skill identifier (only present if available) * *boolean* **timeout**: the step was left because its deadline passed (only present if true) * *list* **items**: the completed contents of a *bundle* step (only present for bundles with completed contents) * the same fields as *content* of the steps, together with: * *float* **result**: result in percentage (only present if available) * *boolean* **successful**: the test was successful (only present if available) * *string* **grade**: grade of the test (only present if available) * *string* **play**: Quiz play identification string of the test completing the content (only present if available) * *datetime* **time_completed**: date and time the content was completed at (only present if available) * *string* **approval**: decision made at a *gate* step (only present for decided approvals) * possible values: approved / declined * *string/null* **operator**: user identification string of the educator making the decision at a *gate* step (only present for decided approvals) * *list* **decisions**: decisions and actions passed after the step, with the outputs taken (only present if there are any) * *string* **decision**: key of the decision or action node * *string/null* **type**: type of the node * possible values: condition / split / notify / flag / counter / mark * the *organizer* nodes are never listed, they only shape the edges * *string/null* **title**: title of the node * *string/null* **output**: output taken (the key of a branch, *else* or *next*) * *string/null* **label**: label of the branch taken (if set) * *list* **events**: manual changes of the journey of the user * *integer* **run**: index of the run * *string* **type**: type of the change * possible values: move / stop / resume / restart * *string* **source**: source of the change * *operator*: made by an educator * *publish*: made by publishing a new version of the journey (the user was on a removed step) * *result*: made by a changed result of a completed step (e.g. re-evaluation, manual scoring, extra points, invalidation), the user was routed again with the current result, because they had not done anything since the step * *hash* **from**: the state before the change * *string/null* **step**: key of the step * *string/null* **status**: status of the journey * *hash* **to**: the state after the change (the same way as *from*) * *string/null* **operator**: user identification string of the educator making the change * *datetime* **time**: date and time of the change #### /exam:journey:graph ??? info "Portable JSON Format" The journeys are exported and imported in a portable JSON format, where the contents are referenced by their identification strings. The format is described by a [JSON Schema](https://www.edubase.net/download:view/exam:journey:schema), and it is the same format the journey editor exports and imports files with. * *integer* **schema**: version of the format, currently *1* * *hash* **settings**: settings of the journey, every key is optional * *string/null* **title**: custom title of the journey (at most 255 characters) * *string/null* **description**: description of the journey (at most 1024 characters) * *integer* **max_visits**: maximum visits of a node by a user, between 1 and 50, loops stop with the *loop-limit* outcome (default: 5) * only the completed visits count, entering a node again after an error, a move or a stop is not a loop, and the manual moves of the educators are never stopped * *boolean* **restartable**: finished journeys can be started again by taking the starting exam again * *boolean* **notify_user**: notify the users when they finish the journey * *list{string}* **educators**: user identification strings of the educators notified by the journey, e.g. about its errors and by the *notify* nodes (at most 10, only the owner and the managers of the starting exam, the owner is notified if none are set) * *list* **nodes**: nodes of the journey (at most 200) * *string* **node**: stable key of the node, unique within the journey (1-32 letters, digits, - or _) * *string* **type**: type of the node * steps (the users stay until they complete them): *start* (exactly one, the starting exam itself), *exam*, *class*, *quiz*, *bundle* (several contents completed in any order) * decisions and actions (passed instantly): *condition*, *split*, *notify*, *flag*, *counter*, *mark* * *organizer*: passed instantly, only shapes the edges on the canvas (waypoint) * *wait*: waits before continuing * *gate*: waits for an educator to approve or decline the user on the interface * *end*: finishes the journey * *string/null* **label**: custom title of the node (at most 64 characters) * *hash* **position**: position of the node on the canvas (*x* and *y*), nodes without a position are laid out automatically * *hash* **config**: configuration of the node, depending on its type * *start*: **complete_on** (when the step is completed: *finish* when the exam is finished, *success* when it is finished successfully, *final* when its result is final) * *exam*: **exam** (exam identification string), **complete_on** (see above), **retake** (revisiting the step grants a retake), **reuse** (results achieved before entering the step complete it), **deadline** (see below) * *class*: **class** (class identification string), **reuse** (an earlier completion of the class completes the step on every visit), **deadline** (see below) * *quiz*: **quiz** (Quiz set identification string), **min_result** (minimum result of a play completing the step in percentage), **min_plays** (number of plays needed, between 1 and 100), **deadline** (see below) * *bundle*: the users are enrolled into every content of the bundle, and the step is completed once enough of them are completed, in any order * **items**: contents of the bundle (at most 10, each content at most once), each with a **type** (*exam*, *class* or *quiz*) and the identification string of the content keyed by its type, e.g. `{"type": "exam", "exam": "..."}` * **required**: number of contents to complete, between 0 and the number of items (0: every content, default: 0) * **complete_on**: when the exams of the bundle are completed (see above) * **min_result**: minimum result of a play completing a Quiz set of the bundle in percentage * **retake**: revisiting the bundle grants a retake of its exams (default: true) * **reuse**: the exam results of the current round (on the first visit) and the earlier class completions count (default: true) * **deadline** (see below) * **deadline** of the *exam*, *class*, *quiz* and *bundle* steps (optional, the starting step cannot have one): users not completing the step in time leave it on its *timeout* output (running exam attempts and results under review are waited for) * **mode**: *duration* or *date* * *duration*: for **minutes** from entering the step (between 1 and 525600) * *date*: until the **date**, given either as YYYY-MM-DD HH:MM in the **timezone** of the node (the same moment for every user), or as YYYY-MM-DD (the start of that day in the timezone of each user), past dates pass immediately * **timezone**: IANA name like *Europe/Budapest*, used with a date and time (default: UTC) * *condition*: **branches** (at most 16, evaluated in order, the first matching one is taken, *else* otherwise), each with a **key**, a **label**, a **match** mode (*all* or *any* of the rules) and at most 16 **rules**; **else_label** (label of the *else* output) * *split*: **branches** (at least 2, at most 16) with a **key**, a **label** and a relative **weight** (0-1000) to split the users randomly * *wait*: **mode** and its settings * *duration*: for **minutes** (between 1 and 525600) * *date*: until the **date**, given either as YYYY-MM-DD HH:MM in the **timezone** of the node (the same moment for every user), or as YYYY-MM-DD (the start of that day in the timezone of each user) * *weekday*: until the next **weekday** from 1 (Monday) to 7 (Sunday), at the optional **time** given as HH:MM in the **timezone** of the node, or at the start of that day in the timezone of each user without a time * **timezone**: IANA name like *Europe/Budapest*, used with a date and time or a weekday time (default: UTC) * *notify*: **target** (*user* or *educators*), **message** (at most 500 characters) * *flag*: **name** of the flag to set and its **value** (at most 64 characters) * *counter*: changes a numeric counter of the user, compared by the *journey.counter* rule field (counters share their names with the flags, and start from 0) * **name**: name of the counter (1-32 letters, digits, - or _, stored in lowercase) * **operation**: *set*, *add* or *subtract* (default: add) * **source**: the operand, the fixed **value** (*value*), or a value of the completed step or the journey: *step.result*, *step.time_spent*, *step.visits*, *journey.days*, *journey.steps* (default: value) * a source without a value (e.g. the completed step has no result) leaves the counter unchanged * **value**: the fixed operand (between -1000000000 and 1000000000, default: 1) * *mark*: **mode** (*mark* or *unmark* the user on the users page of the starting exam) * *gate*: **notify_educators** (notify the educators of the journey when a user arrives), **notify_user** (notify the user when approved or declined) * *organizer*: **input** and **output** (sides of the input and the output on the canvas: *left*, *top*, *right* or *bottom*, they have to be different, default: *left* and *right*) * *end*: **outcome** of the finished journey (*completed*, *failed*, or any custom text of at most 64 characters, default: *completed*) * *hash* **next**: keys of the nodes following this node, keyed by the outputs of this node * steps have a single *done* output (and a *timeout* output when they have a deadline), waits, actions and organizers a single *next* output, conditions have an output for each branch key and an *else* output, splits an output for each branch key, gates an *approved* and a *declined* output, end nodes have no outputs * the output names (*done*, *next*, *else*, *approved* and *declined*) cannot be used as branch keys * an output left out or set to null is unconnected, users taking it finish the journey with the *completed* outcome * the *approved* output of a gate has to be connected, users taking an unconnected *declined* output are stopped with the *declined* outcome (an educator can move or resume them) * users taking an unconnected *timeout* output are stopped with the *timeout* outcome * *string* **marker**, **title**, *hash* **content**: informative only, ignored by the import The rules of the conditions have the following fields: * *string* **field**: the compared value * the completed step: *step.result*, *step.successful*, *step.grade*, *step.attempts*, *step.time_spent*, *step.visits*, *step.skill* * an earlier node of the journey: *node.result*, *node.successful*, *node.grade*, *node.time_spent*, *node.completed*, *node.visits*, *node.skill* * the user: *exam.custom*, *user.gender*, *user.birthdate*, *user.customer_field*, *user.department*, *user.job*, *user.team*, *user.generated* * other contents of the user: *competency.status* (status of a competency of the organization of the starting exam, empty if not assigned to the user), *class.completed* (the user completed a class, not only within the journey), *journey.outcome* (outcome of another journey finished or stopped by the user, empty while not finished) * the journey and the current date: *journey.flag*, *journey.counter* (0 until set by a *counter* node), *journey.days*, *journey.steps*, *date.weekday*, *date.hour*, *date.date*, *date.datetime* * results are compared as percentages (0-100) * *string* **param**: parameter of the field (at most 64 characters) * needed by *step.skill* (skill identifier), the *node.\** fields (node key, or node key and skill identifier separated by a colon for *node.skill*), *exam.custom* (exam identification string and field label or hash separated by a colon), *user.customer_field* (name of the field), *competency.status* (competency identification string, title or external identifier), *class.completed* (class identification string), *journey.flag* (name of the flag), *journey.counter* (name of the counter) and *journey.outcome* (identification string of the starting exam of the journey, it can be the current journey as well) * *string* **operator**: the comparison * possible values: eq / neq / gt / gte / lt / lte / between / elapsed / in / not_in / contains / empty / not_empty / is_true / is_false * *empty*, *not_empty*, *is_true* and *is_false* need no value, *between* needs a [minimum, maximum] list (inclusive), *in* and *not_in* need a list (at most 64 items) * *elapsed* compares the time passed since a date value (e.g. *user.birthdate* or a date custom field with *exam.custom*) at the time of the decision with a [minimum, maximum] list of whole *unit*s (inclusive, one of the limits can be null, future dates give negative values) * *mixed* **value**: value to compare with * dates are YYYY-MM-DD calendar days and are never converted (*date.date* is the current day of the user) * date and time values are YYYY-MM-DD HH:MM, in UTC for *exam.custom* (*datetime* custom fields are stored in UTC, and compared by the day of the user when the rule has dates), and in the timezone of the rule for *date.datetime* * a *between* range has to use the same kind of value for both limits (two dates, or two date and time values) * departments and job roles are given by their titles, teams by their identification strings * competency statuses are WAITING / PROGRESS / DUE / OVERDUE / OUTDATED / COMPLETED / EXPIRED * *string* **aggregate**: which completion of an earlier node is compared (*node.\** fields, except *node.completed* and *node.visits*) * possible values: last / first / best / worst / average * default: last * *string* **measure**: whether the collected points or the percentage of the maximum is compared (skill fields) * possible values: points / percent * default: points * *string* **unit**: unit of the *elapsed* operator (mandatory for it) * possible values: years / months / weeks / days / hours / minutes * completed units are counted, so the *years* since a birth date are the age of the user * dates without a time are calendar days of the user, and they are counted until the current day of the user * *string* **time**: the rule only matches on the listed days from this time on, given as HH:MM (*date.weekday* only, optional) * *string* **timezone**: timezone the rule is evaluated in (IANA name like *Europe/Budapest*) * used by *date.hour*, *date.datetime*, and *date.weekday* with a *time*, the other date fields use the timezone of the user * default: UTC ##### GET /exam:journey:graph !!! abstract "" Export the draft or the published version of the journey started by an exam in the portable format. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the starting exam) * *string* **graph**: version of the journey to export * possible values: draft / published * draft: the version under editing, always available * published: the version used by the users, only available once the journey is published * default: draft ^^Output^^: * *string[url]* **$schema**: URL of the JSON Schema of the format * *integer* **schema**: version of the format * *string* **exam**: exam identification string * *string* **journey**: title of the journey * *boolean* **active**: the journey is enabled * *integer* **version**: number of the published version * *datetime/null* **published**: date and time the journey was last published at * *string* **graph**: the exported version (draft or published) * *boolean* **unpublished_changes**: the draft differs from the published version * *hash* **settings**: settings of the journey (see above) * *list* **nodes**: nodes of the journey, ordered by their position (see above) * every output of the nodes is listed in *next*, the unconnected ones with null * *list* **issues**: problems of the draft (only present if *graph* is *draft*) * *string* **level**: severity of the problem * error: blocks publishing * warning: informative only * *string* **code**: code of the problem * *string/null* **node**: key of the node with the problem * *string/null* **output**: output of the node with the problem * *string* **message**: readable description of the problem * *boolean* **publishable**: the draft has no errors, so it can be published (only present if *graph* is *draft*) ##### PUT /exam:journey:graph !!! abstract "" Replace the draft of the journey started by an exam with a journey in the portable format, and optionally update its settings. The import is strict: every problem is reported at once, and nothing is saved if there is any. If the exam does not start a journey yet, the journey is created and enabled (the simple chaining of the exam to a next exam is removed). The draft is not used by the users until it is published, see [POST /exam:journey:publish](#post-examjourneypublish). Needs the exam journeys feature to be enabled and modification permission for the exam. Archived exams and championships cannot start a journey. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the starting exam) * ==[!]== *list* **nodes**: nodes of the journey in the portable format (see above) * can also be given as a JSON encoded string * the contents of the steps (and of the bundles) have to be controlled by the current user, or already be used by the journey * departments, job roles and competencies are looked up in the organization of the starting exam (departments and job roles by their titles or external identifiers), teams among the teams of the current user * the classes of the *class.completed* rules have to be controlled by the current user (or already be used by the journey), the journeys of the *journey.outcome* rules have to be started by an exam the current user can report * *hash* **settings**: settings of the journey (see above) * can also be given as a JSON encoded string * the given keys are merged over the current settings * *integer* **schema**: version of the format * default: 1 ^^Output^^: The saved draft is returned, the same way as with [GET /exam:journey:graph](#get-examjourneygraph), together with: * *list{string}* **ignored**: unknown fields that were ignored by the import (only present if there are any) #### /exam:journey:publish ##### POST /exam:journey:publish !!! abstract "" Publish the draft of the journey started by an exam, so the users continue on the new version. The draft can only be published if it has no errors (see the *issues* of [GET /exam:journey:graph](#get-examjourneygraph)). If some users are on nodes removed from the draft, a new node has to be specified for each of them with *remap*, otherwise the journey is not published and the affected nodes are listed in the error message. Needs the exam journeys feature to be enabled and modification permission for the exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string (the starting exam) * *hash* **remap**: the users on the removed nodes are moved to these nodes * keyed by the key of the removed node, the value is the key of an existing node of the draft, or an empty string to stop the journeys of the users * can also be given as a JSON encoded string ^^Output^^: * *string* **exam**: exam identification string * *string* **journey**: title of the journey * *boolean* **active**: the journey is enabled * *integer* **version**: number of the published version * *datetime* **published**: date and time the journey was published at * *boolean* **unchanged**: the draft equals the published version, so nothing was published (only present if true, *remapped* and *issues* are not returned then) * *hash* **remapped**: the removed nodes the users were moved from, with the keys of their new nodes (null if their journeys were stopped, only present if any users were moved) * *list* **issues**: remaining warnings of the published version, the same way as with [GET /exam:journey:graph](#get-examjourneygraph) * *string* **warning**: the journey is published but disabled, the users do not enter it until it is enabled in the settings of the exam (only present if the journey is disabled) #### /exam:summary ##### POST /exam:summary !!! abstract "" Submit a new exam summary. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *string* **language**: summary language * *string* **type**: type of summary * possible values: ai * default: ai * ==[!]== *string* **summary**: summary text * basic HTML formatting allowed, but avoid complex designs * keep the summary short and concise * try to avoid including personal information (such as usernames, names and contact addresses) * *string* **llm**: name of the Large Language Model used to generate the summary (in case of AI summaries) * preferred values: openai / claude / gemini * *string* **model**: exact LLM model name used to generate the summary (in case of AI summaries) * must specify *llm* field as well --- ### API: Integrations endpoints #### /integrations ##### GET /integrations !!! abstract "" List owned and managed integrations. ^^Input^^: * *string* **search**: optional search string to filter results * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **integrations** * *string* **integration**: integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *string* **title**: title of the integration #### /integration ##### GET /integration !!! abstract "" Get/check integration. ^^Input^^: * ==[!]== *string* **integration**: integration identification string ^^Output^^: * *string* **integration**: integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *string* **title**: title of the integration * *string* **type**: type of the integration * *api*: EduBase API * *moodle*: Moodle LMS * *canvas*: Canvas LMS * *d2l*: D2L LMS * *schoology*: Schoology LMS * *lms*: other LMS * *boolean* **active**: integration is active * *string* **lti**: LTI version (only present if the integration is an LMS integration) ##### POST /integration !!! abstract "" Create a new API or LMS integration. ^^Input^^: * ==[!]== *string* **title**: title of the integration * *string* **description**: optional short description * *string* **type**: type of the integration * possible values: api / moodle / canvas / d2l / schoology / lms * api: EduBase API integration * moodle/canvas/d2l/schoology: selected LMS integration * lms: other LMS integration * default: api * ==[!]== *string* **lti**: LTI version * only necessary if the integration is an LMS integration * possible values: 1.0/1.1 / 1.3 * *1.0/1.1*: LTI 1.0 or 1.1 * *1.3*: LTI 1.3 (also known as LTI Advantage) * ==[!]== *string* **platform**: LMS platform URL (only necessary if the integration is an LMS integration) * *string* **organization**: organization identification string to assign the integration to * only an organization of the API application owner can be used * always the organization of the user for organizational members ^^Output^^: * *string* **integration**: integration identification string ##### PATCH /integration !!! abstract "" Update integration. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * *boolean* **active**: enable or disable integration * *string* **organization**: organization identification string to move the integration to * only an organization of the API application owner can be used * only the owner of the integration (or an administrator) can change the organization * send *none* (or an empty value) to remove the integration from its current organization * always the organization of the user for organizational members ##### DELETE /integration !!! abstract "" Remove integration. ^^Input^^: * ==[!]== *string* **integration**: integration identification string #### /integration:keys ##### GET /integration:keys !!! abstract "" Get integration keys/secrets. ^^Input^^: * ==[!]== *string* **integration**: integration identification string ^^Output^^: * *string* **app**: API application identification string (only present if the integration is an API integration) * *string* **consumer**: consumer key (only present if the integration is an LMS integration using LTI 1.0/1.1) * *string* **secret**: secret key (only present if the integration is an API integration or an LMS integration using LTI 1.0/1.1) * *string* **jwk**: URL for the JWK (JSON Web Key) set (only present if the integration is an LMS integration using LTI 1.3) * *string* **pem**: PEM formatted public key (only present if the integration is an LMS integration using LTI 1.3) ##### POST /integration:keys !!! abstract "" Rotate integration keys/secrets. ^^Input^^: * ==[!]== *string* **integration**: integration identification string ^^Output^^: The new keys and secrets of the integration are returned, the same way as with [GET /integration:keys](#get-integrationkeys). --- ### API: Metrics endpoints #### /metrics:custom ##### POST /metrics:custom !!! abstract "" Update a custom metric. ^^Input^^: * ==[!]== *string* **metric**: metric name * ==[!]== *float* **value**: target value (also accepts increments with a `+` prefix) ^^Output^^: * *string* **metric**: metric name * *string* **value**: saved value --- ### API: Organizations endpoints #### /organizations ##### GET /organizations !!! abstract "" List owned and managed organizations. ^^Input^^: * *string* **search**: optional search string to filter results * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **organizations** * *string* **organization**: organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *string* **title**: title of the organization #### /organization ##### GET /organization !!! abstract "" Get/check organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string ^^Output^^: * *string* **organization**: organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *string* **title**: title of the organization * *string* **description**: short description of the organization (only present if set for the organization) ##### POST /organization !!! abstract "" Create an organization. ^^Input^^: * ==[!]== *string* **title**: title of the organization * *string* **description**: optional short description * *string* **domain**: domain name (FQDN) for the organization without www prefix * needs special privileges to set! * *string[url]* **website**: homepage URL * *string[email]* **email**: contact email address * *string[phone]* **phone**: contact phone number * *string* **custom_{field}**: custom field data (only if specified field is configured for target EduBase instance) ^^Output^^: * *string* **organization**: organization identification string ##### PATCH /organization !!! abstract "" Update organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * *string{1,255}* **title**: title of the organization * *string* **custom_{field}**: custom field data (only if specified field is configured for target EduBase instance) ##### DELETE /organization !!! abstract "" Remove organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string #### /organization:members ##### GET /organization:members !!! abstract "" List all members in a organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string ^^Output^^: * *list* **members** * *string* **user**: user identification string * *string* **name**: name of the member * *string/null* **department**: name of the department (if member) * *string/null* **job**: name of the job role (if set) * *hash* **permission** * *string* **organization**: permission level to organization * *string* **content**: permission level to contents in organization * *string* **members**: permission level to members in organization * *boolean* **custom**: permission levels are customized for the member, not following the ones of the department * *hash* **restriction**: management restrictions of the member (only if the library is enabled in the organization) * *boolean* **library**: member is restricted from managing the library of the organization * *boolean* **competencies**: member is restricted from managing the competencies of the organization and its members ##### POST /organization:members !!! abstract "" Assign user(s) to a organization. Updates memberships if already member of the organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **users**: comma-separated list of user identification strings * *string* **department**: optional name of department * department and permission_ fields should not be used together, unless permission_custom is set * the competency assignments of the members moved to another department are updated, the competencies they only had through their previous department are lost * *string* **job**: optional name of job role * the external identifier of the job role can also be used * an empty value removes the job role, the job role is kept when not specified * an unknown job role is an error, see [GET /organization:jobs](#get-organizationjobs) * the competency assignments of the members with another job role are updated, the competencies they only had through their previous job role are lost * *string* **permission_organization**: optional permission level to organization * possible values: member / teacher / reporter / supervisor / admin * default: member * *string* **permission_content**: optional permission level to contents in organization * possible values: none / view / report / control / modify / grant / admin * default: none * *string* **permission_members**: optional permission level to members in organization * possible values: none / department / organization * default: none * *boolean* **permission_custom**: customize the permission levels of the members inside the department * only used together with department, the permission_ fields not specified default to the ones of the department * members with custom permission levels keep them when the permission levels of the department are changed * set to false to make the members follow the permission levels of the department again * when not specified, custom permission levels are kept for the members staying in the same department and removed when moved to another department * *boolean* **restriction_library**: restrict the members from managing the library of the organization (documents, versions and types), even with a sufficient permission level * only available if the library is enabled in the organization, ignored otherwise * left unchanged when not specified, default for new members: false * *boolean* **restriction_competencies**: restrict the members from managing the competencies of the organization and its members (competencies, requirements, assignments, completions, expiry dates and attachments), even with a sufficient permission level * only available if the library is enabled in the organization, ignored otherwise * left unchanged when not specified, default for new members: false * *boolean* **notify**: notify users * default: false ##### DELETE /organization:members !!! danger "" Remove user(s) from a organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **users**: comma-separated list of user identification strings #### /organizations:members ##### POST /organizations:members !!! abstract "" Assign user(s) to organization(s). Updates memberships if already member of a organization. ^^Input^^: * ==[!]== *string* **organizations**: comma-separated list of organization identification strings * ==[!]== *string* **users**: comma-separated list of user identification strings * *string* **department**: optional name of department * if department is not found in an organization, permission_ fields are used to set permission levels * the competency assignments of the members moved to another department are updated, the competencies they only had through their previous department are lost * *string* **job**: optional name of job role * the external identifier of the job role can also be used * an empty value removes the job role, the job role is kept when not specified or not found in an organization * the competency assignments of the members with another job role are updated, the competencies they only had through their previous job role are lost * *string* **permission_organization**: optional permission level to organization * possible values: member / teacher / reporter / supervisor / admin * default: member * *string* **permission_content**: optional permission level to contents in organization * possible values: none / view / report / control / modify / grant / admin * default: none * *string* **permission_members**: optional permission level to members in organization * possible values: none / department / organization * default: none * *boolean* **permission_custom**: customize the permission levels of the members inside the department * only used together with department, the permission_ fields not specified default to the ones of the department * members with custom permission levels keep them when the permission levels of the department are changed * set to false to make the members follow the permission levels of the department again * when not specified, custom permission levels are kept for the members staying in the same department and removed when moved to another department * *boolean* **restriction_library**: restrict the members from managing the library of the organization (documents, versions and types), even with a sufficient permission level * only available if the library is enabled in the organization, ignored otherwise * left unchanged when not specified, default for new members: false * *boolean* **restriction_competencies**: restrict the members from managing the competencies of the organization and its members (competencies, requirements, assignments, completions, expiry dates and attachments), even with a sufficient permission level * only available if the library is enabled in the organization, ignored otherwise * left unchanged when not specified, default for new members: false * *boolean* **notify**: notify users * default: false #### /organization:departments ##### GET /organization:departments !!! abstract "" List all departments of an organization, ordered by their hierarchy. ^^Input^^: * ==[!]== *string* **organization**: organization identification string ^^Output^^: * *string* **organization**: organization identification string * *list* **departments** * *string* **department**: name of the department * *string/null* **id**: external unique department identifier (if set for the department) * *string/null* **parent**: name of the parent department (if set) * *integer* **level**: depth of the department within the hierarchy (0 for root level departments) * *hash* **permission** * *string* **organization**: permission level to organization given to the members * *string* **content**: permission level to contents in organization given to the members * *string* **members**: permission level to members in organization given to the members * *integer* **members**: number of visible members in the department * *integer* **members_custom**: number of visible members in the department with custom permission levels * *list* **leaders** * *string* **user**: user identification string * *string* **name**: name of the leader #### /organization:department ##### GET /organization:department !!! abstract "" Get a department of an organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **department**: name of the department * the external identifier of the department can also be used ^^Output^^: * *string* **organization**: organization identification string * *string* **department**: name of the department * *string/null* **id**: external unique department identifier (if set for the department) * *string/null* **parent**: name of the parent department (null for root level departments) * *integer* **level**: depth of the department within the hierarchy (0 for root level departments) * *hash* **permission** * *string* **organization**: permission level to organization given to the members * *string* **content**: permission level to contents in organization given to the members * *string* **members**: permission level to members in organization given to the members * *integer* **members**: number of visible members in the department * *integer* **members_custom**: number of visible members in the department with custom permission levels * *list* **leaders** * *string* **user**: user identification string * *string* **name**: name of the leader ##### POST /organization:department !!! abstract "" Create a department in an organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string{1,255}* **title**: name of the department * must be unique within the organization * *string{0,64}* **id**: external unique department identifier * *string* **parent**: name or external identifier of the parent department * default: the department is created on root level * *string* **permission_organization**: permission level to organization given to the members * possible values: member / teacher / reporter / supervisor / admin * default: member * *string* **permission_content**: permission level to contents in organization given to the members * possible values: none / view / report / control / modify / grant / admin * default: none * *string* **permission_members**: permission level to members in organization given to the members * possible values: none / department / organization * default: none * *string* **leaders**: comma-separated list of user identification strings * leaders must be members of the organization * at most 5 leaders can be specified ^^Output^^: * *string* **organization**: organization identification string * *string* **department**: name of the department * *string/null* **id**: external unique department identifier (if set for the department) ##### PATCH /organization:department !!! abstract "" Update a department of an organization. Only the specified fields are changed. When the permission levels are changed, they are applied automatically to the members, except for the ones with custom permission levels. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **department**: name or external identifier of the department * *string{1,255}* **title**: new name of the department * must be unique within the organization * *string{0,64}* **id**: external unique department identifier * an empty value removes the identifier * *string* **parent**: name or external identifier of the parent department * an empty value moves the department to the root level * the department itself or a department below it cannot be the parent * *string* **permission_organization**: permission level to organization given to the members * possible values: member / teacher / reporter / supervisor / admin * *string* **permission_content**: permission level to contents in organization given to the members * possible values: none / view / report / control / modify / grant / admin * *string* **permission_members**: permission level to members in organization given to the members * possible values: none / department / organization * *boolean* **permission_reset**: reset the custom permission levels of the members, so every member follows the permission levels of the department * default: false * *string* **leaders**: comma-separated list of user identification strings * replaces the current leaders, an empty value removes every leader * leaders must be members of the organization * at most 5 leaders can be specified ##### DELETE /organization:department !!! danger "" Remove a department from an organization. The members of the department are left without a department and lose the permission levels (except for the custom ones, which are kept) and competency assignments given by it, the departments below it are moved one level up. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **department**: name or external identifier of the department #### /organization:jobs ##### GET /organization:jobs !!! abstract "" List all job roles of an organization, ordered by their name. Competencies can be assigned to job roles, so every member with the job role gets them automatically. ^^Input^^: * ==[!]== *string* **organization**: organization identification string ^^Output^^: * *string* **organization**: organization identification string * *list* **jobs** * *string* **job**: name of the job role * *string/null* **id**: external unique job role identifier (if set for the job role) * *integer* **members**: number of members with the job role #### /organization:job ##### GET /organization:job !!! abstract "" Get a job role of an organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **job**: name of the job role * the external identifier of the job role can also be used ^^Output^^: * *string* **organization**: organization identification string * *string* **job**: name of the job role * *string/null* **id**: external unique job role identifier (if set for the job role) * *integer* **members**: number of members with the job role ##### POST /organization:job !!! abstract "" Create a job role in an organization. Members can have one job role each, which can be used to assign competencies automatically. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string{1,255}* **title**: name of the job role * must be unique within the organization * *string{0,64}* **id**: external unique job role identifier ^^Output^^: * *string* **organization**: organization identification string * *string* **job**: name of the job role * *string/null* **id**: external unique job role identifier (if set for the job role) ##### PATCH /organization:job !!! abstract "" Update a job role of an organization. Only the specified fields are changed. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **job**: name or external identifier of the job role * *string{1,255}* **title**: new name of the job role * must be unique within the organization * *string{0,64}* **id**: external unique job role identifier * an empty value removes it ##### DELETE /organization:job !!! danger "" Remove a job role from an organization. The members with the job role are left without a job role and lose the competency assignments given by it. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **job**: name or external identifier of the job role #### /organization:competencies ##### GET /organization:competencies !!! abstract "" List all competencies of an organization. Library and competencies must be enabled for the organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string ^^Output^^: * *string* **organization**: organization identification string * *list* **competencies** * *string* **competency**: competency identification string * *string/null* **id**: external unique competency identifier (if set for the competency) * *string* **title**: title of the competency * *string/null* **description**: description of the competency (if set) * *integer* **requirements**: number of requirements * *integer* **documents**: number of library documents attached * *hash* **assignments** * *boolean* **all**: assigned to every member of the organization * *integer* **departments**: number of departments assigned to * *integer* **jobs**: number of job roles assigned to (members get the competency through their department or their job role) * *integer* **members**: number of members assigned to individually * *integer* **assigned**: number of visible members assigned to the competency * *hash* **status**: number of assigned members by their competency status * *integer* **completed**: completed * *integer* **progress**: in progress * *integer* **waiting**: waiting for completion * *integer* **expired**: expired * *integer* **due**: due soon * *integer* **overdue**: past the deadline * *integer* **outdated**: outdated #### /organization:competency:requirements ##### GET /organization:competency:requirements !!! abstract "" List the requirements of a competency. Library and competencies must be enabled for the organization of the competency. ^^Input^^: * ==[!]== *string* **competency**: competency identification string * the external identifier of the competency can also be used when *organization* is specified * *string* **organization**: organization identification string ^^Output^^: * *string* **organization**: organization identification string * *string* **competency**: competency identification string * *string/null* **id**: external unique competency identifier (if set for the competency) * *string* **title**: title of the competency * *list* **requirements**: requirements ordered by their priority * *string* **requirement**: requirement identification string * *string/null* **title**: title of the requirement * *string* **type**: type of the requirement * possible values: exam / class / document / journey / todo * *boolean* **optional**: requirement is optional * *string* **description**: description of the requirement (only present if set for the requirement) * *hash* **deadline**: deadline of the requirement (only present if a deadline is configured) * *string* **type**: type of the deadline * possible values: date / days * *date* **date**: fixed deadline (only if *type* is *date*) * *integer* **days**: number of days available to complete the requirement (only if *type* is *days*) * *hash/null* **exam**: the required exam (only present if *type* is *exam*, null if the exam is not available) * *string* **exam**: exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *string* **title**: title of the exam * *hash/null* **class**: the required class (only present if *type* is *class*, null if the class is not available) * *string* **class**: class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *string* **title**: title of the class * *hash/null* **document**: the required library document (only present if *type* is *document*, null if the document is not available) * *string* **document**: document identification string * *string/null* **prefix**: prefix of the document * *string* **title**: title of the document * *boolean* **archived**: document is archived * *hash/null* **journey**: the required exam journey (only present if *type* is *journey*, null if the journey is not available) * *string* **exam**: exam identification string of the starting exam (see [GET /exam:journey](../exams/#get-examjourney)) * *string* **title**: title of the journey * *boolean* **active**: the journey is enabled * *list{string}* **outcomes**: outcomes the journey has to be finished with to fulfill the requirement (empty if any outcome is accepted) * *hash* **todo**: the required task (only present if *type* is *todo*) * *string* **type**: type of the task * possible values: default / document / exam / training / attestation * *attestation*: the member has to sign a statement by typing their full name * *boolean* **attachments**: attachments can be uploaded * *list* **educators**: educators of the training (only present if *type* is *training*, visible users only) * *string* **user**: user identification string * *string* **name**: name of the educator * *string* **statement**: the statement the member has to sign, with the titles of the requirement and the competency filled in (only present if *type* is *attestation*) * *list{string}* **fields**: labels of the fields to fill (only present if *type* is not *default* or *attestation*) * *list{string}* **documents**: identification strings of the library documents attached to the requirement (only present if there are any) #### /organization:compliance ##### GET /organization:compliance !!! abstract "" Get the competency compliance overview of an organization, grouped by departments. Organization reporters see every visible member, department leaders only the members of the departments they lead. Library and competencies must be enabled for the organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * *string* **job**: optional name or external identifier of a job role to limit the overview to its members ^^Output^^: * *string* **organization**: organization identification string * *string* **scope**: scope of the overview * possible values: organization (whole organization) / departments (only the departments led by the user) * *list* **competencies**: competencies with any assigned member within the scope * *string* **competency**: competency identification string * *string/null* **id**: external unique competency identifier (if set for the competency) * *string* **title**: title of the competency * *list* **departments**: departments with any member or progress within the scope, ordered by their hierarchy * *string/null* **department**: name of the department (null for the members without a department, in *organization* scope only) * *string/null* **id**: external unique department identifier (if set for the department) * *string/null* **parent**: name of the parent department (null for root level departments) * *integer* **level**: depth of the department within the hierarchy (0 for root level departments) * *integer* **members**: number of members in the department * *list* **competencies**: competency statistics of the department (only competencies with assigned members are listed) * *string* **competency**: competency identification string * *integer* **assigned**: number of members assigned to the competency * *hash* **status**: number of assigned members by their competency status (same keys as the top level *status*) * *integer* **assigned**: total number of competency assignments within the scope * *hash* **status**: total number of competency assignments by their status * *integer* **completed**: completed * *integer* **progress**: in progress * *integer* **waiting**: waiting for completion * *integer* **expired**: expired * *integer* **due**: due soon * *integer* **overdue**: past the deadline * *integer* **outdated**: outdated #### /user:organizations ##### GET /user:organizations !!! abstract "" List all organizations a user is member of. ^^Input^^: * ==[!]== *string* **user**: user identification string ^^Output^^: * *list* **organizations** * *string* **organization**: organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *string* **title**: title of the organization * *string* **link**: link to the organization manager page * *string/null* **department**: name of the department (if member) * *string/null* **job**: name of the job role (if set) * *hash* **permission** * *string* **organization**: permission level to organization * *string* **content**: permission level to contents in organization * *string* **members**: permission level to members in organization * *boolean* **custom**: permission levels are customized for the member, not following the ones of the department * *hash* **restriction**: management restrictions of the member (only if the library is enabled in the organization) * *boolean* **library**: member is restricted from managing the library of the organization * *boolean* **competencies**: member is restricted from managing the competencies of the organization and its members ##### POST /user:organizations !!! abstract "" Assign user to organization(s). Updates membership if already member of a organization. ^^Input^^: * ==[!]== *string* **user**: user identification string * ==[!]== *string* **organizations**: comma-separated list of organization identification strings * *string* **department**: optional name of department * if department is not found in an organization, permission_ fields are used to set permission levels * the competency assignments of the members moved to another department are updated, the competencies they only had through their previous department are lost * *string* **job**: optional name of job role * the external identifier of the job role can also be used * an empty value removes the job role, the job role is kept when not specified or not found in an organization * the competency assignments of the members with another job role are updated, the competencies they only had through their previous job role are lost * *string* **permission_organization**: optional permission level to organization * possible values: member / teacher / reporter / supervisor / admin * default: member * *string* **permission_content**: optional permission level to contents in organization * possible values: none / view / report / control / modify / grant / admin * default: none * *string* **permission_members**: optional permission level to members in organization * possible values: none / department / organization * default: none * *boolean* **permission_custom**: customize the permission levels of the members inside the department * only used together with department, the permission_ fields not specified default to the ones of the department * members with custom permission levels keep them when the permission levels of the department are changed * set to false to make the members follow the permission levels of the department again * when not specified, custom permission levels are kept for the members staying in the same department and removed when moved to another department * *boolean* **restriction_library**: restrict the members from managing the library of the organization (documents, versions and types), even with a sufficient permission level * only available if the library is enabled in the organization, ignored otherwise * left unchanged when not specified, default for new members: false * *boolean* **restriction_competencies**: restrict the members from managing the competencies of the organization and its members (competencies, requirements, assignments, completions, expiry dates and attachments), even with a sufficient permission level * only available if the library is enabled in the organization, ignored otherwise * left unchanged when not specified, default for new members: false * *boolean* **notify**: notify user * default: false ##### DELETE /user:organizations !!! danger "" Remove user from organization(s). ^^Input^^: * ==[!]== *string* **user**: user identification string * ==[!]== *string* **organizations**: comma-separated list of organization identification strings #### /organization:webhook ##### GET /organization:webhook !!! abstract "" Get/check webhook configured in organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **webhook**: webhook identification string ^^Output^^: * *string* **organization**: organization identification string * *string* **webhook**: webhook identification string * *string* **title**: title of the webhook * *boolean* **active**: webhook is active ##### POST /organization:webhook !!! abstract "" Create a webhook for an organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **title**: title of the webhook * ==[!]== *string* **trigger_event**: type of event to trigger webhook * possible values: exam-play-result / quiz-play-result / journey-completed / api * exam-play-result: triggers when a user (must be member of the organization) completes an exam in the organization * quiz-play-result: triggers when a user (must be member of the organization) completes a quiz in practice mode in the organization * journey-completed: triggers when a user (must be member of the organization) finishes an exam journey started by an exam of the organization, see [GET /exam:journey](../exams/#get-examjourney) * api: triggers when a manual API call is made (useful for testing and debugging), see [POST /organization:webhook:trigger](#post-organizationwebhooktrigger) * ==[!]== *string[url]* **endpoint**: URL to send webhook notifications to * *string* **method**: HTTP method to use for webhook notifications * possible values: POST / GET * default: POST * *string* **authentication**: type of authentication * possible values: none / key * none: no authentication * key: use a secret key (or password) for authentication * default: none * *string* **authentication_send**: how to send authentication data * possible values: header / bearer / data * header: as header field * bearer: as Bearer token in Authorization header * data: as data field (in body or query string) * default: data * *string* **authentication_send_header**: name of header field to send authentication data in (required if authentication is set to key and authentication_send is set to header) * *string* **authentication_send_data**: name of data field to send authentication data in (required if authentication is set to key and authentication_send is set to data) * *string* **authentication_key**: secret key (or password) to use for authentication (required if authentication is set to key) * *string* **authentication_key_custom**: custom field name to use as the authentication key * required if authentication is set to key * mutually exclusive with authentication_key * *string[json]* **extra_data**: additional data (as JSON encoded string) to send with the webhook notification * *string* **retry**: how to retry webhook notifications on failure * possible values: none / error * none: no retry * error: delayed retry on any error * default: error ^^Output^^: * *string* **organization**: organization identification string * *string* **webhook**: webhook identification string ##### PATCH /organization:webhook !!! abstract "" Update organizational webhook. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **webhook**: webhook identification string * *boolean* **active**: enable or disable webhook ##### DELETE /organization:webhook !!! abstract "" Remove webhook from organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **webhook**: webhook identification string ##### POST /organization:webhook:trigger !!! abstract "" Trigger an organizational webhook call with optional custom payload. Only triggers webhooks with **trigger_event** set to `api`! ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **webhook**: webhook identification string * *string[json]* **data**: custom payload data to be sent with the webhook call --- ### API: Content permissions endpoints #### /class ##### GET /class:permission !!! abstract "" Check if a user has permission on a class. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *class* * *string* **code**: the class identification string * *string/null* **id**: external unique class identifier (if set for the class) * **status** * *boolean* **permission**: the user has permission on this class * *boolean* **rule**: there is a permission rule with these parameters ##### POST /class:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *class* * *string* **code**: the class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *boolean* **success**: operation was successful ##### DELETE /class:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *class* * *string* **code**: the class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *boolean* **success**: operation was successful ##### POST /class:transfer !!! abstract "" Transfer class to user. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *class* * *string* **code**: the class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *boolean* **success**: operation was successful #### /course ##### GET /course:permission !!! abstract "" Check if a user has permission on a course. ^^Input^^: * ==[!]== *string* **course**: course identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *course* * *string* **code**: the course identification string * *string/null* **id**: external unique course identifier (if set for the course) * **status** * *boolean* **permission**: the user has permission on this course * *boolean* **rule**: there is a permission rule with these parameters ##### POST /course:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **course**: course identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *course* * *string* **code**: the course identification string * *string/null* **id**: external unique course identifier (if set for the course) * *boolean* **success**: operation was successful ##### DELETE /course:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **course**: course identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *course* * *string* **code**: the course identification string * *string/null* **id**: external unique course identifier (if set for the course) * *boolean* **success**: operation was successful ##### POST /course:transfer !!! abstract "" Transfer course to user. ^^Input^^: * ==[!]== *string* **course**: course identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *course* * *string* **code**: the course identification string * *string/null* **id**: external unique course identifier (if set for the course) * *boolean* **success**: operation was successful #### /event ##### GET /event:permission !!! abstract "" Check if a user has permission on an event. ^^Input^^: * ==[!]== *string* **event**: event identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / finances / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *event* * *string* **code**: the event identification string * *string/null* **id**: external unique event identifier (if set for the event) * **status** * *boolean* **permission**: the user has permission on this event * *boolean* **rule**: there is a permission rule with these parameters ##### POST /event:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **event**: event identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / finances / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *event* * *string* **code**: the event identification string * *string/null* **id**: external unique event identifier (if set for the event) * *boolean* **success**: operation was successful ##### DELETE /event:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **event**: event identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / finances / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *event* * *string* **code**: the event identification string * *string/null* **id**: external unique event identifier (if set for the event) * *boolean* **success**: operation was successful ##### POST /event:transfer !!! abstract "" Transfer event to user. ^^Input^^: * ==[!]== *string* **event**: event identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *event* * *string* **code**: the event identification string * *string/null* **id**: external unique event identifier (if set for the event) * *boolean* **success**: operation was successful #### /exam ##### GET /exam:permission !!! abstract "" Check if a user has permission on an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *exam* * *string* **code**: the exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * **status** * *boolean* **permission**: the user has permission on this exam * *boolean* **rule**: there is a permission rule with these parameters ##### POST /exam:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *exam* * *string* **code**: the exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *boolean* **success**: operation was successful ##### DELETE /exam:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *exam* * *string* **code**: the exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *boolean* **success**: operation was successful ##### POST /exam:transfer !!! abstract "" Transfer exam to user. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *exam* * *string* **code**: the exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *boolean* **success**: operation was successful #### /integration ##### GET /integration:permission !!! abstract "" Check if a user has permission on an integration. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *integration* * *string* **code**: the integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * **status** * *boolean* **permission**: the user has permission on this integration * *boolean* **rule**: there is a permission rule with these parameters ##### POST /integration:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *integration* * *string* **code**: the integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *boolean* **success**: operation was successful ##### DELETE /integration:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *integration* * *string* **code**: the integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *boolean* **success**: operation was successful ##### POST /integration:transfer !!! abstract "" Transfer integration to user. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *integration* * *string* **code**: the integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *boolean* **success**: operation was successful #### /organization ##### GET /organization:permission !!! abstract "" Check if a user has permission on an organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *organization* * *string* **code**: the organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * **status** * *boolean* **permission**: the user has permission on this organization * *boolean* **rule**: there is a permission rule with these parameters ##### POST /organization:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *organization* * *string* **code**: the organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *boolean* **success**: operation was successful ##### DELETE /organization:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *organization* * *string* **code**: the organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *boolean* **success**: operation was successful ##### POST /organization:transfer !!! abstract "" Transfer organization to user. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *organization* * *string* **code**: the organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *boolean* **success**: operation was successful #### /quiz ##### GET /quiz:permission !!! abstract "" Check if a user has permission on a Quiz. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *quiz* * *string* **code**: the Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * **status** * *boolean* **permission**: the user has permission on this Quiz * *boolean* **rule**: there is a permission rule with these parameters ##### POST /quiz:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *quiz* * *string* **code**: the Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *boolean* **success**: operation was successful ##### DELETE /quiz:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *quiz* * *string* **code**: the Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *boolean* **success**: operation was successful ##### POST /quiz:transfer !!! abstract "" Transfer Quiz to user. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *quiz* * *string* **code**: the Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *boolean* **success**: operation was successful #### /scorm ##### GET /scorm:permission !!! abstract "" Check if a user has permission on a SCORM learning material. ^^Input^^: * ==[!]== *string* **scorm**: SCORM identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *scorm* * *string* **code**: the SCORM identification string * *string/null* **id**: external unique SCORM identifier (if set for the SCORM) * **status** * *boolean* **permission**: the user has permission on this SCORM learning material * *boolean* **rule**: there is a permission rule with these parameters ##### POST /scorm:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **scorm**: SCORM identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *scorm* * *string* **code**: the SCORM identification string * *string/null* **id**: external unique SCORM identifier (if set for the SCORM) * *boolean* **success**: operation was successful ##### DELETE /scorm:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **scorm**: SCORM identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *scorm* * *string* **code**: the SCORM identification string * *string/null* **id**: external unique SCORM identifier (if set for the SCORM) * *boolean* **success**: operation was successful ##### POST /scorm:transfer !!! abstract "" Transfer SCORM to user. ^^Input^^: * ==[!]== *string* **scorm**: SCORM identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *scorm* * *string* **code**: the SCORM identification string * *string/null* **id**: external unique SCORM identifier (if set for the SCORM) * *boolean* **success**: operation was successful #### /tag ##### GET /tag:permission !!! abstract "" Check if a user has permission on a tag. ^^Input^^: * ==[!]== *string* **tag**: tag identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *tag* * *string* **code**: the tag identification string * *string/null* **id**: external unique tag identifier (if set for the tag) * **status** * *boolean* **permission**: the user has permission on this tag * *boolean* **rule**: there is a permission rule with these parameters ##### POST /tag:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **tag**: tag identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *tag* * *string* **code**: the tag identification string * *string/null* **id**: external unique tag identifier (if set for the tag) * *boolean* **success**: operation was successful ##### DELETE /tag:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **tag**: tag identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *tag* * *string* **code**: the tag identification string * *string/null* **id**: external unique tag identifier (if set for the tag) * *boolean* **success**: operation was successful ##### POST /tag:transfer !!! abstract "" Transfer tag to user. ^^Input^^: * ==[!]== *string* **tag**: tag identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *tag* * *string* **code**: the tag identification string * *string/null* **id**: external unique tag identifier (if set for the tag) * *boolean* **success**: operation was successful #### /video ##### GET /video:permission !!! abstract "" Check if a user has permission on a video. ^^Input^^: * ==[!]== *string* **video**: video identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *video* * *string* **code**: the video identification string * *string/null* **id**: external unique video identifier (if set for the video) * **status** * *boolean* **permission**: the user has permission on this video * *boolean* **rule**: there is a permission rule with these parameters ##### POST /video:permission !!! abstract "" Create new permission for a user. ^^Input^^: * ==[!]== *string* **video**: video identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *video* * *string* **code**: the video identification string * *string/null* **id**: external unique video identifier (if set for the video) * *boolean* **success**: operation was successful ##### DELETE /video:permission !!! danger "" Remove a user permission. ^^Input^^: * ==[!]== *string* **video**: video identification string * ==[!]== *string* **user**: user identification string * ==[!]== *string* **permission**: permission level * possible values: view / report / control / modify / grant / admin ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *video* * *string* **code**: the video identification string * *string/null* **id**: external unique video identifier (if set for the video) * *boolean* **success**: operation was successful ##### POST /video:transfer !!! abstract "" Transfer video to user. ^^Input^^: * ==[!]== *string* **video**: video identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * **content** * *string* **type**: will be *video* * *string* **code**: the video identification string * *string/null* **id**: external unique video identifier (if set for the video) * *boolean* **success**: operation was successful --- ### API: Quiz Plays endpoints #### /quiz:results ##### GET /quiz:results:play !!! abstract "" Get detailed results for a specific Quiz play. ^^Input^^: * ==[!]== *string* **play**: Quiz play identification string ^^Output^^: * *string* **play**: Quiz play identification string * *string* **user**: user identification string * *datetime* **time_start**: start time * *datetime* **time_end**: end time * *integer* **questions_total**: total number of questions asked * *integer* **questions_correct**: number of correctly answered questions * *float* **points_total**: total points * *float* **points_correct**: total points scored * *boolean* **valid**: result is valid * finished successfully, all questions evaluated and not archived * *boolean/null* **successful**: attempt passed grading threshold (if applicable) * *list* **questions** * *string* **index**: question index * *string/null* **id**: external unique question identifier (if present) * *string* **question**: question identification string * *float* **time_answer**: number of seconds spent on question (if available) * *float* **points_maximum**: maximum points * *float* **points**: points scored ##### DELETE /quiz:results:play !!! danger "" Archive or forget the result of a Quiz play. An exam result is archived: it disappears from the result lists and the reports of the exam, but it is kept and can be restored by adding the archived user back to the exam. A result outside of an exam is forgotten: the play is detached from the user and only kept for the statistics. Exam results can only be archived when archiving is enabled for the exam (see the *archive* setting of [POST /exam:settings](../exams/#post-examsettings)), and only the current result of the user can be archived. Results outside of an exam can only be forgotten by administrators. ^^Input^^: * ==[!]== *string* **play**: Quiz play identification string ##### GET /quiz:results:user !!! abstract "" Get user results for a specific Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz set identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *list* **results** * *string* **play**: Quiz play identification string * *string* **user**: user identification string * *datetime* **time_start**: start time * *datetime* **time_end**: end time * *integer* **questions_total**: total number of questions asked * *integer* **questions_correct**: number of correctly answered questions * *float* **points_total**: total points * *float* **points_correct**: total points scored * *boolean* **valid**: result is valid * *boolean/null* **successful**: attempt passed grading threshold (if applicable) #### /exam:results ##### GET /exam:results:user !!! abstract "" Get user results for a specific exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *list* **results** * *string* **play**: Quiz play identification string * *string* **user**: user identification string * *datetime* **time_start**: start time * *datetime* **time_end**: end time * *integer* **questions_total**: total number of questions asked * *integer* **questions_correct**: number of correctly answered questions * *float* **points_total**: total points * *float* **points_correct**: total points scored * *integer* **attempt**: index of attempt * *boolean* **valid**: result is valid * *boolean/null* **successful**: attempt passed grading threshold (if applicable) ##### POST /exam:results:export !!! abstract "" Generate a download link for the results of an exam. The generated link is not authenticated, so it can be handed over to any service, but it can only be used once and it expires in an hour. Only those examinees are exported that are visible for the current user, and the exported columns are controlled by the export settings of the exam (see [GET /exam:settings](../exams/#get-examsettings)). Exporting the results needs reporting permission for the exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * *string* **format**: format of the exported file * possible values: csv / xlsx * csv: semicolon separated values * xlsx: Excel 2007+ workbook * default: the *export_format* setting of the exam * *string* **preset**: content of the exported file * possible values: default / answers * default: the examinees with their results * answers: the answers only, without the examinees * the *answers* preset is only available for anonymous surveys * default: default * *string* **sort**: ordering of the examinees in the exported file * possible values: default / name * default: the same order they are listed in on the users page of the exam * name: by the name of the examinees * default: the *export_sort* setting of the exam * *integer* **round**: export the results of a previous round instead of the current one * only available when the exam is configured to keep the results of its previous rounds * has to be the number of an already closed round, see [GET /exam:round](../exams/#get-examround) * *object* **filters**: only export the examinees matching the given user data * keyed by the label of a custom user data field of the exam, see [GET /exam:fields](../exams/#get-examfields) * only those *select* custom fields can be used that have their *filtering* configured * the value is one of the filterable options of the field, or a list of them * the examinees have to match any of the values given for the same field, and all of the fields specified * cannot be combined with *round*, the results of a previous round cannot be filtered * *string* **language**: language of the exported file * default: the language of the user ^^Output^^: * *string* **exam**: exam identification string * *string* **format**: format of the exported file * *string[url]* **url**: download link for the results * *datetime* **valid**: date and time of link expiration #### /exam:certificates ##### GET /exam:certificates:user !!! abstract "" Get (the latest) certificate details for a specific exam and user. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **play**: Quiz play identification string * *string* **user**: user identification string * *boolean* **archived**: exam result is archived * *boolean* **eligible**: result is eligible for certification * *boolean* **certified**: result is eligible for certification and also certified * *string* **serial**: serial number of the certificate (only present if the result is certified and serial numbering is enabled) * *date* **expires**: date of expiration (only present if the result is certified and expiration is configured) ##### POST /exam:certificates:user:download !!! abstract "" Generate download link for the latest user exam certificate. If a previous valid link exists, it will be returned instead. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **play**: Quiz play identification string * *string* **user**: user identification string * *string[url]* **url**: download link for the certificate * *date* **valid**: date of link expiration --- ### API: Quiz questions endpoints #### /questions ##### GET /questions !!! abstract "" List owned and managed Quiz questions. ^^Input^^: * *string* **search**: optional search string to filter results * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **questions** * *string* **question**: question identification string * *string/null* **id**: external unique question identifier (if set for the question) * *boolean* **ai**: question is marked as AI generated (even if partially) * *datetime/null* **ai_reviewed**: date and time an educator reviewed the AI generated question * *null*: not reviewed yet (or not AI generated) #### /question ##### GET /question !!! abstract "" Check existing question. ^^Input^^: * ==[!]== *string* **id**: external unique question identifier ^^Output^^: * *string* **question**: question identification string * *string/null* **id**: external unique question identifier (if set for the question) * *boolean* **active**: question is active * *boolean* **ai**: question is marked as AI generated (even if partially) * *datetime/null* **ai_reviewed**: date and time an educator reviewed the AI generated question * *null*: not reviewed yet (or not AI generated) ##### POST /question !!! abstract "" Publish or update a question. Check documentation or online editor for more information on additional fields. ^^Input^^: * ==[!]== *string{1,64}* **id**: external unique question identifier[^1] ??? info "ID best practices" Unique identifier for question management. On repeated uploads, the questions are updated (rather then added) based on this value, which can be an arbitrary text. If the question already exists at upload time with the same external identifier (in the given folder or Quiz set), the existing question will be updated instead of being added as a new one. * **Use cases:** * Integration with external systems * Version control * Batch updates * Content synchronization * **Best practices:** * Use consistent naming conventions * Include version, source or date information * Consider hierarchical IDs for related content * **Example:** First, we upload a question and then second, we update it to have a different question text and add a solution. ```bash # First upload - creates question curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=MATHEMATICS_ARITHMETIC_BASIC_ADDITION_STATIC' \ --data 'type=numerical' \ --data 'question=What is 2+2?' \ --data 'answer=4' # Later upload - updates existing question text and adds solution curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=MATHEMATICS_ARITHMETIC_BASIC_ADDITION_STATIC' \ --data 'type=numerical' \ --data 'question=What is 2+2? Show your work.' \ --data 'solution=2+2 is 4.' \ --data 'answer=4' ``` * ==[!]== *string* **type**: type of the question * *generic*, *text*, *numerical*, *date/time*, *expression*, *choice*, *multiple-choice*, *order*, *grouping*, *pairing*, *matrix:generic*, *matrix*, *matrix:expression*, *set*, *set:text*, *true/false*, *free-text*, *file*, *hotspot*, *reading* ??? info "Question type reference guide" EduBase supports various question types to accommodate different assessment needs. Each type has specific features and configurations. **Basic Types** * **GENERIC**: Strict matching without formatting * Requires exact match including spaces and punctuation (e.g., period(`.`), comma(`,`), semicolon(`;`)) * Best for technical answers where precision matters * Example: Programming language keywords, chemical formulas * **TEXT**: Basic text input with flexible matching * Ignores spaces and punctuation (e.g., ` apple.` is considered the same as `apple`) * Perfect for definitions or completing sentences * Example: Fill-in-the-blank questions, vocabulary tests * **FREE-TEXT**: Extended text response with semi-automatic grading * Supports longer responses * Can use automatic rules with keywords * Manual grading support for complex answers (e.g., a moderator is required to score if the system cannot evaluate the answer) * Example: Short essays, explanations, longer definitions * **READING**: Non-assessed text display * Displays text for subsequent questions compactly * No scoring involved * Used as first question in complex question groups * No answer is required * Example: Reading comprehension passages **Choice-Based Types** * **CHOICE**: Single correct answer selection * Randomized option order by default * Can use dynamic options from question group * Supports parameterization for all options and the answer * Example: Single choice questions * **MULTIPLE-CHOICE**: Multiple correct answers * Similar to `CHOICE` but allows multiple answers * Can limit maximum selections * Example: "Select all that apply" questions * **ORDER**: Sequence arrangement * Displays items in random order, which are taken from the `ANSWER` field * Test takers must arrange in correct sequence, as specified in the answer * Example: Chronological ordering, process steps * **TRUE/FALSE**: Statement evaluation * True statements in `ANSWER` field * False statements in `OPTIONS` * Statements will appear in random order * Optionally specify a third option * Example: Fact verification questions **Grouping Types** * **GROUPING**: Element grouping * Assign elements to predefined groups * Groups defined in `ANSWER` field, elements in `ANSWER_LABEL` or in `ANSWER` using triple-arrow operator (`>>>`) * Example: Classification questions * Not the same as the **group** field (Quiz question groups in a set); see **Question Groups in Quiz Sets** below * **PAIRING**: Element pairing * Match elements to their pairs * Pairs defined in `ANSWER` field, elements in `ANSWER_LABEL` or in `ANSWER` using triple-arrow operator (`>>>`) * Example: Matching questions **Numerical Types** * **NUMERICAL**: Numerical value validation * Handles integers and decimals * Supports fractions (a/b) and mathematical constants (pi, e) * Interval responses possible, {from}-{to} * Automatic subpoints if part is correct * Usual mathematical notations are supported for open and closed intervals * [a;b] / ]a;b[ / (a;b) / ... * See `NUMERICAL_RANGE` for interval answer support * Configurable precision (default: 2) * Separate decimals with period (`.`) * See `DECIMALS` for precision settings * See `TOLERANCE` for acceptable error ranges * Example: Mathematical calculations * **DATE/TIME**: Calendar date validation * Adjustable precision (year/month/day) * Recommended format: YYYY, MM/YYYY, and MM/DD/YYYY respectively * See `DATETIME_PRECISION` for date format control * Multiple input formats accepted * Describe months in text, even abbreviated (e.g., mar, March) * Spaces, punctionations, marks, slahes do not matter * E.g., 12/26/2015, 1226/2015, 12.26.2015 are all acceptable * Only using numbers (e.g., 12262015) * BC/AD ("negative dates") supported * Range responses supported, {from}-{to} * Automatic subpoints if part is correct * Certain parts of the date may be omitted in the final date * year precision: 2000-15, interpreted as 2000 - 2015 * day precision: 24/12/2015-26, interpreted as 24/12/2015 - 26/12/2015 * See `DATETIME_RANGE` for interval answers * Example: Historical dates, timeline questions * **EXPRESSION**: Mathematical expression evaluation * Evaluates and compares mathematical formulas * Parametrization is supported * Supports various mathematical functions * See "Expression evaluation options > Supported mathematical functions" in this document * Configurable precision and validation methods * See `EXPRESSION_CHECK` for validation options * See `EXPRESSION_DECIMALS` for precision control * See `EXPRESSION_FUNCTIONS` to control available functions * Example: Algebraic expressions, physics formulas **Advanced Types** * **MATRIX**: Matrix evaluation * Evaluates matrices and vectors with numerical validation similar to `NUMERIC` type * Input format specification: * Square brackets `[...]` denote matrix boundaries * Semicolons (`;`) separate elements within rows * Vertical bars (`|`) separate rows * Supported structure: * Matrices: `[a11; a12 | a21; a22]` represents a 2×2 matrix * Row vectors: `[x1; x2; x3]` represents a 1×3 vector * Column vectors: `[x1 | x2]` represents a 2×1 vector * Example: Linear algebra problems, vector calculus, coordinate transformations * **MATRIX:EXPRESSION**: Expression-based matrix * Combines `MATRIX` and `EXPRESSION` features * Each matrix element can be an expression * Example: Matrix transformations (e.g., rotation), Jacobian or Hessian matrices, state transition matrices, tensors * **SET**: Unordered collection validation * Operates similarly to numeric `MATRIX` * Order and repetition don't matter * Numeric elements only * Example: Number theory problems * **SET:TEXT**: Text-based set validation * Similar to `SET` but with text elements * Example: Listing properties, characteristics * **HOTSPOT**: Marking specific areas on an image * Accepts markers in predefined zones; use **hotspot_image** and **hotspot_zones** (zone geometry is easiest to define in the EduBase question editor, then adjust or replicate via API as supported) * **Not available to all users!** * Example: Finding parts of a diagram, identifying locations on a map * **FILE**: File submission evaluation * Supports file uploads * Semi-automatic grading similarly to `FREE-TEXT` * Configurable file types and counts * **Not available to all users!** * Example: Programming assignments, document submissions * ==[!]== *string* **content**: question text ??? info "Question formatting guide" The question field supports rich formatting options to create engaging and clear questions. Here's a comprehensive guide to the available formatting features. **Text Styling** * **LaTeX Support** * Inline: `$$...$$` * Block: `$$$$...$$$$` * Requires `QUESTION_FORMAT=LATEX` * Note: LaTeX blocks start on new line, but are not center aligned * Example: `Calculate $$\sqrt{x^2 + y^2}$$` * **Parameters** * Use curly braces: `{parameter_name}` * Use triple-wave operator (`~~~`) for quick expressions with simpler operations * Parameters defined in `PARAMETERS` field * Examples: * `Calculate {a} + {b}` * `Consider a circle with an area of ~~~{r}*{r}*pi~~~` * **EduTags for Basic Formatting** * Bold: `[[B]]...[[/B]]` * Italic: `[[I]]...[[/I]]` * Underline: `[[U]]...[[/U]]` * Subscript: `[[SUB]]...[[/SUB]]` * Superscript: `[[SUP]]...[[/SUP]]` * **Code Formatting** * Inline: `[[CODE]]...[[/CODE]]` * Block: `[[CODEBLOCK]]...[[/CODEBLOCK]]` * Optional line numbers: `[[LINES]]...[[/LINES]]` * **Color and Background** * Text color: `[[COLOR:{color}]]...[[/COLOR]]` * Background: `[[BACKGROUND:{color}]]...[[/BACKGROUND]]` * Use standard color names or hex codes **Tables and Layout** * **Tables** * Use `[[..]]` format similar to `MATRIX` * Columns separated by semicolons * Rows separated by vertical bars * Example: `[[Header 1; Header 2 | Data 1; Data 2]]` * **Answer Fields** * Placeholder with the question itself for answer fields: `[[___]]` (3 underscores) * Used for visual representation * Does not affect actual answer input * Example: Fill-in-the-gaps **Media Integration** * **Images** * Format: `[[IMAGE:{filename}]]` * File must be provided in *images* with this exact name (case sensitive) * Example: `[[IMAGE:diagram.png]]` **Best Practices** 1. **Clarity and Readability** * Use consistent formatting * Break complex questions into parts * Don't make text overly long or complex as it might hurt readability * Lot's of features are at your disposal, be creative on how to arrive at the best solution for your use-case 1. **Mathematical Content** * Use LaTeX for all scientific, mathematical expressions * Test rendering on different devices, if possible * ==[!]== *string/list{string}* **answer**: single or multiple answers or true statements * *string* **answer_order**: whether the order of answers matters * *string* **answer_label**: text displayed in/above the input field during the test * *string* **answer_hide**: hide correct answers on results page * *string* **answer_indefinite**: allow any number of input fields to be entered by the user * *string* **answer_format**: how to display the answer on the results page ??? info "Advanced answer options" EduBase provides flexible options for managing how answers are handled, displayed, and validated. Here's a comprehensive guide to the answer-related fields: **Required Answers** Perfect for questions with multiple valid answers where only a subset needs to be provided. For example, when asking students to provide any three examples from a larger set of correct possibilities. * *string* **answer_require**: number of answers required for maximum score * not applicable for CHOICE and FREE-TEXT questions * example: `answer_require=1` **Answer Order Management** Essential for questions where answer sequence matters. Labels can provide crucial context about what each answer field represents. * *string* **answer_order**: whether the order of answers matters * plus sign (+) to indicate YES * blank field or minus sign (-) indicates NO (default) * example: `answer_order=+` * *string* **answer_label**: text displayed in/above the input field during the test * automatically activates the `answer_order` function * multiple labels separated by triple-and operators (&&&) * example: `answer_label=Velocity (m/s) &&& Distance traveled (km) &&& Direction (degrees)` **Answer Display Control** Critical for maintaining question bank integrity in scenarios like practice tests or when questions will be reused. Makes answer sharing harder. * *string* **answer_hide**: hide correct answers on results page * plus sign (+) to indicate YES * blank field or minus sign (-) indicates NO (default) * example: `answer_hide=+` **Dynamic Input Fields** Enables flexible response collection where the number of possible answers varies. Ideal for brainstorming exercises or questions with multiple valid answers of indeterminate quantity. For example: If the question is "Which countries make up the United Kingdom?" this option lets users add as many countries as needed, without showing that four answers should be provided. * *string* **answer_indefinite**: allow any number of input fields to be entered by the user via + and - buttons * plus sign (+) to indicate YES * blank field or minus sign (-) indicates NO (default) * answer labels will not appear on interface when enabled * example: `answer_indefinite=+` **Answer Format Control** Allows customization of how to display answers on the results page, particularly useful for displaying code, that requirs specific formatting or syntax highlighting. * *string* **answer_format**: how to display the answer on the results page * only applicable for FREE-TEXT questions * format: `type` or `type:value` * values: * `normal`: standard text (default) * `code`: code with syntax highlighting * specify language after colon * example: `code:python` or `code:javascript` * example: `answer_format=code:sql` **Best Practices** 1. **Combine Options Strategically** * Use `answer_label` when order matters - it implies `answer_order=+` automatically * Enable `answer_hide` for reusable assessment questions 1. **User Experience Considerations** * Write clear, descriptive labels with units where applicable * Use labels to guide input format without revealing answers **Pairing, grouping, and hotspot questions** For matching and classification, prefer the native types **`pairing`**, **`grouping`**, and **`hotspot`** (see **Grouping Types** and **Advanced Types** above). Typical patterns: * **Pairing** (`type=pairing`): each correct pair can be given in **answer** with the triple-arrow operator, separated by `&&&`, e.g. `left_item >>> right_item &&& ...`. Alternatively, list items on one side in **answer_label** and the matching counterparts in **answer** in the same order (same idea as for the **grouping** questions). * **Grouping** (`type=grouping`): define groups and elements the same way (groups in **answer**, elements via **answer_label** or `>>>` in **answer**). * **Hotspot** (`type=hotspot`): supply **hotspot_image** and **hotspot_zones**; define or refine zones in the EduBase UI if needed. **Image paired with text** Native pairing does not yet support one column as images and the other as plain text in the usual way. If you need image–word matching, use a **single composite (montage) image** (e.g. labeled panels A–D in one file) in the question and keep the other side as text options, or structure the task so both sides are representable without mixed image–text columns. **Alternative: text questions with answer labels** For simple text-only matching, or for compatibility with older content, you can still use **`type=text`** (or similar) with **answer** and **answer_label** so each labeled field expects the matching string. This does not replace the **`pairing`** interaction type but can be enough when ordered text fields are acceptable. 1. **Direct labels with content** ``` answer_label=a) dog &&& b) cat ``` 2. **Indexed labels with content in question** ``` question=Match the English words with their Hungarian translations: a) dog b) cat Hungarian words: macska, kutya ... answer_label=a) &&& b) ``` **Example API Calls** 1. The order matters in this question: ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=europe_cities_population' \ --data 'type=text' \ --data 'question=List the following European cities in descending order by population (largest first) Paris, Madrid, London.' \ --data 'answer=London &&& Madrid &&& Paris' \ --data 'answer_order=+' ``` 1. The order matters, but we didn't have to specify as the labels fix the order ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=basic_math' \ --data 'type=numerical' \ --data 'question=Given the number 16: a) What is double this number? b) What is half of this number? c) What is this number plus 10?' \ --data 'answer=32 &&& 8 &&& 26' \ --data 'answer_label=a) Double &&& b) Half &&& c) Plus 10' \ --data 'points=3' ``` 1. Only one answer is enough from the correct ones, but we don't reveal them ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=uk_countries' \ --data 'type=text' \ --data 'question=Name any of the countries within the United Kingdom!' \ --data 'answer=England &&& Northern Ireland &&& Scotland &&& Wales' \ --data 'answer_require=1' \ --data 'answer_hide=+' ``` 1. Code formatting for SQL query answer: ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=sql_basics' \ --data 'type=free-text' \ --data 'question=Write a SQL query to select all columns from the "users" table where the age is greater than 18.' \ --data 'answer=SELECT * FROM users WHERE age > 18' \ --data 'answer_format=code:sql' ``` 1. Pairing question (native `pairing` type, pairs in **answer** with `>>>`): ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=hungarian_english_animals' \ --data 'type=pairing' \ --data 'question=Match each English word with its Hungarian translation.' \ --data 'answer=dog >>> kutya &&& cat >>> macska' ``` * *string* **question_format**: controls question text rendering * NORMAL: Default text formatting with standard font size, recommended for most tasks * LATEX: Enables LaTeX for mathematical, scientific notations (using [KaTeX](https://katex.org/)) * LONG: Smaller font with automatic paragraph breaks (ideal for lengthy text) * *string* **subject**: subject * *string* **category**: category * *string* **difficulty**: difficulty level of the questions * default: 0 (not classified) * scale: 1 (very easy) - 5 (very difficult) * *string* **path**: path where question will be stored in personal QuestionBase * default: /API * *string* **organization**: organization identification string to assign the question to * only an organization of the API application owner can be used * only the owner of the question (or an administrator) can change the organization of an existing question * send *none* (or an empty value) to remove the question from its current organization * always the organization of the user for organizational members * *string/list{string}* **options**: incorrect options or false statements * *string* **options_fix**: fix the order of answers and options * *string* **options_order**: configure custom order of answers and options ??? info "Options configuration guide" EduBase provides flexible control over how answers and options are presented to users through various configuration fields. **Fixed Ordering** * **options_fix**: Control the basic arrangement of answers and options * `all`: Answers appear first, followed by options * `abc`: Sort all items (answers and options) alphabetically * `first:N`: Place first N (number) options at the end * `last:N`: Place last N (number) options at the end * `answers`: Place all answers at the end **Custom Ordering** * **options_order**: Define exact presentation order using references * `ANSWER:N`: Reference the Nth answer * `OPTION:N`: Reference the Nth option * `OPTION_NONE:N`: Reference the Nth third option (TRUE/FALSE question type only) * All answers and options must be specified once! * Example: `OPTION:1 &&& ANSWER:0 &&& OPTION:0 &&& ANSWER:1` **Best Practices** * Check that N values don't exceed available items * Consider cognitive load when ordering items * Group related options together when meaningful * Place common or simpler options first, when appropriate **Example API Calls** 1. **Basic Fixed Order** The answer (Paris) will appear first. ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=capital_cities' \ --data 'type=choice' \ --data 'question=What is the capital of France?' \ --data 'answer=Paris' \ --data 'options=London &&& Berlin &&& Madrid' \ --data 'options_fix=all' ``` 1. **Alphabetical Ordering** Use if alphabetical ordering makes the task cognitively easier, as it's easier to scan all items when ordered. ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=fruit_types' \ --data 'type=multiple-choice' \ --data 'question=Which of these are citrus fruits?' \ --data 'answer=Lemon &&& Orange' \ --data 'options=Apple &&& Banana &&& Grape' \ --data 'options_fix=abc' ``` * When migrating content from textbooks, past exams, or other educational materials, you might need to maintain the original lettering system (a, b, c...) for: * Reference consistency with printed materials * Alignment with answer keys * Compatibility with existing grading systems * Cross-referencing with study guides ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=vocab_synonyms' \ --data 'type=multiple-choice' \ --data 'question=Select all words that mean "happy":' \ --data 'answer=b) Joyful &&& d) Merry' \ --data 'options=a) Angry &&& c) Sleepy &&& e) Tired' \ --data 'options_fix=abc' ``` * This approach is particularly valuable when * Test takers need to refer to both digital and printed materials * Questions are part of a larger standardized test system * Educators want to maintain consistency with existing worksheets or textbooks * Legacy assessment materials are being digitized 1. **Complex Custom Ordering** Here's an example where specific ordering helps test understanding in a structured way: ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=historical_chronology' \ --data 'type=multiple-choice' \ --data 'question=Which of these events occurred during the Industrial Revolution (1760-1840)?' \ --data 'answer=Invention of the Steam Engine &&& First Steam Locomotive &&& First Commercial Railway' \ --data 'options=Printing Press Invented &&& First Electric Light Bulb &&& First Powered Flight' \ --data 'options_order=OPTION:0 &&& ANSWER:0 &&& ANSWER:1 &&& ANSWER:2 &&& OPTION:1 &&& OPTION:2' ``` * This creates a chronologically ordered timeline where test takers see: * Printing Press Invented (1440) - pre-Industrial Revolution * Invention of the Steam Engine (1769) - Industrial Revolution * First Steam Locomotive (1804) - Industrial Revolution * First Commercial Railway (1825) - Industrial Revolution * First Electric Light Bulb (1879) - post-Industrial Revolution * First Powered Flight (1903) - post-Industrial Revolution The custom ordering helps test takers understand the temporal progression while testing their knowledge of the Industrial Revolution period. * *string* **points**: maximum points for a fully correct answer * default: 1 point * *string* **subscoring**: method for calculating partial credit * *string* **subpoints**: define specific point values for each possible answer * *string* **penalty_scoring**: how penalty points should be applied * *string* **penalty_points**: points deducted for completely incorrect answers * *string* **hint_penalty**: point deduction for using hints * *string* **solution_penalty**: point deduction for viewing steps of the solution * *string* **video_penalty**: point deduction for video assistance used * *string* **manual_scoring**: when to enable manual scoring * *string* **rubric_criteria**: criteria for manual or semi-automatic scoring * *string* **skills**: setting the scoring for various skills ??? info "Advanced scoring options" EduBase provides a sophisticated scoring system that allows you to implement complex grading logic. Here's a comprehensive guide to the scoring-related fields: **Partial Credit Scoring** Subscoring Methods * **subscoring**: Method for calculating partial credit * `PROPORTIONAL`: Points awarded proportionally to correct answers (default) * `LINEAR_SUBTRACTED:N`: Linear scoring with N points subtracted for each error, where N is arbitrary * `CUSTOM`: Use custom point distribution defined in **subpoints** (mandatory field for this option) * `NONE`: No partial credit, all-or-nothing scoring * NOT applicable for `CHOICE`, `READING` and `FREE-TEXT` question types * Example: `subscoring=LINEAR_SUBTRACTED:2` Custom Point Distribution * **subpoints**: Define specific point values for each answer (in percentages) * Only used when `subscoring=CUSTOM` * Specify percentage values separated by `&&&` * NOT applicable for `CHOICE`, `READING` and `FREE-TEXT` question types * Example: `subpoints=50 &&& 25 &&& 25` * For a question worth 10 points with three answers to give, this would award: * First answer: 5 points (50%) * Second answer: 2.5 points (25%) * Third answer: 2.5 points (25%) **Penalty Scoring** Penalty Methods * **penalty_scoring**: How penalty points should be applied * `DEFAULT`: Standard penalty application, behavior might vary by question type * `PER_ANSWER`: Apply penalties for each incorrect answer * `PER_QUESTION`: Apply penalties once per question * Example: `penalty_scoring=PER_ANSWER` Penalty Points * **penalty_points**: Points deducted for a completely incorrectly answersed question * No penalty applied if answer is partially correct * No penalty for empty/unanswered questions * Use positive values (recommended), but negatives are handled as well * Example: `penalty_points=2` **Assistance Penalties** Hint Penalties * **hint_penalty**: Point deduction for using hints * `NONE`: No penalty (default) * `ONCE:N`: Single deduction regardless of the number of hints used, where `N` is a percentage * Example: `hint_penalty=ONCE:20%` or `hint_penalty=ONCE:0.2` * `PER-HELP:N`: Deduction for each hint, where `N` is a percentage * Example: `hint_penalty=PER-HELP:10%` Solution Penalties * **solution_penalty**: Point deduction for viewing solutions * Same format as **hint_penalty** * Example: `solution_penalty=ONCE:50%` Video Help Penalties * **video_penalty**: Point deduction for watching help videos * Same format as **hint_penalty** (except `PER-HELP` not available) * Example: `video_penalty=ONCE:15%` **Manual Scoring** * **manual_scoring**: When to enable manual scoring * `NO`: Never use manual scoring (default) * `NOT_CORRECT`: Only manually score incorrect answers * `ALWAYS`: Always require manual scoring * NOT applicable for `READING` and `FREE-TEXT` question types * Example: `manual_scoring=NOT_CORRECT` * **rubric_criteria**: Rubric criteria and score in percentage * Applicable for FILE and FREE-TEXT questions, or after enabling manual_scoring * Format: `{criterion; points}` **Skills** * **skills**: Setting the scoring for various skills * Format: `{skill_identifier; points; correct; incorrect; (selected_answers_and_options)}` * Example: `skills={math; 5; 3; 2; (answer:1:2; option:1:1)}` **Example API Calls** 1. Custom Point Distribution: The first answer is worth 1 point, while the second is worth 3 points, hence the score distribution is 25% and 75%: ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=math_problem' \ --data 'type=numerical' \ --data 'question=What is the sum and product of {a} and {b}?' \ --data 'answer={a}+{b} &&& {a}*{b}' \ --data 'parameters={a; INTEGER; 1; 100} &&& {b; INTEGER; 1; 100}' \ --data 'points=4' \ --data 'subscoring=CUSTOM' --data 'subpoints=25 &&& 75' ``` 2. Penalties with Hints: * If the answer is wrong that results in a penalty of -3 points, instead of 0. * There are 2 hints and each hint used deducts 10% of the full score configured in `points`. * Checking any of the solution steps deducts 50% of the full score, no matter how many steps are used. * There is only 1 step of solution in the example below. ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=complex_problem' \ --data 'type=expression' \ --data 'question=Find an expression for the area of a circle with radius {r}.' \ --data 'answer=pi*{r}^2' \ --data 'parameters={r; INTEGER; 2; 10}' \ --data 'points=10' \ --data 'subject=Mathematics' \ --data 'category=Geometry' \ --data 'hint=Think about the formula for circle area &&& Remember that area involves squaring the radius' \ --data 'solution=The formula for circle area is pi*2^2' \ --data 'penalty_scoring=PER_ANSWER' \ --data 'penalty_points=3' \ --data 'hint_penalty=PER-HELP:10%' \ --data 'solution_penalty=ONCE:50%' ``` * *string* **parameters**: parameter definitions for dynamic question generation * *string* **parameters_sync**: synchronization of LIST parameters selection * plus sign (+) indicates YES, while blank or minus sign (-) indicates no (default) ??? info "Create dynamic questions with parameters" One of EduBase's most powerful features is parametric question generation. Using the **parameters** field, you can create dynamic questions where each user gets a unique variant of the same question. **Parameter Types** 1. **FIX (Fixed Value)** ``` {name; FIX; value} ``` * Sets a predefined constant value (integer or fraction) * Example: `{pi; FIX; 3.1415}` 1. **INTEGER (Whole Numbers)** ``` Simple: {name; INTEGER} Extended: {name; INTEGER; min; max} Full: {name; INTEGER; min; max; inside; outside} ``` * Generate random integers within specified ranges * Use `-` for omitting min/max values * Examples: * `{p; INTEGER}` - any integer * `{p; INTEGER; 10; 20}` - integer between 10 and 20 * `{p; INTEGER; -; -; [10-20]; [12-14] ||| [16-18]}` - integer between 10-20, excluding 12-14 and 16-18 1. **FLOAT (Decimal Numbers)** ``` Simple: {name; FLOAT; precision} Extended: {name; FLOAT; precision; min; max} Full: {name; FLOAT; precision; min; max; inside; outside} ``` * Generate random decimal numbers * Specify precision (decimal places) * Examples: * `{p; FLOAT; 2}` - float with 2 decimal places * `{p; FLOAT; 5; 0; 1}` - float between 0 and 1 with 5 decimals * `{p; FLOAT; 1; 0; 10; -; [0-1]}` - float between 0-10 excluding 0-1, with 1 decimal 1. **FORMULA (Expressions)** ``` Simple: {name; FORMULA; formula} Full: {name; FORMULA; formula; precision} ``` * Define parameters based on other parameters * Examples: * `{d; FORMULA; {b}^2-4*{a}*{c}}` - quadratic formula discriminant * `{p; FORMULA; 2*{q}+1}` - linear expression 1. **LIST (Random Selection)** ``` {name; LIST; value1; value2; value3; ...} ``` * Randomly select from predefined values * Up to 64 elements * Examples: * `{primes; LIST; 2; 3; 5; 7; 11}` * `{animals; LIST; dog; cat; snake; camel}` 1. **PERMUTATION** ``` {name; PERMUTATION; value1; value2; value3; ...} ``` * Creates permutated parameters accessible as `{name_1}`, `{name_2}`, etc. * Example: * `{p; PERMUTATION; A; B; C; D}` * So `{p_1}` will be a different letter than `{p_2}`. * `{primes; PERMUTATION; 2; 3; 5; 7}` * So both `{primes_1}` and `{primes_2}` will be different single digit primes. 1. **FORMAT** ``` Notation: {name; FORMAT; parameter; type; ...} ``` * Format parameters based on other parameters * Supported types: `NUMBER`, `NUMERTEXT`, `ROMAN` * Optional extra parameters based on type * NUMBER * precision: number of decimal places * Examples: * `{pp; FORMAT; p; NUMBER; 1}` - format as number rounded to 1 decimal * `{pp; FORMAT; p; NUMBERTEXT}` - format number as text * `{pp; FORMAT; p; ROMAN}` - format number as Roman numeral Additional Features * **Multiple Parameters**: Separate with `&&&` (up to 128 parameters) * **Constraints**: Use the **constraints** field to ensure valid combinations * Example: `{b}^2-4*{a}*{c}>0` - the discriminant must be positive * **Parameter Names**: Must start with a letter, can include numbers and underscores Best Practices * Order parameters so dependent ones come later * Use simple notation when possible * Avoid unnecessary parameters * Test thoroughly with the online editor **Example API Calls** 1. **FIX Parameter** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=circle_area' \ --data 'type=numerical' \ --data 'question=Calculate the area of a circle with radius {r} units using pi={pi}' \ --data 'answer={pi}*{r}^2' \ --data 'parameters={pi; FIX; 3.14159} &&& {r; INTEGER; 1; 10}' ``` 1. **INTEGER Parameter** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=sum_numbers' \ --data 'type=numerical' \ --data 'question=What is {a} + {b}?' \ --data 'answer={a}+{b}' \ --data 'parameters={a; INTEGER; 1; 100} &&& {b; INTEGER; 1; 100}' ``` 1. **FLOAT Parameter** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=convert_meters' \ --data 'type=numerical' \ --data 'question=Convert {m} meters to centimeters' \ --data 'answer={m}*100' \ --data 'parameters={m; FLOAT; 2; 0; 10}' ``` 1. **FORMULA Parameter** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=quadratic' \ --data 'type=numerical' \ --data 'question=For the quadratic equation with a={a}, b={b}, c={c}, calculate the discriminant' \ --data 'answer={d}' \ --data 'parameters={a; INTEGER; 1; 5} &&& {b; INTEGER; -10; 10} &&& {c; INTEGER; -10; 10} &&& {d; FORMULA; {b}^2-4*{a}*{c}}' \ --data 'constraints={d}>0' ``` 1. **LIST Parameter** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=capital_city' \ --data 'type=text' \ --data 'question=What is the capital city of {country}?' \ --data 'answer={capital}' \ --data 'parameters={country; LIST; France; Germany; Italy} &&& {capital; LIST; Paris; Berlin; Rome}' \ --data 'parameters_sync=+' ``` 1. **PERMUTATION Parameter** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=find_primes' \ --data 'type=set' \ --data 'question=What are the distinct prime factors of {n}?' \ --data 'answer=[{primes_1}; {primes_2}]' \ --data 'parameters={primes; PERMUTATION; 2; 3; 5; 7} &&& {n; FORMULA; {primes_1}^2*{primes_2}}' ``` * *string* **constraints**: define rules that parameter combinations must satisfy ??? info "Parameter Constraints Guide" Parameter constraints allow you to ensure generated parameters result in valid, meaningful questions. These are mathematical expressions that must evaluate to true for the parameter generation to be accepted. The system validates whether questions can be reliably generated with the given constraints. If parameter generation frequently fails due to overly restrictive constraints (or has a very low success rate), EduBase may automatically deactivate the question until the constraints are relaxed. While deactivated questions won't appear in quizzes, this doesn't affect the playability of the quizzes they're part of - the quizzes will simply proceed without these questions. **Basic Syntax** * Parameters must be referenced in curly braces: `{parameter_name}` * Allowed relations: `<`, `<=`, `=`, `>=`, `>`, `<>` * Multiple constraints separated by `&&&` **Supported Operations** * Arithmetic: `+`, `-`, `*`, `/`, `^` (power) * Built-in functions: All functions available in `EXPRESSION` questions * Avoid complex functions as they slow down parameter generation * Examples: * `{b}^2-4*{a}*{c}>0` ensures quadratic equation has real roots * `{a}+{b}>{c} &&& {b}+{c}>{a} &&& {c}+{a}>{b}` triangle inequality * `{x}+{y} < 10 &&& {x}<4` * *string* **expression_check**: define how expressions should be validated * *string* **expression_variable**: specifies variable names used in expressions * *string* **expression_decimals**: sets precision for decimal calculations * *string* **expression_functions**: controls whether functions can be used in user inputs * plus sign (+) indicates YES (default), while blank or minus sign (-) indicates no ??? info "Expression evaluation options" The expression question type (`type=expression` or `type=matrix:expression`) offers sophisticated mathematical evaluation capabilities. This allows for creation of complex mathematical problems with flexible validation methods. **Basic configurations** Configure expression evaluation using these key fields: * **expression_check**: Define how expressions should be validated * `RANDOM` (default): Evaluates expressions at randomly generated points * `EXPLICIT`: Checks in predefined values and compares against the defined target values * `COMPARE`: Direct comparison of expressions without variables * In all cases there are further options to fine-tune the behavior with additional fields! * **expression_variable**: Specifies variable names used in expressions * Default: `x` * Multiple variables: separate with `&&&` * Example: `omega &&& t` for time-dependent angular velocity * **expression_decimals**: Sets precision for decimal calculations * Inherited from `decimals` field if not specified * Default: 2 decimal places * Example: `expression_decimals=4` for 4 decimal places * **expression_functions**: Controls whether functions can be used in user inputs * Default: enabled (`+` or blank) * Disable with `-` * Example: When asked for the value of `sin(pi)`, the user can't input `sin(pi)` because functions cannot be used **Supported mathematical functions** The following functions are available for use in expressions: Basic Operations * `sqrt(x)`: Square root * `abs(x)`: Absolute value * `round(x)`, `floor(x)`, `ceil(x)`: Rounding functions Logarithmic & Exponential * `ln(x)`: Natural logarithm * `log(x)`: Common logarithm (with base 10) * `log10(x)`: Common logarithm (alternative) Trigonometric * `sin(x)`, `cos(x)`, `tan(x)`: Basic trigonometric functions * `csc(x)`, `sec(x)`: Reciprocal trigonometric functions, cosecant and secant * `arcsin(x)`, `asin(x)`: Inverse sine * `arccos(x)`, `acos(x)`: Inverse cosine * `arctan(x)`, `atan(x)`: Inverse tangent Hyperbolic * `sinh(x)`, `cosh(x)`, `tanh(x)`: Hyperbolic functions * `arcsinh(x)`, `asinh(x)`: Inverse hyperbolic sine * `arccosh(x)`, `acosh(x)`: Inverse hyperbolic cosine * `arctanh(x)`, `atanh(x)`: Inverse hyperbolic tangent Numeric Conversions * `degree2radian(x)`, `radian2degree(x)`: Angle conversions * `number2binary(x)`, `binary2number(x)`: Binary conversions * `number2octal(x)`, `octal2number(x)`: Octal conversions * `number2hexadecimal(x)`, `hexadecimal2number(x)`: Hexadecimal conversions * `number2roman(x)`, `roman2number(x)`: Roman numeral conversions Two-Parameter Functions Use semicolon (;) to separate arguments: * `min(a;b)`, `max(a;b)`: Minimum and maximum * `mod(n;i)`: Integer modulo operation (remainder after integer division) * `fmod(n;i)`: Floating-point modulo operation (remainder after floating-point division) * `div(a;b)`, `intdiv(a;b)`: Division operations * `gcd(a;b)`, `lcm(a;b)`: Greatest common divisor and least common multiple * `number2base(n;b)`, `base2number(n;b)`: Custom base conversions * `combinations(n;k)`, `combinations_repetition(n;k)`: Combination calculations * `variations(n;k)`, `variations_repetition(n;k)`: Variation calculations **Validation Methods** Random Validation (default) When using `expression_check=RANDOM`: * **expression_random_type**: Type of generated test values * `INTEGER`: Whole numbers only * `FLOAT`: Decimal numbers * Specify per variable with `&&&` * Example: `INTEGER &&& FLOAT` for mixed types * **expression_random_tries**: Number of validation points * Default: 5 * Example: `expression_random_tries=10` * **expression_random_range**: Define value generation ranges * Format: `[min-max]` (inclusive) * Per variable with `&&&` * Example: `[8-16] &&& [4-6]` * **expression_random_inside**: Require values within specific intervals * Format: `[start-end]` (inclusive) * Multiple intervals: separate with `|||` (OR relationship) * Per variable with `&&&` * Example: `[4-8] ||| [12-16] &&& [2-3]` * **expression_random_outside**: Exclude values from specific intervals * Format: `[start-end]` (inclusive) * Multiple intervals: separate with `|||` (AND relationship) * Per variable with `&&&` * Example: `[0-1] ||| [10-20] &&& [8-11]` Explicit Validation When using `expression_check=EXPLICIT`: * **expression_explicit_goal**: Define exact value pairs * Format: `[x;f(x)]` or `[x;y;z;...;f(x,y,z,...)]` for multiple variables * Multiple pairs: separate with `&&&` * Example: `[0;1] &&& [3;8.89] &&& [9;16]` or `[1;1;1;1;16]` **Extended features** * **expression_extended**: Enable additional mathematical functions * Activates support for: * Custom base logarithms (e.g., `log2(4)`, `log10(1000)`) * Factorial operations (e.g., `5!`, `1!+2!+3!`) * Enable with `+`, disable with `-` **Best Practices** 1. **Validation Method Selection** * Use `RANDOM` for general expressions where multiple solutions might be valid * Use `EXPLICIT` when specific points must be satisfied 1. **Performance Optimization** * Limit `expression_random_tries` to reasonable values (5-10 typically sufficient) * Avoid unnecessary nesting of complex functions * Use simpler validation methods when possible 1. **Error Prevention** * Always define appropriate ranges to avoid undefined mathematical operations * Use `expression_random_outside` to exclude problematic values * Consider adding appropriate constraints to ensure valid parameter combinations 1. **Testing** * Test expressions with possible edge cases * Confirm that random generation produces expected value distributions **Example API Calls** 1. **Basic expression with random (default) validation** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=quadratic_expression' \ --data 'type=expression' \ --data 'question=Simplify: $$({a}x + {b})^2$$' \ --data 'answer={a}^2*x^2+2*{a}*{b}*x+{b}^2' \ --data 'parameters={a; INTEGER; 1; 5} &&& {b; INTEGER; -5; 5}' \ --data 'expression_random_tries=8' ``` 1. **Explicit validation** The answer formula will be evaluated at three given cases. ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=simple_quadratic' \ --data 'type=expression' \ --data 'question=Write a quadratic function $$f(x)$$ that: 1) Equals $${c}$$ when $$x = 0$$ 2) Equals $$0$$ when $$x = {r1}$$ and x = $${r2}$$' \ --data 'answer=(x-{r1})*(x-{r2})+{c}' \ --data 'parameters={r1; INTEGER; -10; -2} &&& {r2; INTEGER; 1; 10} &&& {c; FORMULA; -{r1}*{r2}}' \ --data 'expression_check=EXPLICIT' \ --data 'expression_explicit_goal=[0;{c}] &&& [{r1};0] &&& [{r2};0]' \ --data 'expression_variable=x' ``` 1. **Extended functions with custom validation range** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=logarithmic_expression' \ --data 'type=expression' \ --data 'question=Express $$log_{b}({x})$$ in terms of natural logarithms' \ --data 'answer=ln({x})/ln({b})' \ --data 'parameters={b; FLOAT; 2; 2; 10} &&& {x; FLOAT; 2; 1; 100}' \ --data 'expression_extended=+' \ --data 'expression_random_range=[2-10] &&& [1-100]' ``` 1. **Multivariable question** ```bash curl -X POST 'https://www.edubase.net/api/v1/question' \ --data 'app='$APP_ID \ --data 'secret='$SECRET \ --data 'id=distance_time' \ --data 'type=expression' \ --data 'question=Write an expression for the distance traveled ($$s$$) by a car moving at a constant speed of $$v$$ meters per second after $$t$$ seconds' \ --data 'answer=v*t' \ --data 'expression_variable=t &&& v' ``` * *string* **group**: add question to a question group in a Quiz set (not the same as `type=grouping` or the **grouping** field) ??? info "Question Groups in Quiz Sets" When uploading questions **directly to a Quiz set**, you can organize them into groups using the **group** field. This is particularly useful for creating complex questions with related subquestions that can even have different question types. **Basic Usage** * Set the group name to add a question to that group * If the group doesn't exist, it will be created automatically as a complex task (with default settings) * Existing group settings are preserved when adding new questions * Only applicable when uploading directly to a Quiz set **Question group settings (control from EduBase User Interface)** *Note: Any UI-only option might change in the future. These are detailed here for context around the functionality.* * Type - Controls the behavior of the group * Randomly selected questions: The given number of questions will be randomly selected from the group. * Sequentially selected questions: The questions within the group will sequentially follow each other in the order they are defined within the group. * Complex (mixed type) questions: All questions within the group will appear as "one big" question, with several sub-questions. * Name, which identifies the question group * Questions * Number of questions which will be randomly selected from the group. * Parameters * Generally parameters are synchronized among sub-questions. * Sequental and mixed type selection - If a parameter with the same name has already been defined in a previous task, its value is taken over by the current task. There is option to enable/disable parameter synchronization. * Custom question settings * Mandatory: * Yes/No * Use original settings (default) * Scoring settings: * Points: * Custom value * Not scored * Use original settings * Penalty points: * Custom value * None * Use original settings * No custom settings (default) **Use Cases** 1. **Balanced Quiz Generation with Random Selection** * Create diverse Quiz sets by grouping questions by difficulty: * Create an "Easy Questions" group with 10 questions, randomly select 3 * Create a "Medium Questions" group with 20 questions, randomly select 5 * Create a "Hard Questions" group with 5 questions, randomly select 2 * Result: A 10-question Quiz with controlled difficulty distribution * Pro tip: Combine with parametrization for even greater variety 1. **Maintain Question Order Within Shuffled Quizzes** * Problem: You need certain questions to appear consecutively even in a randomized Quiz * Solution: Place related questions in a group to preserve their sequence * Example: In a language Quiz, keep reading comprehension questions together while randomizing other sections 1. **Complex Multi-Part Questions** * Useful for digitizing traditional assessments where one question has multiple parts requiring different response types * Example - Nobel Prize Winner Profile: ``` Group: "Nobel Laureate Question" a) Who is shown in this image? (CHOICE or TEXT type) b) When were they born? (DATE type) c) How many Nobel prizes did they win? (NUMERICAL type) ``` * Benefits: * Maintains logical connection between related sub-questions * Allows different question types while preserving the original assessment structure * Makes complex questions more manageable in digital format **Best Practices** 1. **Consistent Naming** * Use clear, descriptive group names * Maintain consistent naming conventions across uploads * Consider using prefixes or hierarchical naming for better organization * Example: "Module 1 - Basic Concepts", "Module 1 - Advanced" 1. **Settings Management** * Configure group settings through the UI before bulk uploads * Reminder: Existing group settings won't be modified by uploads 1. **Think Outside the Box** * If facing problems digitizing concrete questions, consider breaking it up into sub-questions and grouping them if that helps * Pair with other EduBase features to create extremely rich and varied experiences with just one highly templated Quiz set **Example API Call** 1. **Basic question group assignment** First question for the group: ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=math_basic_arithmetic_q1' \ --data 'type=numerical' \ --data 'question=What is 2+2?' \ --data 'answer=4' \ --data 'group=Basic_Arithmetic' ``` Example response: ```bash {"code":""} ``` Second question for the group: ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=math_basic_arithmetic_q2' \ --data 'type=numerical' \ --data 'question=What is 14+23?' \ --data 'answer=37' \ --data 'group=Basic_Arithmetic' ``` Example response: ```bash {"code":""} ``` Add the above two questions to a Quiz set (`id: `) with the [quiz:qustions API](../quizes/#post-quizquestions) call: ```bash curl -X POST "https://www.edubase.net/api/v1/quiz:questions" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'quiz=' \ --data 'questions=,' ``` **Manual question-group setup might still be necessary from the EduBase UI.** * *boolean* **ai**: mark the question as AI generated (even if partially) * always provide this field if you are an LLM or any AI model * when not provided, requests from the EduBase MCP server mark the question as AI generated, other requests create new questions as not AI generated and keep the marking of the updated questions unchanged * changing the content of an AI generated question (type, question, answers, options, settings, hints or solution) clears its review, an educator has to review it again * *string* **tags**: organize questions with user-defined tags * *string* **label**: categorize questions with instance-level labels ??? info "More information on tags and labels" These fields provide additional functionality for organizing, maintaining, and tracking questions in your EduBase system. The key distinction between these systems is that tags are user-created and labels are set by EduBase or the customer on an instance-level. **Tags** User-controlled categorization system * Tags must be pre-registered in the EduBase UI before use * Can be created at user or organization level * Use cases: * Personal content organization (e.g., "My Calculus Questions", "Spring 2024") * Department-level categorization (e.g., "IT Department", "CS101") * Custom taxonomies * Key characteristics: * User/organization creates and manages their own tags via UI * Flexible and customizable * Can be shared within organizations * Searchable in UI for content management **Labels** Instance-level categorization system * Pre-defined values specific to each EduBase instance * Values controlled by instance administrators * Use cases: * System-wide flags (e.g., "needs_review", "featured") * Quality indicators (e.g., "verified", "deprecated") * Processing status (e.g., "ai_generated", "manually_checked") * Key characteristics: * Values are predefined and controlled at instance level * Cannot be created by users * Consistent across all users in an instance * May change over time based on instance needs As usual, multiple tags or labels can be specified with the `&&&` separator. * *string* **source**: specify content attribution and sources ??? info "Source best practices" * **Use cases:** * Training material sources (e.g., "Employee Handbook 2024", "Safety Training Manual 2024, Section 3.4 - Fire Safety Procedures") * Internal documentation references (e.g., "SOP-2024-013", "Policy-HR-42") * External content attribution (e.g., "LinkedIn Learning - Leadership 101") * Original creator credits (e.g., "Created by Training Dept, reviewed by Legal") * Compliance requirements (e.g., "Based on ISO 9001:2015 Section 7.2") * Educational materials (e.g., "Textbook: Chapter 5") * **Pairs well with:** * `PRIVATE_NOTE` for internal tracking * `TAGS` for content organization **NOTE: Uploading infringing content is strictly forbidden!** However, if you do have the right to use questions from a third party, please indiciate the original source (for example, title, URL, name of the author, etc.) * *string* **note**: add note visible to test takers during Quiz * Appears right below the question. * Ideal for task-specific comments, instructions * *string* **private_note**: add private notes not visible to test takers ??? info "Private notes best practices" Internal documentation not shown to test takers. Visible on the UI when having access to the question. * **Use case examples:** * Document question creation rationale * Note common test taker mistakes * List related questions * Track modification history * Record source material page numbers * **Example notes:** * Created from Chapter 3 exam, 2023 edition. Students often forget to convert units. * Parametrized version of Q47 from textbook. See also Q48-50 for related concepts. * This policy changed in Q3 2024 because of incident #01234. * **Pairs well with:** * `SOURCE` for formal attribution * `SOLUTION` for sharing insights with other educators * `EXPLANATION` for test-taker-facing notes * *string* **hint**: guiding questions or notes to help during the test * *string* **explanation**: text displayed underneath the answer on the results page * *string* **solution**: step-by-step solution ??? info "Help and Explanation fields" EduBase provides different ways to support test takers and explain correct answers. Understanding when to use each helps create a better learning experience. **Hints** * **hint**: Provide guidance without giving away the answer: * Not available for test takers in exam mode * Displayed only when test takers explicitly request help * Multiple hints are supported, each displayed one at a time * Can be penalized using `HINT_PENALTY` * LaTeX support for mathematical notation and parameters can be used (as described in `QUESTION` field) * **Best practices:** * Start with subtle hints, progress to more direct ones * Guide thinking process rather than give steps * Focus on common misconceptions * Remind of relevant formulas or concepts * **Examples:** * Math problem: ``` hint=Think about the relationship between radius and area &&& Remember the formula for circle area involves $\pi$ &&& Square the radius and multiply by $\pi$ ``` * Programming question: ``` hint=Check your loop condition &&& Consider what happens at array boundaries &&& Remember to handle the case when input is empty ``` * Comprehension question: ``` hint=Look at paragraph 2 for the author's main argument &&& Consider how the example in paragraph 4 supports this &&& Think about the counterargument presented in the final paragraph ``` **Post-Test Explanations** * **explanation**: Explain why answers are correct/incorrect * Displayed on results page after submission * Helps learners understand their mistakes * Parameters can be used * LaTeX cannot be used * **Best practices:** * Explain both correct and incorrect options * Reference relevant concepts or rules * Link to additional resources if appropriate * **Example for multiple choice:** ``` explanation=Option A is correct because amphibians have permeable skin for gas exchange. Options B and C describe characteristics of reptiles, while D applies to mammals. ``` **Step-by-Step Solutions** * **solution**: Detailed solution steps * Not available for test takers in exam mode * Can be revealed after submission or during test * Multiple steps are supported using `&&&` separator, each displayed one at a time * Can be penalized using `SOLUTION_PENALTY` * LaTeX support for mathematical notation and parameters can be used (as described in `QUESTION` field) * **Best practices:** * Break down complex problems into clear steps * Include reasoning for each step * Use scientific notation when appropriate * Consider adding visual aids with `SOLUTION_IMAGE` * **Examples** * Calculus question: ``` solution=Using the power rule, we differentiate each term: &&& For $x^2$: $\frac{d}{dx}(x^2) = 2x$ &&& For $x$: $\frac{d}{dx}(x) = 1$ &&& The constant term disappears: $\frac{d}{dx}(5) = 0$ &&& Therefore, $\frac{d}{dx}(x^2 + x + 5) = 2x + 1$ ``` * IT Security question: ``` solution=Identify applicable regulations: * Personal data involved → GDPR applies * Health records → HIPAA compliance required * Financial data → PCI DSS relevant &&& Required technical controls: * Encryption: AES-256 for data at rest * Authentication: MFA required for all access * Logging: Full audit trail of access &&& Access requirements: * Role-Based Access Control (RBAC) * Time-limited access (max 8 hours) * Automatic session termination * IP restriction to office network only &&& Documentation needs: * Access request forms completed * Manager approval obtained * Annual training completed * DPO notification for bulk access &&& Incident response: * Immediate supervisor notification * 72-hour window for authority notification * Document the breach in security log * Trigger data subject notifications if required ``` **Example API Call** ```bash curl -X POST "https://www.edubase.net/api/v1/question" \ --data "app=$APP_ID" \ --data "secret=$APP_SECRET" \ --data 'id=math_derivatives' \ --data 'type=expression' \ --data 'question=Find the derivative of ${a}x^2 + {b}x + {c}$' \ --data 'answer=2*{a}*x+{b}' \ --data 'parameters={a; INTEGER; 2; 15} &&& {b; INTEGER; 2; 10} &&& {c; INTEGER; -10; 10}' \ --data 'hint=Remember the power rule: derivative of $x^n$ is $nx^(n-1)$ &&& Look at each term separately &&& What happens to constants when you differentiate?' \ --data 'explanation=For the first term {a}x^2, multiply by the power (2) and reduce the exponent by 1, giving us 2*{a}x. The term {b}x becomes just {b} since the power is 1. The constant {c} disappears in differentiation. Adding these gives us 2*{a}x + {b}.' \ --data 'solution=Use the power rule on each term. &&& For ${a}x^2$: $\frac{d}{dx}({a}x^2) = 2\cdot{a}x$ &&& For ${b}x$: $\frac{d}{dx}({b}x) = {b}$ &&& For ${c}$: $\frac{d}{dx}({c}) = 0$ &&& Therefore, $\frac{d}{dx}({a}x^2 + {b}x + {c}) = 2\cdot{a}x + {b}$' \ --data 'hint_penalty=PER-HELP:10%' \ --data 'solution_penalty=ONCE:50%' ``` * *string* **decimals**: number of decimal places used in numerical validation * default: 2 * *string* **tolerance**: method for how strictly user input should match answers ??? info "More information on decimals and tolerance" * These configurations only applicable to the following question types: * `NUMERICAL` * `EXPRESSION` * `MATRIX` * `MATRIX:EXPRESSION` * `SET` **Decimals** The expected decimal precision of the final answer * Examples: * Finance: `decimals=2` for currency calculations * Chemistry: `decimals=4` for molecular weights **Tolerance** * Format: `type` or `type:value` * Types: * `ABSOLUTE`: * Maximum allowed absolute difference between answer and user input (e.g., ±0.1) * Example: `ABSOLUTE:0.1` * `RELATIVE`: * Maximum percentage difference between answer and user input (e.g., ±5%) * Symmetric mean absolute percentage error ([SMAP](https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error)) value is used * Example: `RELATIVE:5%` or `RELATIVE:0.05` * `QUOTIENT`: integer multiple, and `QUOTIENT2`: scalar multiple * Answer and user input are scalar or integer multiples of one another * Append `:SYNCED` for synchronized validation * Example: `QUOTIENT` or `QUOTIENT2:SYNCED` * Use-cases: * `ABSOLUTE` reading from a graph where exact figures are hard to read * `QUOTIENT2` for eigenvectors * *string* **datetime_precision**: set granularity of date validation * applicable only for `DATE/TIME` questions * accepted values: YEAR / MONTH / DAY (default) * *string* **datetime_range**: enable date/time range (interval) question * applicable only for `DATE/TIME` questions * plus sign (+) indicates YES, while blank or minus sign (-) indicates no (default) * *string* **image**: attach an image to the question * in filename=data format, where data is a URL, base64-encoded data (optionally as a data URL), or the identifier of a file uploaded to the filebin * supported formats: PNG, JPEG, WebP * *string* **solution_image**: attach an image to the question * in filename=data format, where data is a URL, base64-encoded data (optionally as a data URL), or the identifier of a file uploaded to the filebin * supported formats: PNG, JPEG, WebP * *string* **attachment**: attach a file to the question * in filename=data format, where data is a URL, base64-encoded data (optionally as a data URL), or the identifier of a file uploaded to the filebin * *string* **media_audio**: attach an audio file to the question * in filename=data format, where data is a URL, base64-encoded data (optionally as a data URL), or the identifier of a file uploaded to the filebin * supported formats: MP3, AAC, M4A ??? info "Additional optional fields" * *string* **main_category** * *string* **attachment** * *string* **media_video** * *string* **media_audio** * *string* **solution_image** * *string* **maximum_choices**: maximum number of options the test taker can select, only for **`type=multiple-choice`** * *string* **video** * *string* **graph** * *string* **numerical_range** * *string* **truefalse_third_options** * *string* **truefalse_third_options_label** * *string* **grouping**: used with **`type=choice`** or **`type=multiple-choice`** when incorrect options are pulled from other questions assigned to the same grouping number * *string* **freetext_characters** * *string* **freetext_words** * *string* **freetext_rules** * *string* **file_count**: maximum number of files the test taker can upload, between 1 and 25 (default: 1), only for **`type=file`** * *string* **file_types**: allowed file extensions separated by commas (or by triple-and operators "&&&"), any file type is allowed if not set, only for **`type=file`** * *string* **hotspot_image**: background image for **`type=hotspot`** (same filename=data convention as **image** where applicable) * *string* **hotspot_zones**: zone definitions for **`type=hotspot`**; prefer authoring in the EduBase question editor for correct geometry, then export or mirror via API if supported ^^Output^^: * *string* **question**: question identification string ##### DELETE /question !!! danger "" Permanently delete a Quiz question. ^^Input^^: * ==[!]== *string* **id**: external unique question identifier [^1]: can be an arbitrary string but it should be unique, a GUID is recommended if no other identifier is available ##### POST /question:export !!! abstract "" Generate download link for exporting the question (in JSON format). If a previous valid link exists, it will be returned instead. The export includes the AI generated marking of the question (*ai* field). ^^Input^^: * ==[!]== *string* **id**: external unique question identifier ^^Output^^: * *string/null* **id**: external unique question identifier (if set for the question) * *string* **question**: question identification string * *string[url]* **url**: download link for the question * *date* **valid**: date of link expiration #### /question:id ##### GET /question:id !!! abstract "" Get external unique question identifier by question identification string. ^^Input^^: * ==[!]== *string* **question**: question identification string ^^Output^^: * *string* **question**: question identification string * *string/null* **id**: external unique question identifier (if set for the question) ##### POST /question:id !!! abstract "" Set external unique question identifier for question identified by a question identification string. ^^Input^^: * ==[!]== *string* **question**: question identification string * ==[!]== *string* **id**: external unique question identifier --- ### API: Quizes endpoints #### /quizes ##### GET /quizes !!! abstract "" List owned and managed Quiz sets. ^^Input^^: * *string* **search**: optional search string to filter results * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **quizes** * *string* **quiz**: Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *string* **title**: title of the Quiz set #### /quiz ##### GET /quiz !!! abstract "" Get/check Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string ^^Output^^: * *string* **quiz**: Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *string* **title**: title of the Quiz set * *string* **language**: language of the Quiz set * *string* **description**: short description of the Quiz set (only present if set for the Quiz set) ##### POST /quiz !!! abstract "" Create a new Quiz set. ^^Input^^: * *string* **language**: desired Quiz set language * ==[!]== *string* **title**: title of the Quiz set * *string{1,64}* **id**: external unique Quiz identifier * *string* **description**: optional short description * *string* **copy_settings**: optional Quiz set identification string to copy settings from * *string* **copy_questions**: optional Quiz set identification string to copy questions from * *string* **organization**: organization identification string to assign the Quiz set to * only an organization of the API application owner can be used * always the organization of the user for organizational members * *string* **mode**: sets how questions are displayed during the Quiz * possible values: TEST / TURNS * TEST: all questions are displayed at once, user can answer them in any order and switch between them * TURNS: questions are displayed one by one, only one question is visible at a time and the user must answer it before moving to the next question * default: TEST * *string* **type**: type of the Quiz set * possible values: set / final / exam / private * set: for practice purposes * final: for course exam purposes * exam: for exam purposes * private: for private purposes (e.g testing) * default: set ^^Output^^: * *string* **quiz**: Quiz identification string ##### PATCH /quiz !!! abstract "" Update the details of an existing Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * *string{1,255}* **title**: title of the Quiz set * *string{0,64}* **id**: external unique Quiz identifier * send an empty value to remove the current identifier * *string* **language**: language of the Quiz set * *string{0,255}* **description**: short description of the Quiz set * send an empty value to remove the current description * *string* **organization**: organization identification string to move the Quiz set to * only an organization of the API application owner can be used * only the owner of the Quiz set (or an administrator) can change the organization * send *none* (or an empty value) to remove the Quiz set from its current organization * always the organization of the user for organizational members ##### DELETE /quiz !!! abstract "" Remove/archive Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string #### /quiz:settings ##### GET /quiz:settings !!! abstract "" Get the settings of a Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string ^^Output^^: * *string* **quiz**: Quiz identification string * *boolean* **shuffle**: questions are shuffled for every play * *integer/null* **timelimit**: time limit of the whole test in seconds * *null*: there is no time limit * *integer/null* **roundtime**: time limit of a single question in seconds (turn based Quiz sets only) * *null*: there is no time limit * *string* **grading**: grading of the Quiz set * the code of the grading preset in use, *custom* for a manually configured grading, or *none* when grading is disabled * *integer* **grading_threshold**: threshold of the grading in percentage (only present if a threshold is configured for the Quiz set) ##### POST /quiz:settings !!! abstract "" Change individual settings of a Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * *boolean* **shuffle**: shuffle questions for every play * when disabled, the current order of the questions is kept * *integer/string* **timelimit**: time limit of the whole test in seconds * *0* or *default*: there is no time limit * *integer/string* **roundtime**: time limit of a single question in seconds * only available for turn based (TURNS mode) Quiz sets * *0* or *default*: there is no time limit * *string* **grading**: grading of the Quiz set * the code of a grading preset (see [GET /quiz:grading-presets](#get-quizgrading-presets)), or *none* to disable grading * *number* **grading_threshold**: threshold of the grading in percentage, between 0 and 100 * only available for grading presets with a configurable threshold (of the *go-nogo-custom* type) * can be changed without selecting the grading preset again ^^Output^^: The updated settings of the Quiz set are returned, the same way as with [GET /quiz:settings](#get-quizsettings). ##### PUT /quiz:settings !!! abstract "" Replace the complete configuration of a Quiz set with the configuration of another Quiz set. Only the configuration is copied, questions and question groups are not. The question selection, filtering and export settings of the target Quiz set are kept as well. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string (the target Quiz set) * ==[!]== *string* **source**: Quiz identification string to copy the settings from * must be different from the target Quiz set ^^Output^^: * *string* **quiz**: Quiz identification string * *string* **source**: Quiz identification string the settings were copied from ##### PATCH /quiz:settings !!! abstract "" Copy only those settings from another Quiz set that are not configured yet in the target Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string (the target Quiz set) * ==[!]== *string* **source**: Quiz identification string to copy the settings from * must be different from the target Quiz set ^^Output^^: * *string* **quiz**: Quiz identification string * *string* **source**: Quiz identification string the settings were copied from #### /quiz:grading-presets ##### GET /quiz:grading-presets !!! abstract "" List the grading presets available for the user. The global presets, the owned custom presets and the presets of the organizations of the user are returned. A Quiz set or an exam can be specified to list only the presets that can be used on it, together with the one it is currently using. ^^Input^^: * *string* **quiz**: Quiz set identification string * only the presets that can be used on this Quiz set are listed * *string* **exam**: exam identification string * only the presets that can be used on this exam are listed * either the *quiz* or the *exam* can be specified, not both of them * *string* **language**: optional language to filter the results with * presets that are not bound to a language are always included * default: the language of the specified Quiz set or exam ^^Output^^: * *string* **quiz**: Quiz set identification string (only present if a Quiz set is specified) * *string* **exam**: exam identification string (only present if an exam is specified) * *list* **presets** * *string* **preset**: grading preset identification string * *string* **title**: title of the grading preset * *string* **type**: type of the grading preset * *string/null* **language**: language of the grading preset * *null*: the preset is available in every language * *boolean* **configurable**: the threshold of the preset can be configured on the Quiz set or the exam * *boolean* **certificates**: the preset decides whether the test was successful, so certificates can be issued with it (see [POST /exam:certificates](../exams/#post-examcertificates)) * *boolean* **own**: the preset is a custom preset owned by the user * *string/null* **organization**: organization identification string of the preset * *null*: the preset is not bound to an organization * *boolean* **current**: the specified Quiz set or exam is currently graded with this preset (only present if a Quiz set or an exam is specified) #### /quiz:grading-preset ##### GET /quiz:grading-preset !!! abstract "" Get a grading preset with its thresholds and grades. ^^Input^^: * ==[!]== *string* **preset**: grading preset identification string ^^Output^^: * *string* **preset**: grading preset identification string * *string* **title**: title of the grading preset * *string* **type**: type of the grading preset * *string/null* **language**: language of the grading preset * *null*: the preset is available in every language * *boolean* **configurable**: the threshold of the preset can be configured on the Quiz set or the exam * *boolean* **certificates**: the preset decides whether the test was successful, so certificates can be issued with it (see [POST /exam:certificates](../exams/#post-examcertificates)) * *boolean* **own**: the preset is a custom preset owned by the user * *string/null* **organization**: organization identification string of the preset * *null*: the preset is not bound to an organization * *boolean* **used**: the preset is already used by a Quiz set or an exam of the user * *list* **grades** * *float* **threshold**: lowest result in percentage the grade is given for * the first item always starts at 0 * *string* **grade**: the grade itself * *string* **text**: text shown under the grade on the results page (if set) * *boolean* **successful_all**: every grade means success, even the lowest one (custom presets only) ##### POST /quiz:grading-preset !!! abstract "" Create a new custom grading preset. Needs the custom grading feature to be enabled. ^^Input^^: * ==[!]== *string{1,128}* **title**: title of the grading preset * *string* **language**: language of the grading preset * default: content language of the API application owner * ==[!]== *string* **type**: type of the grading preset * possible values: custom / go-nogo-custom / hungarian-school / hungarian-university * custom: freely named grades, at least 2 items * go-nogo-custom: successful/unsuccessful grading with a custom threshold, exactly 2 items * hungarian-school: Hungarian primary and secondary school grading (1-5), exactly 5 items * hungarian-university: Hungarian university grading (1-5), exactly 5 items * ==[!]== *list* **grades**: thresholds and grades of the preset, in ascending order * *number* **threshold**: lowest result in percentage the grade is given for, between 0 and 100 * the first item always starts at 0, its threshold is ignored * *string* **grade**: the grade itself * only used with the *custom* type, where every item needs a unique, non-empty grade * the grades of the other types are fixed by the type * *string* **text**: optional text shown under the grade on the results page * text between asterisks (*) is displayed in bold, three or more consecutive spaces start a new line * *boolean* **successful_all**: every grade means success, even the lowest one * only used with the *custom* type, where the grades are labels rather than pass or fail levels * default: false ^^Output^^: * *string* **preset**: grading preset identification string ##### PATCH /quiz:grading-preset !!! abstract "" Update an existing custom grading preset. The global presets and the presets of the organizations cannot be modified. ^^Input^^: * ==[!]== *string* **preset**: grading preset identification string * *string{1,128}* **title**: title of the grading preset * *string* **language**: language of the grading preset * *string* **type**: type of the grading preset * possible values: custom / go-nogo-custom / hungarian-school / hungarian-university * cannot be changed anymore once the preset is used by a Quiz set or an exam * *list* **grades**: thresholds and grades of the preset, in ascending order * the complete list is replaced, see [POST /quiz:grading-preset](#post-quizgrading-preset) for the items * *boolean* **successful_all**: every grade means success, even the lowest one * only used with the *custom* type, it is dropped when the preset is changed to another type * kept unchanged if not specified ^^Output^^: The updated grading preset is returned, the same way as with [GET /quiz:grading-preset](#get-quizgrading-preset). ##### DELETE /quiz:grading-preset !!! danger "" Remove a custom grading preset. The global presets, the presets of the organizations, and the presets that are still used by a Quiz set or an exam cannot be removed. ^^Input^^: * ==[!]== *string* **preset**: grading preset identification string #### /quiz:skills ##### GET /quiz:skills !!! abstract "" Get skills defined in a Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string ^^Output^^: * *string* **quiz**: Quiz identification string * *list* **skills** * *string* **identifier**: skill identifier * *string* **title**: title of the skill * *string* **description**: skill description (if set) #### /quiz:questions ##### GET /quiz:questions !!! abstract "" List all questions and question groups in a Quiz set. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string ^^Output^^: * *list* **items** * *string* **question**: question identification string (if question) * *string* **id**: external unique question identifier (if question and present) * *string* **group**: question group title (if group) * *boolean* **active**: active item * *boolean* **ai**: question is marked as AI generated, even if partially (if question) * *datetime/null* **ai_reviewed**: date and time an educator reviewed the AI generated question, null if not reviewed yet (if question) ##### POST /quiz:questions !!! abstract "" Assign question(s) to a Quiz set, or one of its question group. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * *string* **group**: optional question group title * ==[!]== *string* **questions**: comma-separated list of question identification strings ##### DELETE /quiz:questions !!! danger "" Remove question(s) from a Quiz set, or one of its question group. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * *string* **group**: optional question group title * ==[!]== *string* **questions**: comma-separated list of question identification strings --- ### API: Content tagging endpoints #### /tags ##### GET /tags !!! abstract "" List owned and managed tags. ^^Input^^: * *string* **search**: optional search string to filter results * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string/null* **id**: external unique tag identifier (if set for the tag) * *string* **title**: title of the tag #### /tag ##### GET /tag !!! abstract "" Get/check tag. ^^Input^^: * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: tag identification string * *string/null* **id**: external unique tag identifier (if set for the tag) * *string* **title**: title of the tag * *string* **color**: color in HEX format * *string* **icon**: Font Awesome icon class name #### /class ##### GET /class:tags !!! abstract "" List all attached tags of a class. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /class:tag !!! abstract "" Check if tag is attached to a class. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *class* * *string* **code**: the class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *boolean* **status**: tag is attached to this class ##### POST /class:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *class* * *string* **code**: the class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *boolean* **success**: operation was successful ##### DELETE /class:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **class**: class identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *class* * *string* **code**: the class identification string * *string/null* **id**: external unique class identifier (if set for the class) * *boolean* **success**: operation was successful #### /course ##### GET /course:tags !!! abstract "" List all attached tags of a course. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /course:tag !!! abstract "" Check if tag is attached to a course. ^^Input^^: * ==[!]== *string* **course**: course identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *course* * *string* **code**: the course identification string * *string/null* **id**: external unique course identifier (if set for the course) * *boolean* **status**: tag is attached to this course ##### POST /course:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **course**: course identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *course* * *string* **code**: the course identification string * *string/null* **id**: external unique course identifier (if set for the course) * *boolean* **success**: operation was successful ##### DELETE /course:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **course**: course identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *course* * *string* **code**: the course identification string * *string/null* **id**: external unique course identifier (if set for the course) * *boolean* **success**: operation was successful #### /event ##### GET /event:tags !!! abstract "" List all attached tags of an event. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /event:tag !!! abstract "" Check if tag is attached to an event. ^^Input^^: * ==[!]== *string* **event**: event identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *event* * *string* **code**: the event identification string * *string/null* **id**: external unique event identifier (if set for the event) * *boolean* **status**: tag is attached to this event ##### POST /event:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **event**: event identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *event* * *string* **code**: the event identification string * *string/null* **id**: external unique event identifier (if set for the event) * *boolean* **success**: operation was successful ##### DELETE /event:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **event**: event identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *event* * *string* **code**: the event identification string * *string/null* **id**: external unique event identifier (if set for the event) * *boolean* **success**: operation was successful #### /exam ##### GET /exam:tags !!! abstract "" List all attached tags of an exam. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /exam:tag !!! abstract "" Check if tag is attached to an exam. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *exam* * *string* **code**: the exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *boolean* **status**: tag is attached to this exam ##### POST /exam:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *exam* * *string* **code**: the exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *boolean* **success**: operation was successful ##### DELETE /exam:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **exam**: exam identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *exam* * *string* **code**: the exam identification string * *string/null* **id**: external unique exam identifier (if set for the exam) * *boolean* **success**: operation was successful #### /integration ##### GET /integration:tags !!! abstract "" List all attached tags of an integration. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /integration:tag !!! abstract "" Check if tag is attached to an integration. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *integration* * *string* **code**: the integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *boolean* **status**: tag is attached to this integration ##### POST /integration:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *integration* * *string* **code**: the integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *boolean* **success**: operation was successful ##### DELETE /integration:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **integration**: integration identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *integration* * *string* **code**: the integration identification string * *string/null* **id**: external unique integration identifier (if set for the integration) * *boolean* **success**: operation was successful #### /organization ##### GET /organization:tags !!! abstract "" List all attached tags of an organization. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /organization:tag !!! abstract "" Check if tag is attached to an organization. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *organization* * *string* **code**: the organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *boolean* **status**: tag is attached to this organization ##### POST /organization:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *organization* * *string* **code**: the organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *boolean* **success**: operation was successful ##### DELETE /organization:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **organization**: organization identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *organization* * *string* **code**: the organization identification string * *string/null* **id**: external unique organization identifier (if set for the organization) * *boolean* **success**: operation was successful #### /quiz ##### GET /quiz:tags !!! abstract "" List all attached tags of a Quiz. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /quiz:tag !!! abstract "" Check if tag is attached to a Quiz. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *quiz* * *string* **code**: the Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *boolean* **status**: tag is attached to this Quiz ##### POST /quiz:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *quiz* * *string* **code**: the Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *boolean* **success**: operation was successful ##### DELETE /quiz:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **quiz**: Quiz identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *quiz* * *string* **code**: the Quiz identification string * *string/null* **id**: external unique Quiz identifier (if set for the Quiz) * *boolean* **success**: operation was successful #### /scorm ##### GET /scorm:tags !!! abstract "" List all attached tags of a SCORM learning material. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /scorm:tag !!! abstract "" Check if tag is attached to a SCORM learning material. ^^Input^^: * ==[!]== *string* **scorm**: SCORM identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *scorm* * *string* **code**: the SCORM identification string * *string/null* **id**: external unique SCORM identifier (if set for the SCORM) * *boolean* **status**: tag is attached to this SCORM learning material ##### POST /scorm:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **scorm**: SCORM identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *scorm* * *string* **code**: the SCORM identification string * *string/null* **id**: external unique SCORM identifier (if set for the SCORM) * *boolean* **success**: operation was successful ##### DELETE /scorm:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **scorm**: SCORM identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *scorm* * *string* **code**: the SCORM identification string * *string/null* **id**: external unique SCORM identifier (if set for the SCORM) * *boolean* **success**: operation was successful #### /video ##### GET /video:tags !!! abstract "" List all attached tags of a video. ^^Output^^: * *list* **tags** * *string* **tag**: tag identification string * *string* **title**: title of the tag ##### GET /video:tag !!! abstract "" Check if tag is attached to a video. ^^Input^^: * ==[!]== *string* **video**: video identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *video* * *string* **code**: the video identification string * *string/null* **id**: external unique video identifier (if set for the video) * *boolean* **status**: tag is attached to this video ##### POST /video:tag !!! abstract "" Attach tag to content. ^^Input^^: * ==[!]== *string* **video**: video identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *video* * *string* **code**: the video identification string * *string/null* **id**: external unique video identifier (if set for the video) * *boolean* **success**: operation was successful ##### DELETE /video:tag !!! danger "" Remove a tag attachment. ^^Input^^: * ==[!]== *string* **video**: video identification string * ==[!]== *string* **tag**: tag identification string ^^Output^^: * *string* **tag**: the tag identification string * **content** * *string* **type**: will be *video* * *string* **code**: the video identification string * *string/null* **id**: external unique video identifier (if set for the video) * *boolean* **success**: operation was successful --- ### API: Users endpoints #### /users ##### GET /users !!! abstract "" List managed, non-generated users. ^^Input^^: * *string* **search**: optional search string to filter results * *integer* **limit**: limit number of results * default: 16 * *integer* **page**: page number * default: 1 * not used in search mode! ^^Output^^: * *list* **users** * *string* **user**: user identification string * *string* **name**: full name #### /user ##### GET /user:me !!! abstract "" Get/check current user. ^^Output^^: * *string* **user**: user identification string * *string* **name**: full name * *boolean* **status**: user is enabled * *boolean* **exam**: exam (generated) account ##### GET /user !!! abstract "" Get/check user. ^^Input^^: * ==[!]== *string* **user**: user identification string * use *me* to get the current user, but prefer /user:me endpoint instead ^^Output^^: * *string* **user**: user identification string * *string* **name**: full name * *boolean* **status**: user is enabled * *boolean* **exam**: exam (generated) account ##### POST /user !!! abstract "" Create new user account. ^^Input^^: * ==[!]== *string{4,64}* **username**: username * *string{4,64}* **password**: password * default: initial random password is automatically generated * ==[!]== *string{1,64}* **first_name**: first name * ==[!]== *string{1,64}* **last_name**: last name * *string{1,255}* **full_name**: override automatic full name (built from first and last names) * *string{1,255}* **display_name**: override automatic display name (which is the full name) * ==[!]== *string[email]* **email**: valid email address * *string[phone]* **phone**: valid phone number * format: "+prefix number" without any special characters and symbols * example: *+1 1234567890* or *+36 301234567* * *string* **gender**: gender * male * female * other * *date* **birthdate**: date of birth * *boolean* **exam**: user is only allowed to login when accessing exams * default: false * *string* **group**: name of the user group * *string* **template**: a template ID for the new account * *corporate_test* / *corporate_user* / *corporate_reporter* / *corporate_supervisor* / *corporate_teacher* / *corporate_admin*: corporate users * *streaming*: restricted users for automatically generated users for occasional streams * default: none * *string* **custom_{field}**: custom field data (only if specified field is configured for target EduBase instance) * *string* **language**: desired account language * default: API application owner's language * *string* **timezone**: desired timezone * default: API application owner's timezone * *string* **color**: desired favorite color * possible values: default / branding / red / blue / yellow / green / purple / gray * default: default * *boolean* **must_change_password**: user is forced to change password on first login * default: false * *boolean* **notify**: notify user via email (or SMS) * default: false ^^Output^^: * *string* **user**: user identification string * *string* **username**: username, only if *exam=false* * *string* **password**: password, only if *exam=false* ^^Example^^: === "PHP" ``` php-inline /* Initialize cURL */ $ch=curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); /* Add data (as array) */ curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); /* Process call and receive response */ curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $response=curl_exec($ch); ``` === "Bash" ``` bash ### Send data as multipart/form-data curl -s --request POST --url "https://$API_DOMAIN/api/v$API_VERSION/user" --header "Content-Type: application/x-www-form-urlencoded" --data "username=$username" --data "first_name=$first_name" --data "last_name=$last_name" --data "full_name=$full_name" --data "email=$email" --data "exam=false" --data "template=$template" --data "app=$APP_ID" --data "secret=$APP_SECRET" ``` ##### PATCH /user !!! abstract "" Update user. ^^Input^^: * ==[!]== *string* **user**: user identification string * *boolean* **active**: enable or disable user ##### DELETE /user !!! danger "" Delete user. ^^Input^^: * ==[!]== *string* **user**: user identification string #### /user:name ##### GET /user:name !!! abstract "" Get user's name. ^^Input^^: * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * *string* **first_name**: first name * *string* **last_name**: last name * *string* **full_name**: full name * *string* **display_name**: display name ##### POST /user:name !!! abstract "" Update a user's name. ^^Input^^: * ==[!]== *string* **user**: user identification string * ==[!]== *string{1,64}* **first_name**: first name * ==[!]== *string{1,64}* **last_name**: last name * *string{1,255}* **full_name**: full name * *string{1,255}* **display_name**: display name ^^Output^^: * *string* **user**: the user identification string * *boolean* **success**: operation is successful * *boolean* **changed**: name has been changed #### /user:group ##### GET /user:group !!! abstract "" Get user's group. ^^Input^^: * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * *string* **group**: user group code ##### POST /user:group !!! abstract "" Update a user's group. ^^Input^^: * ==[!]== *string* **user**: user identification string * ==[!]== *string* **group**: user group code ^^Output^^: * *string* **user**: the user identification string * *boolean* **success**: operation is successful * *boolean* **changed**: name has been changed #### /user:login ##### GET /user:login !!! abstract "" Get latest valid login link for user. ^^Input^^: * ==[!]== *string* **user**: user identification string ^^Output^^: * *string* **user**: the user identification string * *string[url]* **url**: the login link * *datetime* **valid**: validity (end of day) of the generated link ##### POST /user:login !!! abstract "" Generate link. If a valid link with the same settings exist, it will be returned instead of creating a new one. ^^Input^^: * ==[!]== *string* **user**: user identification string * *string* **redirect**: redirect after a successful login * URI path or [{content_type}:{tag}] * *string* **exam**: the exam identification string to redirect the user to * user must be added to the exam first * mutually exclusive with redirect * *integer/date* **expires**: expiry in days (1-30) or `YYYY-MM-DD` * default: 1 day * *integer* **logins**: total count (up to 255) the link can be used to login users * *string* **template**: a template identifier for the login link * *boolean* **short**: generate shortened (eduba.se) link (only if feature is enabled on EduBase) ^^Output^^: * *string* **user**: the user identification string * *string[url]* **url**: the login link * *datetime* **valid**: validity of the generated link * *integer* **count**: maximum number the link can be used to login ##### DELETE /user:login !!! danger "" Delete a previously generated link. ^^Input^^: * ==[!]== *string* **user**: user identification string * ==[!]== *string[url]* **url**: generated login link to be invalidated #### /user:search ##### GET /user:search !!! abstract "" Lookup user by email, username or code. ^^Input^^: * ==[!]== *string* **query**: query string ^^Output^^: * *string* **user**: user identification string * *boolean* **exam**: exam (generated) account #### /user:assume ##### POST /user:assume !!! abstract "" Assume user for next requests with assume token. ^^Input^^: * ==[!]== *string* **user**: user identification string, username or email address * *string* **password**: password or user secret ^^Output^^: * *string* **user**: user identification string * *string* **token**: assume token * *datetime* **valid**: validity of the generated token ##### DELETE /user:assume !!! danger "" Revoke assume token. ^^Input^^: * ==[!]== *string* **token**: assume token --- ## Contact ### Email You can reach us at [info@edubase.net](mailto:info@edubase.net). !!! bug Have you found a **bug**, a **typo** or an **error**? Please do not hesitate to contact us at [webmaster@edubase.net](mailto:webmaster@edubase.net). ### More Visit our [contact page](https://www.edubase.net/page/contact) for more ways to get in touch with us. --- ## Legal Please visit our [legal page](https://www.edubase.net/page/legal) for more information.