Update content records in bulk

Prev Next
Patch
/contents/bulk

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

Submits an asynchronous job that updates up to 100 content records. Each entry takes the same body as PATCH /contents/{id} plus the id of the record it targets.

The endpoint responds immediately with 202 Accepted and a jobId; poll GET /contents/bulk/{jobId} for progress and per-entry results. Entries that add upload items, or that carry thumbnail, return fresh presigned URLs in their result.

Batch validation, the Idempotency-Key requirement and the 207 and 422 behaviour are identical to POST /contents/bulk.

The single update's caveats apply per entry, and matter more at this scale: updates are last-write-wins, and incrementVersion permanently deletes the assets it replaces.

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
bulkUpdateContent

Update two records in one submission

Each entry is the body PATCH /contents/{id} takes, plus the id of the record it targets.

{
  "contents": [
    {
      "id": "5f9d88d6e1b2a34c56d78902",
      "authorRef": "UID30084022",
      "title": "Fire safety (2026)"
    },
    {
      "id": "5f9d88d6e1b2a34c56d78903",
      "authorRef": "UID30084022",
      "removeItems": [
        "5c8936159ec2d00010cdd334"
      ]
    }
  ]
}
Expand All
object

The changes to apply, one entry per content record.

contents
Array of object (ContentBulkUpdateEntry) Required

The changes to apply, at most 100 per submission. Each entry is the body of PATCH /contents/{id} plus the id of the record it targets, so each entry carries its own authorRef and each entry is subject to the same per-request item cap.

Min items1
Max items100
object

One entry of a bulk update: the body PATCH /contents/{id} takes, plus the id of the record the entry targets. The change fields repeat ContentUpdateBody because that schema is closed, and a closed schema cannot take id through an allOf — the closed branch rejects it. The two must stay in step; a spec test fails if they do not.

id
string Required

The id of the content record to update

Pattern^[0-9a-fA-F]{24}$
Example5f9d88d6e1b2a34c56d78902
title
string
Min length3
Max length150
ExampleOnboarding pack (2026)
authorRef
string Required

Your organisation's reference for the acting user. The update is recorded against this user. A suspended or deleted user is rejected.

Min length1
ExampleUID30084022
canComment
boolean

Whether learners may comment on this content.

Exampletrue
isOfficial
boolean

Whether the content is endorsed by your organisation. false leaves a stored CPD category as it is; send cpdCategoryId: null to clear it.

Exampletrue
cpdCategoryId
string | null

The CPD category to record this content against. null clears it. An id is only accepted when the content is endorsed after the update (400 otherwise), and it must exist in your organisation (422 otherwise).

Pattern^[0-9a-fA-F]{24}$
Example507f1f77bcf86cd799439012
completionType
string

How the content is completed.

Valid values[ "auto", "confirm", "external", "on_passing_quizzes", "on_passing_observer_completed_quizzes", "on_passing_assessments", "on_elearning_completion", "on_completing_interactive" ]
incrementVersion
boolean

Bump the content's version number. Valid only on published content (409 otherwise). This is not a snapshot — content keeps no version history, so any item replaced in this request has its previous file deleted permanently.

Defaultfalse
addItems
Array

Items to append. The per-request cap applies to this array only, not to the resulting document: at most one upload item plus one non-upload item. Each rank must be unique within this array (422 otherwise) and must not collide with the rank of an item already on the content (409 otherwise).

Max items100
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.

updateItems
Array

Existing items to change, identified by their item id. The type must match the stored item — an item cannot change type. Fields the change does not name keep their stored values. A rank that collides with the rank of another item that survives this request answers 409, the same as an added item's rank — ranks are checked as a set once the whole change is applied.

Max items100
OneOf
ContentUpdateRichTextItem
object (ContentUpdateRichTextItem)
id
string Required

The id of the item to change.

Pattern^[0-9a-fA-F]{24}$
Example5c8936159ec2d00010cdd334
type
string Required
Valid values[ "rich_text" ]
rank
integer

The display position of the item.

Minimum0.0
summary
string

A short description of the item.

meta
object
body
string

The rich text body. May contain HTML.

Min length1
Max length100000
ContentUpdateUrlItem
object (ContentUpdateUrlItem)
id
string Required

The id of the item to change.

Pattern^[0-9a-fA-F]{24}$
Example5c8936159ec2d00010cdd334
type
string Required
Valid values[ "url" ]
rank
integer
Minimum0.0
summary
string
meta
object
url
string

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

Examplehttps://example.com/guide
ContentUpdateElearningItem
object (ContentUpdateElearningItem)
id
string Required

The id of the item to change.

Pattern^[0-9a-fA-F]{24}$
Example5c8936159ec2d00010cdd334
type
string Required
Valid values[ "elearning" ]
rank
integer
Minimum0.0
summary
string
meta
object
name
string

The replacement package file name, for example course.zip. Send it with uploadId. The two fields go together and either one on its own is rejected with a 400: a name has no effect without a replacement package.

Examplecourse.zip
uploadId
string (uuid)

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

canDownload
boolean

Whether learners can download the file.

ContentUpdateFileItem
object (ContentUpdateFileItem)
id
string Required

The id of the item to change.

Pattern^[0-9a-fA-F]{24}$
Example5c8936159ec2d00010cdd334
type
string Required

The stored item's kind. The file extension in meta.name must match it.

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

The replacement file name, for example intro.mp4 or cover.png. Send it with uploadId. The two fields go together and either one on its own is rejected with a 400: a name has no effect without a replacement file.

Exampleintro.mp4
uploadId
string (uuid)

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

canDownload
boolean

Whether learners can download the file.

removeItems
Array of string

The ids of items to delete. Removing an upload item deletes its stored file permanently. An id that does not match an item on this content is rejected with 422.

Max items100
string
Pattern^[0-9a-fA-F]{24}$
Example5c8936159ec2d00010cdd334
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
Responses
202

Accepted — a bulk update 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