Data API contains an Item definition structure - a clean and consistent format for specifying the layout of Questions and Features within an Item. The Item definition makes it easy to create, import and update Items with advanced layouts via Data API. Single column and side-by-side layouts as well as tabs and scrolling panels can all be created programmatically using the expressive definition structure.
Author API v1 and Data API v1 both use the same Item definition, so your Items are fully interoperable whether you bulk upload them or use the authoring tools.
The Item definition determines the Item's layout everywhere it is rendered to a user: in an Items API Activity, in Author API, or in Reports API, on desktop, tablet and mobile.
Basics
The Item definition is stored in the definition property against the Item. The definition is returned with all Items retrieved from Data API v1's itembank/items endpoint . You must specify an Item definition when you create or update Items via Data API v1.
The Item definition is specified as one or more nested objects, representing regions and a list of Widgets (Questions and/or Features) they contain. Here's a sample Item demonstrating the simplest kind of Item definition - a single root region and a list of Widgets. The two Widgets will be rendered in a single column layout, in the same order they appear in the array.
{
"reference": "5b8b9d40-f03b-4df3-b276-84eef5580431",
"status": "unpublished",
"definition": {
"widgets": [
{
"reference": "1a52f377-0db1-4aed-818c-866f684bb088"
},
{
"reference": "2b21eca6-37af-4272-950a-fd9c472b2290"
}
]
},
"questions": [
{
"reference": "2b21eca6-37af-4272-950a-fd9c472b2290",
"type": "clozeassociation"
}
],
"features": [
{
"reference": "1a52f377-0db1-4aed-818c-866f684bb088",
"type": "sharedpassage"
}
]
}There are different types of regions to support more complex layouts. The currently supported region types are root, column and tab. Each region type may contain either:
- A set of Widgets (rendered vertically in a root, column or tab region); OR
- A set of nested regions, which must all share the same type. Use multiple layers of nesting to achieve combinations of columns and tabs.
The following sections cover the usage and behaviour of the different region types.
The root region
{
"regions": [],
"widgets": [
{
"reference": "widget_reference_1"
},
{
"reference": "widget_reference_2"
}
],
"vertical_divider": false,
"scroll": {
"enabled": false
}
}The root region is always at the top level of the Item definition. Its rendering behaviour is designed to easily accommodate the most common Item layouts:
- If the
widgetsarray is specified, those Widgets will be rendered in a single vertical column, in the same order they are listed in the array. - If the
regionsarray is specified as a list of column regions, the columns are rendered side-by-side. - If the
regionsarray is specified as a list of tab regions, the tabs will occupy the full width and height of the Item.
Note: either widgets or regions MUST be defined at the root level.
The vertical_divider and scroll properties can only be set on the root region. They globally determine the behaviour for all regions on the Item.
| Property | Type | Description |
|---|---|---|
| type | `"root"` | Required: false Default: "root"
|
| regions | array of Region objects |
Nested column or tab regions. The given Region objects must all share the same type. Required: false, unless "Widgets" is not defined |
| Widgets | array of IncludedWidget objects |
Each IncludedWidget object contains a Required: false, unless "regions" is not defined |
| vertical_divider | boolean |
Specify true to render a vertical divider between all column regions in the Item. Enabling this option on the root region affects all descendent regions. Required: false |
| scroll | ScrollOptions object |
If specified, the ScrollOptions has a single property as follows: Specify Omit the scroll property (or specify Required: false |
Column regions
{
"type": "column",
"regions": [],
"widgets": [
{
"reference": "widget_reference_1"
},
{
"reference": "widget_reference_2"
}
],
"width": 50
}Column regions subdivide their parent region into a set of side-by-side columns. Any parent region may contain either 1 or 2 columns. Widgets included in a column region will be rendered in a vertical stack, in the same order they appear in the widgets array.
| Property | Type | Description |
|---|---|---|
| type | "column" | Required: true |
| regions | array of Region objects |
Nested column or tab regions. The given Region objects must all share the same type. Required: false |
| widgets | array of IncludedWidget objects |
Each IncludedWidget object contains a Required: false |
| width | int |
Controls the width of this column, as a percentage of the width of the parent region. Allowed values are 50, 30, 40, 60, 70, 100. The width value of sibling columns must sum to 100. Required: false |
Tabs regions
A tabs region acts as a required wrapper for grouping tabs - individual tabs will render standalone without this defined parent. The wrapper will occupy the full width and height of the parent region, and will provide the navigation element for displaying tabs.
{
"type": "tabs",
"regions": [
{
"type": "tab",
...
},
{
"type": "tab",
...
}
]
}| Property | Type | Description |
|---|---|---|
| type | "tabs" | Required: true |
| regions | array of Region objects |
Nested column or tab regions. The given Region objects must all share the same type. Required: true |
Tab regions
{
"type": "tab",
"label": "tab-1",
"regions": [],
"widgets": [
{
"reference": "widget_reference_1"
},
{
"reference": "widget_reference_2"
}
]
}All sibling tab regions will be rendered as a set of tabs within their parent region. The set of tabs will occupy the full width and height of their parent region. Widgets included in a tab region will be rendered as a vertical stack within the tab's panel.
| Property | Type | Description |
|---|---|---|
| type | "tab" | Required: true |
| label | string |
Label to be displayed for this tab. Required: true |
| regions | array of Region objects |
Nested column or tab regions. The given Region objects must all share the same type. Required: false |
| widgets | array of IncludedWidget objects |
Each IncludedWidget object contains a Required: false |
Example: 2 columns
{
"regions": [
{
"type": "column",
"width": 70,
"widgets": [
{
"reference": "widget_reference_1"
}
]
},
{
"type": "column",
"width": 30,
"widgets": [
{
"reference": "widget_reference_2"
}
]
}
]
}Example: 2 tabs
{
"type": "tabs",
"regions": [
{
"type": "tab",
"label": "Tab 1",
"widgets": [
{
"reference": "widget_reference_1"
}
]
},
{
"type": "tab",
"label": "Tab 2",
"widgets": [
{
"reference": "widget_reference_2"
}
]
}
]
}Example: combining tabs and columns
{
"regions": [
{
"type": "column",
"width": 60,
"regions": [
{
"type": "tabs",
"regions": [
{
"type": "tab",
"label": "Tab 1",
"widgets": [
{
"reference": "widget_reference_1"
}
]
},
{
"type": "tab",
"label": "Tab 2",
"widgets": [
{
"reference": "widget_reference_2"
}
]
}
]
}
]
},
{
"type": "column",
"width": 40,
"widgets": [
{
"reference": "widget_reference_3"
}
]
}
]
}Dynamic content in the Item
Dynamic Content lets a single Question definition serve many variations, by pulling values from a data table into the Question's formula, response, and validation fields at render time. See What is Dynamic Content? and Setting Up a Data Table and Inserting Dynamic Content into a Question for the authoring-interface (CSV paste) workflow.
The same data table can also be created or updated programmatically, via the dynamic_content_data property on an Item, when using the Data API v1's Set Items endpoint. This lets you bulk-import Items and Questions together with their dynamic content data in a single request, instead of creating or pasting a CSV into each Item individually through Author API.
dynamic_content_data is a sibling property to definition on the Item object - it is not part of the layout structure, but travels with the Item in the same way questions and features do.
{
"reference": "math_question_operands",
"status": "unpublished",
"definition": {
"widgets": [
{
"reference": "operand_question_1"
}
]
},
"questions": [
{
"reference": "operand_question_1",
"type": "formulaV2"
}
],
"dynamic_content_data": {
"cols": [
"operand1",
"operand2",
"sum"
],
"rows": {
"1ba3db0c-4c51-4dd7-a610-18048fffda50": {
"values": [
"4",
"8",
"12"
],
"index": 0
},
"dd9d49fc-7e0b-4f12-9d2f-4d5b63893db0": {
"values": [
"6",
"9",
"15"
],
"index": 1
},
"fa9d76sw-7e0b-4f12-9f2f-4d5b85305jd3": {
"values": [
"12",
"3",
"15"
],
"index": 2
}
}
}
}| Property | Type | Description |
|---|---|---|
| cols | array of strings | Required: true The column headers of the data table, in display order. Each entry becomes a table variable name that can be referenced from the Question's formula, response, or validation fields (for example, {{operand1}}). The number and order of entries in cols defines the number and order of entries expected in every row's values array. |
| rows | object | Required: true A map of individual data table rows, keyed by a unique row reference (a UUID). Each key represents one row - equivalent to one row of the CSV you would otherwise paste into the Author Site. |
| rows{row_reference} | string (UUID) | Required: true A unique identifier for the row. This can be generated by you (e.g., when converting from CSV/spreadsheet source data) and is used to identify the row for future updates; it is not displayed to the learner. |
| rows{row_reference}.values | array of strings | Required: true The cell values for this row, positionally matched to the cols array, i.e., values[0] corresponds to cols[0], values[1] to cols[1], and so on. The array must contain exactly as many entries as cols, in the same order. |
| rows{row_reference}.index | integer | Required: true The zero-based display/preview order of this row relative to other rows in the table. |
Important: Every entry in a row's values array must be a string, even if it represents a number. Unquoted or non-string values can cause the content engine to bind the wrong column to the Question at render/preview time.
// Correct — every value is a quoted string
"values": ["4", "8", "12"]
// Avoid — unquoted/non-string values can cause column mismatches
"values": [4, 8, 12]If converting from CSV or a spreadsheet as part of a bulk import script, ensure your conversion step casts every cell value to a string before writing it into values, correctly handling any source values that use quote-wrapping to protect internal commas (for example, 10,000).
Note: Data API v1 recommends a limit of 20 columns and 50 rows per data table.