Import questions from GIFT, Aiken or CSV
US Region: https://public-api.sessionboard.com · EU Region: https://public-api-eu.sessionboard.com
Reads a GIFT, Aiken or CSV file (sent as text) and lists every question found as a row, ready or with its problems. Without commit nothing is written. With commit: true the ready rows are added to the bank; rows with problems are only left out when skipInvalid is true, and the answer lists every row not imported with its reason. Up to 1 MB and 500 questions. Requires write:learning. Also at POST /v1/learning/workspace/question-bank/import.
Request example
curl --request POST \ --url 'https://public-api.sessionboard.com/v1/organization/{orgId}/learning/workspace/question-bank/import' \ --header 'x-access-token: <api-token>' \ --header 'Content-Type: application/json' \ --data '{ "text": "<string>", "format": "gift", "filename": "<string>", "tags": [ "<string>" ], "commit": true, "skipRows": [ 1 ], "skipInvalid": true}'const response = await fetch('https://public-api.sessionboard.com/v1/organization/{orgId}/learning/workspace/question-bank/import', { method: 'POST', headers: { "x-access-token": "<api-token>", "Content-Type": "application/json" }, body: JSON.stringify({ "text": "<string>", "format": "gift", "filename": "<string>", "tags": [ "<string>" ], "commit": true, "skipRows": [ 1 ], "skipInvalid": true }),});
const data = await response.json();console.log(data);import requests
response = requests.request( "POST", "https://public-api.sessionboard.com/v1/organization/{orgId}/learning/workspace/question-bank/import", headers={ "x-access-token": "<api-token>", "Content-Type": "application/json" }, json={ "text": "<string>", "format": "gift", "filename": "<string>", "tags": [ "<string>" ], "commit": True, "skipRows": [ 1 ], "skipInvalid": True },)
print(response.json())<?php
$curl = curl_init();
curl_setopt_array($curl, [ CURLOPT_URL => "https://public-api.sessionboard.com/v1/organization/{orgId}/learning/workspace/question-bank/import", CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => '{ "text": "<string>", "format": "gift", "filename": "<string>", "tags": [ "<string>" ], "commit": true, "skipRows": [ 1 ], "skipInvalid": true}', CURLOPT_HTTPHEADER => [ "x-access-token: <api-token>", "Content-Type: application/json", ],]);
$response = curl_exec($curl);curl_close($curl);
echo $response;package main
import ( "fmt" "io" "net/http" "strings")
func main() { req, _ := http.NewRequest("POST", "https://public-api.sessionboard.com/v1/organization/{orgId}/learning/workspace/question-bank/import", strings.NewReader(`{ "text": "<string>", "format": "gift", "filename": "<string>", "tags": [ "<string>" ], "commit": true, "skipRows": [ 1 ], "skipInvalid": true}`)) req.Header.Add("x-access-token", "<api-token>") req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))}HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://public-api.sessionboard.com/v1/organization/{orgId}/learning/workspace/question-bank/import")) .header("x-access-token", "<api-token>") .header("Content-Type", "application/json") .method("POST", HttpRequest.BodyPublishers.ofString("{\n \"text\": \"<string>\",\n \"format\": \"gift\",\n \"filename\": \"<string>\",\n \"tags\": [\n \"<string>\"\n ],\n \"commit\": true,\n \"skipRows\": [\n 1\n ],\n \"skipInvalid\": true\n}")) .build();
HttpResponse<String> response = HttpClient.newHttpClient() .send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());Try it
Response headers
Authorizations
x-access-tokenstringheaderOrganization API token. Generate from Organization Settings → API Tokens.
AuthorizationBearer <token>headerorOAuth 2.1 access token. Obtained via the OAuth PKCE flow at
/oauth/token.
Send one of these. See Authentication.
Path parameters
orgIdstringpathrequiredYour organization ID. Any other organization returns
404.
Request body
textstringrequiredThe file's contents.
- Max length
1100000
formatstringLeave out to detect it.
- Allowed values
"gift", "aiken", "csv"
filenamestring- Max length
255
tagsstring[]Added to every question.
- Max items
10
commitbooleanskipRowsinteger[]Preview rows to leave out.
- Max items
500
skipInvalidbooleanImport the ready rows when other rows have problems.
{ "text": "<string>", "format": "gift", "filename": "<string>", "tags": [ "<string>" ], "commit": true, "skipRows": [ 1 ], "skipInvalid": true}Responses
200application/json
The preview, or what was imported.
successbooleanpayloadobjectShow child attributes
formatstring- Allowed values
"gift", "aiken", "csv"
fatalstringWhy nothing could be read (an empty file, no Question column).
summaryobjectShow child attributes
totalintegerreadyintegerwith_errorsinteger
rowsobject[]Show child attributes
rowintegerlineintegerpromptstring | nulltype_labelstring | nulltagsstring[]errorsstring[]warningsstring[]questionobject | null
importedintegernot_importedobject[]Show child attributes
rowintegerpromptstringreasonstring
400application/json
A path, query or body value failed validation, or an unknown body property was sent. message names the field. (A source object naming the assistant, with the person's prompt, is accepted on every write and removed before validation.) For an {eventId} path that is not a whole number: error: VALIDATION_ERROR, eventId must be a valid integer.
Gateway errors carry error (a code) and message. Errors from Learning itself carry status, success: false, message, name, and sometimes code (and feature or paused_at).
errorstring | objectThe gateway's error code (
SCOPE_REQUIRED,ORG_WIDE_TOKEN_REQUIRED,NOT_FOUND, …).messagestringstatusintegersuccessbooleannamestringcodestringFEATURE_NOT_ENABLED,AGENT_WRITES_PAUSED, …featurestringpaused_atstring<date-time>
401
Missing, invalid or expired token.
403application/json
SCOPE_REQUIRED— the token lacksread:learning(reads) orwrite:learning(writes). Legacy tokens with no scopes cannot write.ORG_WIDE_TOKEN_REQUIRED— the token is restricted to particular events: "The Learning workspace covers every event and learner in the organization, so this API key (restricted to particular events) cannot use it. Use a key without an event restriction."FEATURE_NOT_ENABLED(feature: lessons_credits) — Learning is not on for the organization.FORBIDDEN— the token's user is not an active member of the organization, or lacks the Lessons permission the operation needs.
Gateway errors carry error (a code) and message. Errors from Learning itself carry status, success: false, message, name, and sometimes code (and feature or paused_at).
errorstring | objectThe gateway's error code (
SCOPE_REQUIRED,ORG_WIDE_TOKEN_REQUIRED,NOT_FOUND, …).messagestringstatusintegersuccessbooleannamestringcodestringFEATURE_NOT_ENABLED,AGENT_WRITES_PAUSED, …featurestringpaused_atstring<date-time>
404application/json
orgId is not your organization (error: NOT_FOUND, message: Organization not found).
Gateway errors carry error (a code) and message. Errors from Learning itself carry status, success: false, message, name, and sometimes code (and feature or paused_at).
errorstring | objectThe gateway's error code (
SCOPE_REQUIRED,ORG_WIDE_TOKEN_REQUIRED,NOT_FOUND, …).messagestringstatusintegersuccessbooleannamestringcodestringFEATURE_NOT_ENABLED,AGENT_WRITES_PAUSED, …featurestringpaused_atstring<date-time>
429application/json
- The token's request limit (100 requests per 15 minutes per bucket by default — see Rate Limiting). Wait
Retry-Afterseconds. QUOTA_EXCEEDED(writes) — the token's daily write quota (10,000 a day). Resets at midnight UTC.
Gateway errors carry error (a code) and message. Errors from Learning itself carry status, success: false, message, name, and sometimes code (and feature or paused_at).
errorstring | objectThe gateway's error code (
SCOPE_REQUIRED,ORG_WIDE_TOKEN_REQUIRED,NOT_FOUND, …).messagestringstatusintegersuccessbooleannamestringcodestringFEATURE_NOT_ENABLED,AGENT_WRITES_PAUSED, …featurestringpaused_atstring<date-time>
Retry-AfterintegerheaderSeconds to wait before retrying.
Response examples
{ "success": true, "payload": { "format": "gift", "fatal": "<string>", "summary": { "total": 123, "ready": 123, "with_errors": 123 }, "rows": [ { "row": 123, "line": 123, "prompt": "<string>", "type_label": "<string>", "tags": [ "<string>" ], "errors": [ "<string>" ], "warnings": [ "<string>" ], "question": {} } ], "imported": 123, "not_imported": [ { "row": 123, "prompt": "<string>", "reason": "<string>" } ] }}{ "error": "<string>", "message": "<string>", "status": 123, "success": true, "name": "<string>", "code": "<string>", "feature": "<string>", "paused_at": "2026-01-15T09:30:00Z"}{ "error": "ORG_WIDE_TOKEN_REQUIRED", "message": "The Learning workspace covers every event and learner in the organization, so this API key (restricted to particular events) cannot use it. Use a key without an event restriction."}{ "error": "SCOPE_REQUIRED", "message": "This operation requires the 'write:learning' scope."}{ "status": 403, "success": false, "message": "Feature 'lessons_credits' is not enabled for this organization", "name": "FeatureNotEnabledError", "code": "FEATURE_NOT_ENABLED", "feature": "lessons_credits"}{ "error": "NOT_FOUND", "message": "Organization not found"}{ "error": "<string>", "message": "<string>", "status": 123, "success": true, "name": "<string>", "code": "<string>", "feature": "<string>", "paused_at": "2026-01-15T09:30:00Z"}