Overview

Sana Learn is a plug and play solution to add personalization to learning products. Sana Learn evaluates the answers, response times and an array of contextual information from the learners to understand what the learners know, how they learn and how they forget. Building on these insights, Sana Learn suggests the optimal content for learners tailored to the objectives of the learning product, increasing learning outcomes and engagement.

To integrate with Sana Learn the content model is submitted through the upsert catalog endpoint. The learning platform is then integrated with Sana Learn API as follows:

The image below indicates a typical flow of a learning platform integrated with the Sana Learn API.

Sana Learn Single Image

What is new in v2

The Sana Learn system has received extensive improvements since the launch of version 1 of the API. These improvements have allowed us to create a more simple and also more powerful API for the underlying adaptive engine. Here is a list of changes from v1 along with new terms that are introduced in v2.

v2 termv1 termComment
catalogviewCatalogs can be much larger in size than views, and catalogs are atomic.
topicpathTopics contain both id and display name, and allow for more flexible and precise content structure.
asset & view_itemitemThe separation between assets and items has been simplified.
sessionn/aThe concept of adaptive session is new to v2.
adaptive‑enginen/aThe adaptive engine endpoint endpoint handles all requirements of an adaptive session, removing the need for multiple requests to power an adaptive session.

We only support v2 API for new integrations. If needed, our v1 API Reference can be found here.

Content model

In order to get real-time recommendations from the Sana Learn API, metadata for the available content must be uploaded to Sana Learn in the form of a Catalog

Catalog

A catalog represents the complete set of recommendable content, and a catalog typically corresponds to the curriculum of a course. Completely separate sets of content should be modeled as different catalogs.

Example catalog:

{
  "catalog_id": "catalog_id_1",
  "display_name": "Catalog One",
  "items": [
    {
      "item_id": "item_id_1",
      "display_name": "Item One",
      "scorable": true,
      "difficulty": 0.3,
      "theory_item_refs": [
        {
          "item_id": "item_id_2"
        }
      ],
      "content_type": "mcq",
      "tags": [
        "exam_year_2019"
      ]
    },
    {
      "item_id": "item_id_2",
      "display_name": "Item Two",
      "scorable": false
    }
  ],
  "topics": [
    {
      "topic_id": "topic_id_1",
      "display_name": "Topic One",
      "item_refs": [
        {
          "item_id": "item_id_1"
        }
      ],
      "child_topic_refs": [
        {
          "topic_id": "topic_id_2"
        }
      ]
    },
    {
      "topic_id": "topic_id_2",
      "display_name": "Topic Two",
      "item_refs": [
        {
          "item_id": "item_id_2"
        }
      ]
    }
  ]
}
KeyMandatoryTypeDescription
catalog_idYesStringUnique identifier for the catalog.
display_nameYesStringUsed for display in analytics dashboards and Sana Portal.
itemsYesArray<Item>The set of items in the catalog.
topicsYesArray<Topic>The set of topics in the catalog. Topics give structure to items in the form of a hierarchical grouping, corresponding roughly to chapters of a book.

Each catalog must have at least one item and at least one topic.

Item

KeyMandatoryTypeDescription
item_idYesStringUnique identifier for the item within the catalog.
display_nameYesStringUsed for display in analytics dashboards and Sana Portal.
scorableYesBooleanSpecifies whether interactions with this item should contain a score.
difficultyNoNumber
between
0 and 1
This difficulty will be used in the case of cold start. As Sana Learn gathers more data, the item difficulty will be computed from interaction events. The difficulty is interpreted as the probability that an average learner without direct prior knowledge of the item will fail assessment of the item. If your item database consists of three levels of difficulty, “easy”, “medium” and “hard”, a rule of thumb is to map these levels to difficulty 0.33, 0.5 and 0.66, respectively.
theory_item_refsNoArray<ItemRef>Used for linking theory items to exercise items.
content_typeNoStringText field used to identify items by content type. Examples include mcq (multiple choice question), theory, text, etc.
tagsNoArray<String>Used to specify custom annotations that can be used for filtering.

Each item must be included at least in one topic.

Topic

KeyMandatoryTypeDescription
topic_idYesStringUnique identifier for the topic within the catalog.
display_nameYesStringUsed for display in analytics dashboards and Sana Portal.
item_refsNoArray<ItemRef>Specifies which items belong to this topic. Items in a child topic should not be specified on the parent topic.
child_topic_refsNoArray<TopicRef>Specifies the child topics.
child_topic_refs_specify_prerequisite_orderNoBooleanSpecifies whether the order of child_topic_refs should be used as sequential order of prerequisites for the corresponding child_topics.

Each topic must have at most one parent. Topics should contain either items or child topics, not both.

Topic prerequisites: If topic_refs_specify_prerequisite_order == true and topic_refs = [ { “topic_id": "topic_1” }, { “topic_id": "topic_2” }, { “topic_id": "topic_3” } ], then topic_1 will be considered a prerequisite for topic_2, and topic_2 will be considered a prerequisite for topic_3. The prerequisite information will be used by the adaptive engine.

Item Ref

KeyMandatoryTypeDescription
item_idYesStringReference to an Item.

Topic Ref

KeyMandatoryTypeDescription
topic_idYesStringReference to a Topic.

Catalog annotation

This data is separated from catalog.item because item annotations are potentially orders of magnitude larger than the catalog object. Item annotations are not directly used by real-time algorithms, annotations are used for offline modeling. Item annotations are not synchronized as frequently as the catalog object.

KeyMandatoryTypeDescription
catalog_idYesStringReference to Catalog.
item_annotationsYesArray<ItemAnnotation>Additional data associated to items. Used for analytics and modeling.

Item annotation

KeyMandatoryTypeDescription
item_idYesStringReference to Item.
text_plainNoStringUsed for content analysis through natural language processing. Multiple choice questions should have this field set to a concatenation of the question text and response alternatives, separated by \n.

Example catalog annotation:

{
  "catalog_id": "catalog_id_1",
  "item_annotations": [
    {
      "item_id": "item_id_1",
      "text_plain": "What is 1 + 1?",
    }
  ]
}

Content endpoints

Endpoint POST /v2/upsert-catalog

Creates or updates a catalog.

Request payload

The payload should consists of a Catalog object.

Response

200 if successful, otherwise an Error object is returned along with a suitable non-2xx HTTP response code.

Endpoint POST /v2/query-catalog

Gets the catalog with the specified catalog_id

Request payload

KeyMandatoryTypeDescription
catalog_idYesStringIdentifier of the catalog to fetch.

Response payload

KeyTypeDescription
dataCatalogFull representation of catalog.

Endpoint POST /v2/query-catalog-ids

Gets all catalog IDs for the tenant.

Request payload

The request does not have any payload.

Response payload

KeyTypeDescription
data.catalog_idsArray<String>List of all catalogs IDs for the tenant.

Endpoint POST /v2/delete-catalog

Request payload:

KeyMandatoryTypeDescription
catalog_idYesStringIdentifier of the catalog to delete.

Response: 200 if successful, otherwise an Error object is returned along with a suitable non-2xx HTTP response code.

Endpoint POST /v2/upsert-catalog-annotation

Request payload

The payload should consists of a CatalogAnnotation object.

Response

200 if successful, otherwise an Error object is returned along with a suitable non-2xx HTTP response code.

Adaptive Engine

Endpoint POST /v2/create-adaptive-session

In order to get recommendations from the adaptive engine, a valid session has to be created. The session defines the properties of the recommendations such as the mode, what topics or items to include or when should the adaptive engine stop recommending items. This endpoints returns a session_context which must be sent as a parameter in the /v2/adaptive-engine endpoint.

Example request payload

{
    "user_id": "user_id_1",
    "catalog_id": "catalog_id_1",
    "mode": {
        "type": "review"
    },
    "filter": {
        "include_topic_ids": [
            "topic_id_1",
            "topic_id_2"
        ]
    },
    "stopping_conditions": {
        "max_proficiency": 0.95,
        "max_interaction_count": 20,
        "max_duration_ms": 900000
    }
}

Example response payload

{
  "data": {
    "session_context": "bL5gOYun68zG2UnhS1"
  }
}

Request payload

KeyMandatoryTypeDescription
user_idYesStringUnique identifier for the user.
catalog_idYesStringReference to the Catalog.
modeYesModeSpecifies what mode to use for the adaptive engine within the session.
filterYesFilterFilter to use for computing recommendations. The filter should not match more than 10,000 items.
stopping_conditionsNoStoppingConditionsSpecifies when to end the session.

Mode

See API Modes for a full description of the mode parameter.

KeyMandatoryTypeDescription
typeYesStringThe mode of the Sana Learn session. One of learn, review, assess.

Filter

The filter object is used to specify the subset of items within the catalog for which the Adaptive Engine will provide recommendations and user statuses. The filtering process starts with the complete set of items, and each specified filter condition may reduce the set of items. If a filter condition is not specified, it will be inactive, meaning that no filtering will take place for this condition. One common use case supported by filter objects is to combine include and exclude filters e.g. to exclude a sub-topic within an included topic.

KeyMandatoryTypeDescription
Inclusion conditions
include_topic_idsNoArray<String>Only include items that belong to the specified topics or their sub-topics. If specified, must not be empty.
include_tagsNoArray<String>Only include items that match at least one of the specified tags. If specified, must not be empty.
include_content_typesNoArray<String>Only include items that match the specified content types. See Item.content_type. If specified, must not be empty.
include_item_idsNoArray<String>Only include the specified items. If specified, must not be empty.
Exclusion conditions
exclude_topic_idsNoArray<String>Only include items that do not belong to the specified topics or their sub-topics. If specified, must not be empty.
exclude_tagsNoArray<String>Only include items that do not match at least one of the specified tags. If specified, must not be empty.
exclude_content_typesNoArray<String>Only include items that do not match the specified content types. See Item.content_type. If specified, must not be empty.
exclude_item_idsNoArray<String>Only include items that do not match the specified items. If specified, must not be empty.

Session Stopping Conditions

The adaptive engine will signal (through session status) that a session should be terminated when either of the stopping conditions are met.

KeyMandatoryTypeDescription
max_proficiencyNoNumberThe desired proficiency level to reach during the session, a number between 0 and 1. When the proficiency is reached, the session should be terminated.
max_interaction_countNoIntegerThe maximum number of interactions before a session should be terminated.
max_duration_msNoIntegerThe maximum time elapsed in milliseconds before a session should be terminated.

Response payload

KeyTypeDescription
data.session_contextStringRepresentation of the session context for use by the Adaptive Engine.

Endpoint POST /v2/adaptive-engine

This endpoint serves three main purposes:

  1. Upload events.
  2. Produce recommendations.
  3. Compute user (learner) status.
  4. Compute session status.

These three functions are combined into one endpoint to avoid the possibility of race conditions when uploading events, producing a recommendation and computing the user status.

Example request payload

{
    "session_context": "CO/9tvUNEhAP4Uj3qU5G+qBcSunXdgF2GhF0ZXN0aW5nX3RlbmFudF9pZCIJdXNlcl9pZF8xKgxjYXRhbG9nX2lkXzEyGBIKdG9waWNfaWRfMRIKdG9waWNfaWRfMjoJGWZmZmZmZu4/QAI=",
    "events": [
        {
            "item_id": "item_id_1",
            "topic_id": "topic_id_1",
            "timestamp": "2019-10-08T11:28:04.029Z",
            "type": "attempt",
            "score": 1,
            "time_spent_ms": 60000,
            "recommendation_context": "CO/9tvUNEAY="
        }
    ]
}

Example response payload

{
  "data": {
    "recommendation": {
      "item_id": "item_id_1",
      "topic_id": "topic_id_1",
      "recommendation_context": "CO/9tvUNEhDJEHu52",
      "reason": {
        "keyword": "spaced_repetition",
        "description": "The learner should practice this item again to retain the knowledge."
      }
    },
    "user_status": {
      "progress": 0.47,
      "proficiency": 0.26
    },
    "session_status": {
       "stop_session": true,
       "satisfied_stopping_condition_keys": ["max_interaction_count"],
       "interaction_count": 15,
       "duration_ms": 720000
    }
  }
}

Request payload

KeyMandatoryTypeDescription
session_contextYesStringCreated by Create Session endpoint.
eventsNoArray<Event>Events to upload.

Event

The properties of interaction events depend on the event.type property. The shared properties that all event types have are:

KeyMandatoryTypeDescription
typeYesStringOne of attempt, skip, view_theory.
item_idYesStringReference to Item. Used to specify which part of the curriculum the user is active in. This is relevant because an item might belong to multiple topics.
topic_idYesStringReference to Topic.
timestampYesStringISO 8601 timestamp of when the exercise was completed.
recommendation_contextDependsStringMust be included if the event corresponds to a recommendation by the Adaptive Engine. See Recommendation.

Event of type attempt

In addition to the common event properties, events of type attempt have the following properties.

KeyMandatoryTypeDescription
scoreDepends on item.scorableNumberThe score of attempt at solving the exercise. The score should be normalized to a value between 0 and 1. Incorrect response is represented as 0 and correct response is represented as 1.
time_spent_msNoIntegerThe time spent on solving the exercise in milliseconds.

Event of type skip

No additional properties.

Event of type view_theory

In addition to the common event properties, events of type view_theory have the following properties.

KeyMandatoryTypeDescription
time_spent_msNoIntegerThe time spent on solving the exercise in milliseconds.
fraction_completedNoNumberNumber between 0 and 1. This value is normally obtained by taking the maximum point reached divided by the total time. Example: If user viewed up to 7:27 out of 9:56 video, then set fraction_completed = 0.75. If the user skips to the end of the video, then it is appropriate to set fraction_completed = 1, the user might have already seen the video.

Response payload

KeyTypeDescription
data.recommendationRecommendationRecommendations produced by the adaptive engine. The recommended item will match the session.filter.
data.user_statusUserStatusComputed for the items matching session.filter.
data.session_statusSessionStatusStatus of the current adaptive session.

Recommendation

KeyTypeDescription
item_idStringReference to Item.
topic_idStringReference to Topic.
recommendation_contextStringA context object to be included in the event corresponding to the recommendation.
reasonReasonReason for why this item recommended.

Reason

Each recommendation is associated with a reason that explains on a high level why an item is recommended. The purpose of this field is to make the recommendations more transparent and explainable. The reason can be propagated to the end user. In such case, the learning application should provide a mapping from each reason keyword to a product specific display string that is displayed to the end user.

KeyTypeDescription
keywordStringThe keyword is suitable for programmatic parsing. One of knowledge_retention, observed_knowledge_gap, predicted_knowledge_gap, content_progression, assessment, exploration. This may be different depending on your configuration.
descriptionStringA human-readable string that explains the reason and the user context. Suitable for debugging purposes.

User status

KeyTypeDescription
progressNumberThe progress of the user, a value between 0 and 1. Computed across all items matching the specified filter.
proficiencyNumberThe proficiency of the user, a value between 0 and 1. Computed across all items matching the specified filter.

Session status

KeyTypeDescription
stop_sessionBooleanSignals if the session should be ended. This occurs when at least one stopping conditions is satisfied.
satisfied_stopping_condition_keysArray<String>This field lists one or multiple stopping conditions that are satisfied if stop_session is true. Possible stopping conditions are max_proficiency, max_interaction_count, max_duration_ms.
interaction_countIntegerThe number of interactions within the session so far.
duration_msIntegerThe duration of the session so far in milliseconds.

Endpoint POST /v2/user-status

This endpoint is useful for populating a dashboard with user status across different parts of the catalog. User status within an adaptive session is already included in the response of the adaptive engine endpoint.

Request payload:

KeyMandatoryTypeDescription
user_idYesStringReference to User.
catalog_idYesStringReference to Catalog.
filter_listYesArray<Filter>Specifies the set of items within the catalog for which to compute the user status. A UserStatus object will be computed for each filter.

Response payload:

KeyTypeDescription
data.user_status_listArray<UserStatus>One user status object per specified filter object, in the same order.

Endpoint POST /v2/upsert-events

This endpoint is useful for uploading interaction events that do not belong to an adaptive session. Such events are typically produced by the user when manually selecting exercises.

Events that have the same (user_id, catalog_id, item_id, timestamp) will not be duplicated. In this case, the most recent request overwrites previous requests.

Request payload:

KeyMandatoryTypeDescription
user_idYesStringUnique identifier for the user.
catalog_idYesStringReference to the Catalog.
eventsYesArray<Event>Events to upload.
is_offline_eventsNoBooleanSpecifies whether the events ocurred offline and are being sent to the API at a later point in time. Defaults to false.

Response:

200 if successful, otherwise an Error object is returned along with a suitable non-2xx HTTP response code.

API Modes

The mode parameter expresses the purpose of the adaptive session. The Sana Learn API supports three modes: learn, review and assess.

In a typical use case, the adaptive assessment is used for a placement test. After this the learner can continue learning using the adaptive learn mode. The adaptive review mode can be used to provide the learner with review sessions focusing on their knowledge gaps.

Adaptive learn

The learn mode guides the learner through the content specified by the filter object with an adaptive rate of progress. Previously covered concepts are seamlessly integrated when deemed necessary. For example, to cover knowledge gaps and to retain previously learned knowledge.

Adaptive review

The review mode recommends assets by focusing on learner knowledge gaps within previously covered concepts with the aim of maximizing content recall. This means that the learner will be provided with more questions on the concepts where the proficiency level is low. In the case when the learner has not covered sufficient content within the specified filter, the review mode prioritizes questions with high discriminative power.

Adaptive assess

The adaptive assessment mode is designed to effectively assess the proficiency level of a learner. The assess mode is suitable for use in a placement tests, used to guide the learner to the right part of the course.

Customization

Additional customization beyond the mode type parameter is currently not available as part of the public API. Contact us if you need additional customization.

API semantics

Authentication

A valid API key is needed to access the Sana Web API. Visit Sana Labs Portal to manage apps and API keys. Your secret API keys carry privileges for you to access the Sana Web API, be sure to keep them secret. Do not share your API keys in publicly accessible places such as GitHub or client-side code.

Sana Web API expects the API key to be included in all API requests to the server in a header that looks like the following:

X-API-KEY: $API_KEY

If the key is omitted or is wrong, you will get a 401 Unauthorized response to your request.

To authorize, pass the X-API-KEY header

curl -H "X-API-KEY: $API_KEY" -X POST https://api.sanalabs.com/v2/adaptive-engine

Make sure to replace $API_KEY with your API key.


Response format

All API response payloads contain a JSON object with the following top-level structure:

KeyTypeDescription
dataObjectContains one or multiple returned domain objects.
errorError objectThis field is present if an error occurred.

data and error fields are mutually exclusive, one and only one of them is present in the response object.

Error

KeyTypeDescription
statusIntegerHTTP Status code related to this error.
messageStringDetails around why the error occurred.

String encoding

All strings should be UTF-8 encoded.

All ids and tags must match the regexp ^[a-zA-Z0-9_-]{1,36}$. That is, such a string cannot be longer than 36 characters and should consist of alphanumerics, underscore or hyphen.

Content-Type

The Sana Learn API consumes and produces JSON data. The content-type header must be set to application/json when calling API endpoints.