# Customer-Dev

Customer Documentation with integration information and technical details

# Swiss Learning Hub integration

The purpose of this documentation is to describe available integrations of the Swiss Learning Hub.

<table id="bkmrk-integration-descript"><thead><tr><th>Integration</th><th>Description</th></tr></thead><tbody><tr><td>[Detail report](https://docs.swisslearninghub.help/books/customer-dev/page/detail-report)</td><td>In the Swiss Learning Hub, a detailed report can be generated per learning content if the report definition and certain SCORM attributes are provided by the learning object.</td></tr><tr><td>[File based API](https://docs.swisslearninghub.help/books/customer-dev/page/user-import)</td><td>File based user import in XML or JSON format over SFTP.</td></tr><tr><td>[LTI](https://docs.swisslearninghub.help/books/customer-dev/page/learning-tools-interoperability)</td><td>Learning Tools Interoperability (LTI) is an education technology specification developed by the IMS Global Learning Consortium, which specifies a method for a LMS to securely exchange information with external systems.</td></tr><tr><td>[SAML 2.0](https://docs.swisslearninghub.help/books/customer-dev/page/integrations)</td><td>Single sign-on (SSO) with SAML</td></tr></tbody></table>

# Detail report

In Swiss Learning Hub, a detailed report can be created per learning content if the report definition (*report.xml*) and certain SCORM keys (**cmi.objectives.n.x** and **cmi.interactions.n.x** are provided by the learning object.

## Report definition

### File name and location

The XML file must always have the file name **report.xml** and must be stored in the *root* of the respective SCORM package. The detail report can only be created if the Swiss Learning Hub can find and use this file.

### Scheme

The [XSD scheme](https://cdn.swisslearninghub.com/xml/trc/v1.0/report.xsd) can be used to create the report file.

#### Element **report**

Is the root element of the report XML

#### Element **organization**

This element refers to a chapter in the learning object.

Attribute:

- id = unique ID of the chapter (e.g.: chapter\_000\_010\_150)
- name = Name of the chapter (e.g.: Schweizer Quiz)

No hierarchical chapter structures can be mapped. The chapters must be reduced to one level.

*Example:*

- Chapter 1 
    - Chapter 1-1
    - Chapter 1-2
- Chapter 2 
    - Chapter 2-1

*report.xml*

```xml
<organization name="Chapter 1">
    <item name="Page A" />
</organization>
<organization name="Chapter 1-1">
    <item name="Page B" />
</organization>
<organization name="Chapter 1-2">
    <item name="Page C" />
</organization>
<organization name="Chapter 2">
    <item name="Page D" />
</organization>
<organization name="Chapter 2-1">
    <item name="Page E" />
</organization>

```

#### Element **item**

This element refers to a learning object page within a chapter.

Attribute:

- id = unique ID of the learning object page (e.g.: item\_000\_010\_010\_de)
- name = Page title (z.B.: Testbeginn / Sprachen / Hauptstadt)
- type = Type of learning object page (e.g.: dynamicFrames / multipleChoice / testInteraction, dynamicFrames)

#### Element **question**

This element refers to a question within a learning object (*item*).

Attribute:

- name = Question text (e.g.: Zu welchem Kontinent gehört die Schweiz? Wählen Sie die korrekte Antwort.)
- type = Question type (e.g.: multipleChoice / matchingDragAndDrop / matching / matrix)
- maxScore = Maximum number of points for this question (for example: 1)

#### Element **answer**

This element refers to an answer option of a question.

Attribute:

- id = unique ID of the answer choice (e.g.: A / 1.A / usw.)
- name = Description of the answer choice (e.g.: Afrika / Tennisspieler / Schweiz / Nicht Schweiz / usw.)
- correct = Indicates whether the answer choice is correct or not (e.g.: true / false)

## SCORM keys

The learning object must send data to the following SCORM keys according to [SCORM 1.2](https://adlnet.gov/projects/scorm/) (Specification SCORM 1.2 RunTimeEnv).

- cmi.objectives.n.id
- cmi.objectives.n.score.raw
- cmi.objectives.n.score.min
- cmi.objectives.n.score.max
- cmi.objectives.n.status
- cmi.interactions.n.id
- cmi.interactions.n.objectives.n.id
- cmi.interactions.n.objectives.n.type
- cmi.interactions.n.objectives.n.time
- cmi.interactions.n.correct\_responses.n.pattern
- cmi.interactions.n.weighting
- cmi.interactions.n.student\_response
- cmi.interactions.n.result
- cmi.interactions.n.latency

## Mapping SCORM keys and the report XML

The SCORM key **cmi.interactions.n.id** (value e.g.: item\_000\_010\_020\_en) can be used to determine which question is involved in the report XML. All further SCORM keys for this interaction can be read out using the index (n). The same logic is used for the SCORM keys **cmi.objectives.n.x**. The index (n) for cmi.objectives.n.x and cmi.interactions.n.x does not necessarily have to have the same value per learning object page, nor is the order of the indexes necessarily the same as the page order of the learning object.

*Example (report.xml)*

```xml
<item id="item_000_010_020_de" name="Geographie" type="multipleChoice">
    <question name="Zu welchem Kontinent gehört die Schweiz? Wählen Sie die korrekte Antwort." type="multipleChoice" maxScore="1">
        <answer id="A" name="Afrika" correct="false"/>
        <answer id="B" name="Amerika" correct="false"/>
        <answer id="C" name="Asien" correct="false"/>
        <answer id="D" name="Europa" correct="true"/>
    </question>
</item>

```

The SCORM key **cmi.interactions.n.student\_response** (value e.g.: D) can be used to read out which response option the learner has selected.

*Example (report.xml)*

```xml
<question name="Zu welchem Kontinent gehört die Schweiz? Wählen Sie die korrekte Antwort." type="multipleChoice" maxScore="1">
    <answer id="A" name="Afrika" correct="false"/>
    <answer id="B" name="Amerika" correct="false"/>
    <answer id="C" name="Asien" correct="false"/>
    <answer id="D" name="Europa" correct="true"/>
</question>

```

The SCORM key **cmi.objectives.n.score.raw** (value e.g.: 1) can be used to determine how many points the learner has achieved in this question. The SCORM key **cmi.interactions.n.result** (value: correct / incorrect) shows whether the learner has solved the question completely correctly.

## Example

### XML

File name: report.xml XML scheme: https://cdn.swisslearninghub.com/xml/trc/v1.0/report.xsd

```xml
<?xml version="1.0" encoding="utf-8"?>
<report xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://cdn.crealogix.com/xml/trc/v1.0/report"
        xsi:schemaLocation="http://cdn.crealogix.com/xml/trc/v1.0/report http://cdn.crealogix.com/xml/trc/v1.0/report.xsd">
    <organization id="chapter_1" name="Kapitel 1">
        <item id="page_A" name="Seite A" type="dynamicFrames" />
    </organization>
    <organization id="chapter_1_1" name="Kapitel 1-1">
        <item id="question_1" name="Geographie" type="multipleChoice">
            <question name="Zu welchem Kontinent gehört die Schweiz? Wählen Sie die korrekte Antwort." type="singleChoice" maxScore="1">
                <answer id="A" name="Afrika" correct="false"/>
                <answer id="B" name="Amerika" correct="false"/>
                <answer id="C" name="Asien" correct="false"/>
                <answer id="D" name="Europa" correct="true"/>
            </question>
        </item>
    </organization>
    <organization id="chapter_1_2" name="Kapitel 1-2">
        <item id="question_2" name="Geographie" type="multipleChoice">
            <question name="Welcher Kontinent liegt ganz auf der Südhalbkugel? Wählen Sie die korrekten Antworten." type="multipleChoice" maxScore="1">
                <answer id="A" name="Afrika" correct="false"/>
                <answer id="B" name="Australien" correct="true"/>
                <answer id="C" name="Asien" correct="false"/>
                <answer id="D" name="Europa" correct="false"/>
                <answer id="E" name="Antarktika" correct="true"/>
            </question>
        </item>
    </organization>
    <organization id="chapter_2" name="Kapitel 2">
        <item id="page_B" name="Seite B" type="dynamicFrames" />
    </organization>
    <organization id="chapter_2_1" name="Kapitel 2-1">
        <item id="question_2" name="Geographie" type="multipleChoice">
            <question name="Welcher Kontinent liegt ganz auf der Nordhalbkugel, ganz auf der Südhalbkugel oder auf beiden? Wählen Sie die korrekten Antworten." type="matrix" maxScore="1">
                <answer id="1.A" name="Afrika / Nordhalbkugel" correct="false"/>
                <answer id="1.B" name="Afrika / Südhalbkugel" correct="false"/>
                <answer id="1.C" name="Afrika / auf beiden" correct="true"/>
                <answer id="2.A" name="Europa / Nordhalbkugel" correct="true"/>
                <answer id="2.B" name="Europa / Südhalbkugel" correct="false"/>
                <answer id="2.C" name="Europa / auf beiden" correct="false"/>
            </question>
        </item>
    </organization>
</report>

```

# User Import

This document describes the file-based interface for user import into the Swiss Learning Hub. The personal data can be transferred in XML format via SFTP.

**Data transfer** The files are transferred to the SLH server via SFTP. You will receive the SFTP access data from your SLH contact person.

**File format** The file format is XML. The files must be encoded as UTF-8.

**Process time** The process time can be determined individually (usually once a day at night), but must be coordinated with other processes by SLH.

**User import** The full functionality of a user import can only be executed if all current users are listed in the transferred file. It is possible to configure the user import for a specific organizational unit only. Thus, further organizational units can be maintained manually in the Swiss Learning Hub and are not affected by further processes.

## Processes

**Removing users** The user import is designed to automatically remove users that are not listed in the file. The import can be configured accordingly, so that these users are effectively deleted (incl. all learning data) or archived (thus access by the user to the system is prevented, but as an administrator one still has access the user incl. all his/her learning data). If a user has set the value "Deletable" = no (0) in the Swiss Learning Hub, this user will never be removed by the user import. Thus, e.g. via the interface of the Swiss Learning Hub, external users can be subscribed, which do not have to be part of the user import file.  
It's also possible to protect user from deletion by adding them to a specific organizational unit. In the setup a queryparameter ('excludeorgs') with a list of units which should be excluded from deleting/archiving.

**Updating users** If a user is already integrated in the Swiss Learning Hub, it will not be newly created by the user import, but updated. However, the user must be uniquely identifiable for this purpose. In the first priority, the user is identified by the personnel number. A personal id can be, for example, a system ID from the conversion system or effectively an internal company value. It should be noted that this is unique and will certainly never be changed (e.g., in the event of a name change due to marriage) or reassigned to another person (e.g., e-mail addresses are reassigned after a resignation). This is important in case persons are not deleted from the Swiss Learning Hub for data protection reasons, but archived and certainly thus never overwritten by a new user. In second priority, the user is also identified by user name or e-mail address.

**Updating user roles** By default the import does not update a users role. It's set on the first import (on create) of a user. After that, higher roles than “learner” have to be set manually in user administration.   
There is a functionality (query parameter 'changerole') in place which explicitly allows to update all user roles given in an import file.

## Data model

### Object Person

<table id="bkmrk-name-description-key"><thead><tr><th>Name</th><th>Description</th><th>Key</th><th>Format</th><th>Example</th></tr></thead><tbody><tr><td>Prename</td><td>first name of the person.</td><td>prename</td><td>String 255 character</td><td>Max</td></tr><tr><td>Name</td><td>last name of the person.</td><td>name</td><td>String 255 character</td><td>Muster</td></tr><tr><td>E-Mail</td><td>E-mail address of the person.</td><td>email</td><td>String 255 character</td><td>max.muster@meinefirma.ch</td></tr><tr><td>Username</td><td>Unique username of the person.</td><td>username</td><td>String 255 character</td><td>max.muster@meinefirma.ch</td></tr><tr><td>Personnel number</td><td>Unique internal id of the person.</td><td>personal\_id</td><td>String 255 character</td><td>135487</td></tr><tr><td>Status</td><td>Status of the person: - activated = person can use the Swiss Learning Hub
- deactivated = person cannot use the Swiss Learning Hub, but the administrator can use the person, just like an activated person
- archived = person cannot use the Swiss Learning Hub, the admin can access the person incl. learning data.

</td><td>status</td><td>Enum: - enabled
- disabled
- archived

</td><td>enabled</td></tr><tr><td>Birthday</td><td>Birthday of the person.</td><td>birthday</td><td>Date yyyy-mm-dd</td><td>1986-04-12</td></tr><tr><td>Deletable</td><td>Defines whether a person can be deleted by the user import (manually or automatically).</td><td>is\_deletable</td><td>Boolean (0/1)</td><td>1</td></tr><tr><td>Language</td><td>The language of the person.</td><td>language</td><td>Language (CLDR) - de = german
- fr = french
- it = italian
- en = english

</td><td>de</td></tr><tr><td>Role</td><td>The system role of the person.</td><td>role</td><td>Enum: - learner
- default-subadministrator
- administrator

</td><td>learner</td></tr><tr><td>Organizational units</td><td>All organizational units of the person.</td><td>orgunits</td><td>Array of the object *organizational unit*</td><td> </td></tr><tr><td>Job descriptions</td><td>All job descriptions of the person.</td><td>jobdescriptions</td><td>Array of the object *job description*</td><td> </td></tr></tbody></table>

### Object Organisational unit

<table id="bkmrk-name-description-for"><thead><tr><th>Name</th><th>Description</th><th>Format</th><th>Example</th></tr></thead><tbody><tr><td>Name</td><td>Complete path of the organisational unit</td><td>String. Organisational units are separated by "/", per unit max 255 chars can be used</td><td>OU-1/OU-11/OU-111</td></tr></tbody></table>

### Object Jobdescription

<table id="bkmrk-name-description-for-1"><thead><tr><th>Name</th><th>Description</th><th>Format</th><th>Example</th></tr></thead><tbody><tr><td>Name</td><td>Complete path of the jobdescription</td><td>String. Jobdescriptions are separated by "/", per unit max 255 chars can be used</td><td>Developer/Frontend</td></tr></tbody></table>

### Example

```xml
<?xml version="1.0" encoding="UTF-8"?>
<persons schemaVersion="1.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="https://cdn.swisslearninghub.com/xml/trc/v2.0/import_person"
  xsi:schemaLocation="https://cdn.swisslearninghub.com/xml/trc/v2.0/import_person https://cdn.swisslearninghub.com/xml/trc/v2.0/import_person.xsd">
    <person>
        <prename>Max</prename>
        <name>Muster</name>
        <email>max.muster@meinefirma.ch</email>
        <username>max.muster</username>
        <personal_id>135487</personal_id>
        <status>enabled</status>
        <birthday>1986-04-12</birthday>
        <is_deletable>1</is_deletable>
        <language>de</language>
        <role>learner</role>
        <orgunits>
            <orgunit>OU-1/OU-11</orgunit>
            <orgunit>Entwicklung/Team Frontend</orgunit>
        </orgunits>
        <jobdescriptions>
            <jobdescription>Meine Tätigkeit</jobdescription>
            <jobdescription>Frontend Entwickler</jobdescription>
        </jobdescriptions>
    </person>
    <!-- more persons -->
</persons>

```

Validate XML file using `xmllint` and schema file:

```shell
# Download schema file
wget -O import_person.xsd https://cdn.swisslearninghub.com/xml/trc/v2.0/import_person.xsd

# Validate/Lint XML file
xmllint --schema import_person.xsd import_persons.xml --noout

```

# Supervisor Import

Goal is to import/synchronize bigger sets of supervisor-to-users relations. `Content-Type` header defines the requested import format:

Supported content types:

- `application/json`
- `application/xml`
- `text/csv`

#### Configuration Possibilities

Different importing-modes can be applied which define what will happen to entries that are available in the database, but are not available via the given import file anymore (deprecation).

- **None**  
    In mode none - nothing will happen to the existing entries in our database if it's missing in the current file
- **Delete**  
    Before the import we will remove all existing supervisor-user relations and recreate this from the provided file. This is the default setting

The other way round, if there are entries in the provided file that do not exist in our database, the entries are simply ignored, the import is not stopped and runs normally.

#### Supported content-types / hierarchy

JSON, XML and CSV are supportet filetypes. They all contain a simple list of **supervisor&lt;-&gt;user** relation - the matching happens based on **USERNAME**.  
A supervisor can have multiple users whereas a user only has one supervisor.

##### application/json

**Example**

```json
[
  {
    "supervisor": "chefa",
    "user": "usera"
  },
  {
    "supervisor": "chefb",
    "user": ""
  }
]
```

##### application/xml

**Example**

```xml
<?xml version="1.0" encoding="utf-8"?>
<supervisors>
    <supervisor>
        <supervisor>chefa</supervisor>
        <user>usera</user>
    </supervisor>
    <supervisor>
        <supervisor>chefb</supervisor>
        <user/>
    </supervisor>
</supervisors>
```

##### text/csv

**Example**

```markdown
supervisor,user
chefa,usera
chefb,
```

#### Fields

While the field `supervisor` is mandatory, the field `user` can be omitted (see examples above).

<table id="bkmrk-field-mandatory-info" style="width: 99.5062%;"><thead><tr><th class="align-left" style="width: 19.5031%;">Field</th><th class="align-left" style="width: 17.764%;">Mandatory</th><th class="align-left" style="width: 62.7329%;">Info</th></tr></thead><tbody><tr><td style="width: 19.5031%;">`supervisor`</td><td style="width: 17.764%;">yes</td><td style="width: 62.7329%;">Marks user as supervisor and gives permissions</td></tr><tr><td style="width: 19.5031%;">`user`</td><td style="width: 17.764%;">no</td><td style="width: 62.7329%;">Attaches user as member of supervisor</td></tr></tbody></table>

#### Process time

The process time can be determined individually (usually once a day at night), but must be coordinated with other processes by SLH.

# SSO / SAML 2.0

# Integrations

Security Assertion Markup Language (SAML) is an open standard that allows identity providers (IdP) to pass authorization credentials to service providers (SP).

SAML transactions use Extensible Markup Language (XML) for standardized communications between the identity provider and service providers. SAML is the link between the authentication of a user’s identity and the authorization to use a service.

Integration | Description
----------- | -----------
[Microsoft Azure](https://docs.swisslearninghub.help/books/customer-dev/page/microsoft-azure-exchange-of-metadata-basic-guideline) | SAML 2.0 integration over Azure Active Directory admin portal

# Microsoft Azure - Exchange of Metadata: Basic Guideline

### Preparation
1.  Go to [Azure Active Directory admin portal](https://aad.portal.azure.com/)
2.  Go to *Enterprise Applications*
3. create a *new application* (*create your own application*)
    - Name: "Swiss Learning Hub" or something that helps you recognize this configuration again
    - Select: **Integrate any other application you don't find in the gallery (Non-gallery)**
4.  Go to *Single sign-on* and choose SAML
    1.  On step 2 (Attributes & Claims): change the *Attributes & Claims* if necessary. As *Unique User Identifier (Name ID)* the same variable must be used, which contains the value that is identical to the Swiss Learning Hub username.
    2.  On step 3 (SAML Certificates): Copy the *App Federation Metadata URL*. **Send this link (or Metadata XML behind this link) to your Swiss Learning Hub contact**.
5.  Go to *Users and groups* and add users or groups to be authorized for authentication.

### Receive the metadata of Swiss Learning Hub
1.  Go to [Azure Active Directory admin portal](https://aad.portal.azure.com/)
2.  Go to *Enterprise Applications*
3.  Choose the Swiss Learning Hub application
4.  Go to *Single sign-on* and choose *SAML*
5.  Upload the received metadata file.

# Calendar Sync with Microsoft Graph API

<div id="bkmrk-f%C3%BCr-einen-funktionie">Für einen funktionierenden Calendar Sync müssen Sie in Ihrer MS Entra ID (Azure AD) die folgenden drei Schritte ausführen. Sobald Sie die Schritte ausgeführt haben, benötigen wir die drei Werte <span style="color: rgb(224, 62, 45);">*Application (client) ID, Directory (tenant) ID* und *Secret Value*</span>, um den Calendar Sync auf unserer Seite fertig einrichten zu können:</div><div id="bkmrk-"></div>---

<div id="bkmrk--2"></div><div id="bkmrk--3"></div><div id="bkmrk--4"></div><div id="bkmrk--5"></div><div id="bkmrk--6"></div>[![image.png](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/scaled-1680-/7NHT4jEsOJSyDF03-image.png)](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/7NHT4jEsOJSyDF03-image.png)

##### **Schritt 1**  
Eine neue **App registration** erstellen

- MS Entra ID öffnen: [https://entra.microsoft.com](https://entra.microsoft.com)
- Im Hauptmenü unter ***App registrations*** auf ***New registration*** klicken
- Einen Namen für die neue Registration vergeben und anschliessend auf ***"Register"*** klicken
- Unter ***Essentials*** die <span style="color: rgb(224, 62, 45);">***Application (client) ID***</span> und  
    <span style="color: rgb(224, 62, 45);">***Directory (tenant) ID***</span> herauskopieren und uns zusenden

---

[![image.png](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/scaled-1680-/CuCmCetBXhwBq35U-image.png)](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/CuCmCetBXhwBq35U-image.png)

##### **Schritt 2**  
Ein neues **Zertifikat/Secret** erstellen

- Im Menü unter ***Certificates &amp; secrets*** auf ***New client** **secret*** klicken
- Eine Beschreibung und Gültigkeitsdauer erfassen und  
    auf ***"Add"*** klicken
- Den <span style="color: rgb(224, 62, 45);">***Value*** </span>des soeben erstellten Secrets herauskopieren und uns **über einen sicheren Kanal** zusenden

---

#### **Schritt 3**  
Eine neue **Permission** für Microsoft Graph erstellen

- Im Menü unter ***API permissions*** auf ***Add a permission*** klicken
- In der Auswahl die API ***Microsoft Graph*** auswählen
- Unter ***Application permissions*** -&gt; ***Calendars*** die Option ***Calendars.Read*** aktivieren und auf ***"Add permissions"*** klicken
- Anschliessend noch auf ***"Grant admin consent for \[company\]"*** klicken und bestätigen, damit alle User der Firma diese Berechtigung erhalten.  
      
    [![image.png](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/scaled-1680-/DxIgDNeSn2yfpAH1-image.png)](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/DxIgDNeSn2yfpAH1-image.png)[![image.png](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/scaled-1680-/OWm5N2CKMb5AbxZY-image.png)](https://docs.swisslearninghub.help/uploads/images/gallery/2026-01/OWm5N2CKMb5AbxZY-image.png)