Create content records in bulk

Prev Next
Post
/contents/bulk

Coming soon. This endpoint is not available yet, and the details below may change before release.

Submits an asynchronous job that creates up to 100 content records. Each entry takes the same body as POST /contents.

The endpoint responds immediately with 202 Accepted and a jobId; poll GET /contents/bulk/{jobId} for progress and per-entry results. Each successful result carries the new record's id and its presigned upload URLs, so one poll gives you everything you need to start uploading.

The batch is validated before any job is created, so a malformed request never half-runs. If every entry is invalid you get 422; if only some are, 207. In both cases no job is created — correct the entries and resubmit the whole batch. Failures that only surface once the job is running are reported per entry in results[] and do not stop the rest of the batch.

Every submission must carry an Idempotency-Key header. Replaying the same key returns the existing job rather than starting a second one, which is what makes retrying after a timeout safe. It deduplicates submissions, not content — two different keys create two sets of records.

Security
HTTP
Type basic

HTTP Basic authentication using your Tenant ID and API secret.

  • Username is the Tenant ID (i.e. eu-west-2_AbcdEfghI)
  • Password is the API secret you would have received.

You can also authenticate using OAuth 2.0 client credentials (oauth2 security scheme) instead of Basic auth.

OAuth

OAuth 2.0 client credentials authentication.

Use the following token endpoints (replace :tenantId with your Tenant ID):

  • Public API access tokens

    • Staging: https://public.api.learnstaging.link/oauth2/token/:tenantId
    • Production: https://public.api.learn.link/oauth2/token/:tenantId
    • Staging MEA: https://public.api.meastaging.learn.tech/oauth2/token/:tenantId
    • Production MEA: https://public.api.mea.learn.tech/oauth2/token/:tenantId
  • Webhooks access tokens

    • Staging: https://user.api.learnstaging.link/oauth2/token/:tenantId
    • Production: https://user.api.learn.link/oauth2/token/:tenantId
    • Staging MEA: https://user.api.meastaging.learn.tech/oauth2/token/:tenantId
    • Production MEA: https://user.api.mea.learn.tech/oauth2/token/:tenantId

All access tokens must be sent using the Authorization: Bearer <access_token> header.

Scopes

For API access (non-webhooks), the following scopes are available:

  • api/all – Full read and write access to the API.
  • api/read – Read-only access to the API.
  • api/write – Write access to the API.

For webhooks, the following scopes are available:

  • api/webhooks – Access to webhook functionality.
  • api/all – Full read and write access to the API, including webhooks.

Tokens must include appropriate scopes for the endpoints you wish to call.

FlowClient Credentials
Token URLhttps://public.api.learn.link/oauth2/token/:tenantId
Scopes:
api/writeWrite access to the API.
Header parameters
Idempotency-Key
stringRequired

Caller-generated idempotency key for the submission. Generate it once per logical submission and reuse it on retries — replaying the same key returns the existing job instead of creating a duplicate. Requests without the header are rejected with 422.

Body parameters
bulkCreateContent

Create two records in one submission

Each entry is the same body POST /contents takes. Remember the Idempotency-Key header — without it the submission is rejected.

{
  "contents": [
    {
      "title": "Fire safety",
      "primaryCategory": "article",
      "authorRef": "UID30084022",
      "languageCode": "en-gb",
      "items": [
        {
          "type": "rich_text",
          "rank": 0,
          "summary": "Fire safety basics",
          "meta": {
            "body": "

Fire safety basics.

" } } ] }, { "title": "Data protection", "primaryCategory": "elearning", "authorRef": "UID30084022", "languageCode": "en-gb", "items": [ { "type": "elearning", "rank": 0, "meta": { "name": "gdpr.zip", "uploadId": "1a2b3c4d-5e6f-4708-9a0b-1c2d3e4f5a6b" } } ] } ] }
Expand All
object

The content records to create.

contents
Array of object (ContentCreateBody) Required

The content records to create, at most 100 per submission

Min items1
Max items100
object

authorRef and, when present, collaborationId are validated in your organisation before anything is created.

title
string Required
Min length3
Max length150
ExampleOnboarding pack
languageCode
string Required

The content language, for example en-gb.

Exampleen-gb
authorRef
string Required

Your organisation's reference for the acting user. The user becomes the recorded actor, and the author unless a collaboration owns the content. The user must be active. A new, suspended, expired or deleted user is rejected.

ExampleUID30084022
collaborationId
string

The collaboration that owns the content. The authorRef user must be a member of it, or the request is rejected with 422.

Pattern^[0-9a-fA-F]{24}$
Example5c8936159ec2d00010cdd334
publishWhenReady
boolean

Publish automatically when the content becomes ready: every item has completed processing and the content has a thumbnail. Send thumbnail to get a thumbnail upload URL in the same response; without a thumbnail the content stays created. The flag is stored so the publish happens as soon as the record is ready.

skills
Array of string

Skills to associate with the content. Each name must be an existing skill in your organisation, or the request is rejected with 422. Only accepted together with publishWhenReady: true. Stored in the content tags alongside the topics.

string
Max length255
Exampleleadership
topics
Array of string

Topics to associate with the content. Free names; a topic is not checked against your organisation's tags. Only accepted together with publishWhenReady: true. Stored in the content tags alongside the skills. A name cannot appear in both lists.

string
Max length255
Exampleonboarding
primaryCategory
string

The declared category. A default is inferred when it is omitted. The value is case insensitive, but send it in lower case.

Valid values[ "file", "article", "elearning", "url", "quiz", "assessment", "video", "event", "pathway", "question", "standard" ]
Exampleelearning
completionType
string

How the content is completed. Defaults to auto. auto, confirm and external apply to any content; on_elearning_completion requires an e-learning item. The quiz, assessment and interactive types require item types this API cannot create yet, so they are rejected with a 409.

Valid values[ "auto", "confirm", "external", "on_passing_quizzes", "on_passing_observer_completed_quizzes", "on_passing_assessments", "on_elearning_completion", "on_completing_interactive" ]
customDurationInMs
integer

A custom duration in milliseconds.

Minimum1.0
Example60000
isOfficial
boolean

Create the content as endorsed by your organisation. Defaults to false.

Exampletrue
cpdCategoryId
string

The CPD category to record this content against. Only accepted together with isOfficial: true (400 otherwise). The category must exist in your organisation (422 otherwise).

Pattern^[0-9a-fA-F]{24}$
Example507f1f77bcf86cd799439012
thumbnail
object (ContentThumbnailInput)

Asks for a thumbnail upload URL. The content thumbnail is uploaded like an item file: give the upload a UUID here and PUT the image bytes (JPEG, PNG, GIF or WebP) to the signedPutUrls entry that echoes it. The uploadId is not stored; it only pairs this request with its response entry, so it must differ from every item uploadId in the same request (400 otherwise). A content has one thumbnail, and a later upload replaces it. Processing is asynchronous: the thumbnail counts for readiness once the image has been resized and stored.

uploadId
string (uuid) Required

A UUID you generate for this upload.

Example8b1e3d2a-4c5f-4e6a-9b7c-0d1e2f3a4b5c
items
Array

The content items. At most one upload item (elearning, video or image) plus one non-upload item (rich_text or url) per request. Each rank must be unique.

OneOf
ContentCreateRichTextItem
object (ContentCreateRichTextItem)
type
string Required
Valid values[ "rich_text" ]
rank
integer Required

The display position of the item.

Minimum0.0
summary
string Required

A short description of the item.

meta
object Required
body
string Required

The rich text body. May contain HTML.

Min length1
Max length100000
ContentCreateUrlItem
object (ContentCreateUrlItem)
type
string Required
Valid values[ "url" ]
rank
integer Required
Minimum0.0
summary
string Required
meta
object Required
url
string Required

A public HTTP(S) address. The link preview is generated asynchronously.

Examplehttps://example.com/guide
ContentCreateElearningItem
object (ContentCreateElearningItem)
type
string Required
Valid values[ "elearning" ]
rank
integer Required
Minimum0.0
summary
string
meta
object Required
name
string Required

The package file name, for example course.zip.

Examplecourse.zip
uploadId
string (uuid) Required

A client-generated UUID for the upload. The response echoes it in signedPutUrls.

format
string

The package format. Normally detected during processing — supply it only when known.

Valid values[ "scorm12", "scorm2004", "cmi5", "tincan", "web" ]
ContentCreateFileItem
object (ContentCreateFileItem)
type
string Required

The file kind. The file extension in meta.name must match it.

Valid values[ "video", "image" ]
rank
integer Required
Minimum0.0
summary
string
meta
object Required
name
string Required

The file name, for example intro.mp4 or cover.png.

Exampleintro.mp4
uploadId
string (uuid) Required

A client-generated UUID for the upload. The response echoes it in signedPutUrls.

canDownload
boolean Required

Whether learners can download the file.

Responses
202

Accepted — a bulk create job was created (or an existing job was matched by idempotency key). Poll pollUrl for progress and per-item results.

accepted

Job accepted

The batch was accepted. Poll pollUrl for progress and per-entry results.

{
  "jobId": "6863f9a2c1d2e3f4a5b6c7d8",
  "status": "pending",
  "pollUrl": "/rest/v2/contents/bulk/6863f9a2c1d2e3f4a5b6c7d8"
}
object

Returned when a bulk content job has been accepted for processing.

jobId
string

The id of the job processing the batch

Example6863f9a2c1d2e3f4a5b6c7d8
status
string

The processing status of a bulk job

Valid values[ "pending", "processing", "completed", "failed" ]
Exampleprocessing
pollUrl
string

The path to poll for progress and per-item results

Example/rest/v2/contents/bulk/6863f9a2c1d2e3f4a5b6c7d8
207

Multi-Status — some entries failed validation, so no job was created. The envelope lists the entries that passed and the ones that were rejected, so the batch can be corrected and resubmitted.

partiallyRejected

Some entries rejected, no job created

Entry 2 failed validation, so nothing was submitted. Correct it and resubmit the whole batch.

{
  "success": {
    "count": 2,
    "entities": [
      {
        "reference": "contents[0]"
      },
      {
        "reference": "contents[1]"
      }
    ]
  },
  "failure": {
    "count": 1,
    "entities": [
      {
        "reference": "contents[2]",
        "message": "title is required"
      }
    ]
  }
}
Expand All
object

A multi-status envelope returned when some entries in the batch failed validation. No job is created — correct the failed entries and resubmit the whole batch.

success
object

The entries that passed validation

count
number

The number of entries that passed validation

Example2
entities
Array of object

The entries that passed, by position in your request

object
reference
string

The entry's position in the submitted array

Examplecontents[0]
failure
object

The entries that failed validation

count
number

The number of entries that failed validation

Example1
entities
Array of object

The entries that were rejected, with the reason for each

object
reference
string

The entry's position in the submitted array

Examplecontents[2]
message
string

The reason the entry was rejected

ExamplelanguageCode is required
401

Unauthorized

object

Unauthorized

status
number
Example401
error
string
ExampleUnauthorized
message
string
403

Forbidden

object

Forbidden

status
number
Example403
error
string
ExampleForbidden
message
string #deprecatedtemplate# #additional-property-template#
OneOf
string
string
object
object
415

Unsupported Media Type — Content-Type must be application/json

Expand All
id
string
ExampleUNIQUEREFERENCE111000
timestamp
string
Example2020-03-09T22:18:26.625Z
eventType
string
Valid values[ "user_joined", "user_updated", "user_suspended" ]
message
object

The request could not be processed due to a validation error

status
number
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleThe startDate must be in a valid ISO 8601 format
422

Unprocessable Content — the submission was rejected before any job was created. This happens when the batch array is missing or empty, contains more than 100 entries, or no entry in it passed validation; and when the Idempotency-Key header is absent.

A batch in which only some entries fail validation returns 207 instead, so that you can see which ones to correct.

object

The request could not be processed due to a validation error

status
number
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleThe startDate must be in a valid ISO 8601 format
default

An unexpected error has occurred

When any default error occurs it may be a system failure and persistent errors may require support.

Expand All
id
string
ExampleUNIQUEREFERENCE111000
timestamp
string
Example2020-03-09T22:18:26.625Z
eventType
string
Valid values[ "user_joined", "user_updated", "user_suspended" ]
message
object

The server is unable to process the request

status
number
Example500
error
string
ExampleInternal Server Error