Overview

Sana Learn is a plug and play solution to add personalization to learning products. Sana Learn evaluates learners’ answers, response times and an array of contextual information to understand what they 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 undergone extensive improvement 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 allow for more flexible configuration.
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.
adaptiveenginen/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.

Sana Learn Catalog Example

Catalog

A catalog represents the complete set of recommendable content, and 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",
  "topics": [
    {
      "topic_id": "topic_id_1",
      "display_name": "Topic One",
      "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.
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 database

The item database is a global collection of items that catalogs can refer to. The item database is an item list object.

Item list

Example item list object:

{
  "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
    }
  ]
}
KeyMandatoryTypeDescription
itemsYesArray<Item>List of items.

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<TheoryItemRef>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.

Topic

KeyMandatoryTypeDescription
topic_idYesStringUnique identifier for the topic within the catalog.
display_nameYesStringUsed for display in analytics dashboards and Sana Portal.
item_refsNoArray<TopicItemRef>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.
prerequisite_topic_refsNoArray<TopicRef>Specifies the topic prerequisites.

Each topic must have at most one parent. Topics should contain either items_refs or child_topics_refs, not both.

Topic Ref

KeyMandatoryTypeDescription
topic_idYesStringReference to a Topic.

Topic Item Ref

KeyMandatoryTypeDescription
item_idYesStringReference to an Item.
difficultyNoNumber between 0 and 1Used to override Item difficulty specific to this topic, if available.

Theory Item Ref

KeyMandatoryTypeDescription
item_idYesStringReference to an Item.

Item database annotation

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

KeyMandatoryTypeDescription
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-items

Updates or inserts items in the item database.

Request payload

The payload should consists of an ItemList object.

Response

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

Endpoint POST /v2/delete-items

Deletes items from item database.

Request payload

KeyMandatoryTypeDescription
item_idsYesArray<String>List of item ids 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-item-database

Updates or inserts the entire item database. Note: this endpoint will remove items that are not upserted, consider using upsert-items.

Request payload

The payload should consists of an Item database object.

Response

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

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

Returns 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-item-database

Returns the item database.

Request payload

Empty.

Response payload

Item database object.

Endpoint POST /v2/query-catalog-ids

Returns all catalog IDs for the API key.

Request payload

The request does not have any payload.

Response payload

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

Endpoint POST /v2/delete-catalog

Request payload

KeyMandatoryTypeDescription
catalog_idYesStringIdentifier of the catalog to delete.

Respons

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

Endpoint POST /v2/upsert-item-database-annotation

Request payload

The payload should consists of a ItemDatabaseAnnotation object.

Response

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

Content size limits

By default, the following size restrictions are applied to content for every application (API key):

DimensionLimit
Number of items in item database100 000
Number of catalogs3 000
Number of topics per single catalog2 500
Total number of TopicItemRef per single catalog.20 000

When an attempt is made to upload content past the limit, Sana API responds with HTTP code 413 and a detailed message in the body regarding what limit was hit.

If the limits are too low for your use case, please contact Sana Labs for adjustment.

Adaptive Engine

Endpoint POST /v2/create-adaptive-session

In order to obtain 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 the adaptive engine should stop recommending items. This endpoint 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
    },
    "constraints": [
        {
            "type": "max_recommendations",
            "value": 1,
            "filter": {
                "include_topic_ids": [
                    "topic_2"
                ]
            }
        }
    ]
}

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.
constraintsNoArray<SessionConstraint>Specifies additional requirements on recommendations within the session. Maximum of 5 constraints is allowed in the array.

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.

Session Constraints

This is a BETA feature, future changes are possible

When a session is created, session constraints could be used to fine-tune what recommendations are served in what quantities.

For example to limit the number of repetitions of items with IDs item_id_1 and item_id_2 the following constraint must be set:

{
    "type": "max_recommendations",
    "value": 2,
    "filter": {
        "include_item_ids": [
            "item_id_1", "item_id_2"
        ]
    }
}

The rules could be applied to specific items, topics, tags, and content types using a filter object provided in the filter field.

KeyMandatoryTypeDescription
typeYesStringType of the constraint. Currently, only max_recommendations is supported.
valueYesNumberConfigures a specific constraint. For example, maximal number of repetetions.
filterYesFilterSpecifies subset of items to which the constraint should apply.

Response payload

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

Endpoint POST /v2/adaptive-engine

This endpoint serves four purposes:

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

These four 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": [
    {
      "type": "attempt",
      "timestamp": "2019-10-08T11:28:04.029Z",
      "recommendation_context": "CO/9tvUNEAY=",
      "score": 1,
      "time_spent_ms": 60000,
    }
  ]
}

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, listed below. The shared properties that all event types have depend on if the event has an associated recommendation_context:

Common properties of plain event

Used with /v2/upsert-events.

KeyMandatoryTypeDescription
typeYesStringOne of attempt, skip, view_theory.
item_idYesStringReference to Item.
topic_idNoStringReference to Topic. Used to specify which part of the curriculum the user was active in at the time of the event, if available. This is relevant because an item might belong to multiple topics.
catalog_idYesStringReference to the Catalog.
timestampYesStringISO 8601 timestamp of when the exercise was completed.

Common properties of event with recommendation_context

Used with /v2/adaptive-engine.

KeyMandatoryTypeDescription
typeYesStringOne of attempt, skip, view_theory.
timestampYesStringISO 8601 timestamp of when the exercise was completed.
recommendation_contextYesStringMust be included if the event corresponds to a recommendation by the Adaptive Engine. See Recommendation.

Event types

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.
proficiencyNumber?The proficiency of the user, a value between 0 and 1, or null if there are no scorable items in the filter. Computed across all items matching the specified filter.
time_spent_msIntegerThe sum of time_spent_ms for all interactions matching the specified filter.
last_interaction_timestampString?ISO 8601 timestamp of the most recent interaction 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.
eventsYesArray<Event>Events to upload.

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. These modes in order power Mastery Learning, Personalized Review Sessions and Adaptive Assessment use cases.

Mastery Learning

Learn mode guides the learner through the content specified by the filter object to reach mastery. The learn mode optimizes content difficulty adhering to content order, resurfaces materials when deemed necessary and offers remedial content until full mastery is reached. The learn mode ensures mastery in prerequisite knowledge before moving forward to learn subsequent information.

Personalized Review Sessions

The objective of the Personalized Review Sessions is to maximize knowledge retention by focusing on observed knowledge gaps and resurfacing content that creates long-term memory. This means that the learner will be provided with more questions on the concepts where the proficiency level is low, but progress is high. A prerequisite to use review mode is to have progress within the specified filter. If nothing has been learned, there is nothing to review.

Adaptive Assessment

The objective of the adaptive assessment is to assess the proficiency level of a learner with minimum number of interactions with a high confidence. This is done by evaluating the user profifiency and serving content that has the highest assessment power at each step within the test.

In a typical scenario, when a learner wants to learn a piece of content, the learning product should first assess the skill level of a user by using Adaptive Assessment. This will reveal what the learner knows and what they don’t know. Then the learning app should teach this content to the learner highly focusing on their knowledge gaps by Mastery Learning. Once the content is mastered, it is appropriate to resurface the material later on using Personalized Review Sessions to create long-term memories.

Customization

It is also possible to combine different modes and create a custom use case beyond the default modes. 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,128}$.

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.