Publish a content record

Prev Next
Post
/contents/{id}/publish

Publishes a content record, making it visible in the catalogue.

The content must be ready: every item has completed processing and the content has a thumbnail. Publishing anything else returns 409 with a message that says why — not ready yet, already published, or archived. Both a draft and a ready record read as created on GET /rest/v1/contents/{id}, so use the per-item status there to tell them apart: the content is ready once every item is ok and a thumbnail is set. The usual flow is: create with thumbnail and any upload items, PUT the image and the files to the signed URLs, poll the read until every item is ok, then publish.

This is one of three ways to publish. publishWhenReady on create publishes automatically; this endpoint publishes on demand; the authoring UI publishes manually. A fully API-driven integration never has to touch the UI, and a UI-first one is never forced to auto-publish.

authorRef is required and is recorded as the publisher. It is your organisation's reference for the acting user, who must exist and be active. Nothing is published until it resolves. collaborationId is not accepted: ownership is set on create and a publish never changes it, so the field could change nothing.

Skills and topics. The content is published with the skills and topics it was created with, unless the request sends a list. A sent skills list replaces the stored skills and keeps the stored topics; a sent topics list replaces the stored topics and keeps the stored skills. Send an empty list to publish without that group. Every sent skill must already exist in your organisation; a topic is a free name. A name cannot appear in both lists. This endpoint and create with publishWhenReady are the only two places skills and topics can be set.

Backdating. An optional publishedAt records an earlier publish date, for content that was originally published elsewhere. It cannot be in the future. The date is applied by a second write after the publish, and that second write is not atomic with the first: if it fails, the content is live with the publish time of the call, the failure is logged, and the response is still 200. The response then carries publishedAtApplied: false, which is the signal to read for this case; the backdate sets createdAt and contentBodyUpdatedAt, while contentHistory keeps the time of the call. There is no retry, and a repeat call answers 409 because the content is already published, so a date that was not applied cannot be repaired through this API today.

OAuth2 (Cognito) tokens need the api/write or api/all scope. API tokens are not scoped.

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.
Path parameters
id
stringRequired

Unique identifier of the content item

Pattern^[0-9a-fA-F]{24}$
Example5f9d88d6e1b2a34c56d78902
Body parameters
publishContent

Publish with a backdated publish date

Publishes content that has reached ready, replacing the skills and topics it was created with and recording the date it was originally published elsewhere. Omit skills and topics to publish with the lists the content was created with.

{
  "authorRef": "UID30084022",
  "skills": [
    "leadership"
  ],
  "topics": [
    "onboarding"
  ],
  "publishedAt": "2024-03-01T00:00:00.000Z"
}
object

Who is publishing, the skills and topics to publish with, and an optional earlier publish date. authorRef is validated in your organisation before anything is published. collaborationId is not accepted: ownership is set on create and a publish never changes it, so the field could change nothing.

authorRef
string Required

Your organisation's reference for the acting user. The user is recorded as the publisher. The user must be active. A new, suspended, expired or deleted user is rejected.

ExampleUID30084022
skills
Array of string

The complete list of skills to publish with. Sending this field replaces the skills the content was created with and keeps its topics; omitting it keeps both. Each name must be an existing skill in your organisation, or the request is rejected with 422. An empty list publishes with no skills.

string
Min length1
Max length255
Exampleleadership
topics
Array of string

The complete list of topics to publish with. Sending this field replaces the topics the content was created with and keeps its skills; omitting it keeps both. Free names; a topic is not checked against your organisation's tags. An empty list publishes with no topics. A name cannot appear in both lists.

string
Min length1
Max length255
Exampleonboarding
publishedAt
string (date-time)

The publish date to record, for content that was originally published elsewhere. ISO-8601, not in the future. Applied by a second write after the publish; see the endpoint description for the failure mode. Defaults to the time of the call.

Example2024-03-01T00:00:00.000Z
Responses
200

The published content. tags, skills and topics carry the list the content was published with.

published

Published with a backdated publish date

Every item has reached ok and the content is now live in the catalogue. The backdate set createdAt to the date sent, while the contentHistory entries keep the time of the call.

{
  "id": "5f9d88d6e1b2a34c56d78902",
  "title": "Onboarding pack",
  "tags": [
    "leadership",
    "onboarding"
  ],
  "skills": [
    "leadership"
  ],
  "topics": [
    "onboarding"
  ],
  "type": "mixed",
  "createdAt": "2024-03-01T00:00:00.000Z",
  "updatedAt": "2026-09-11T10:00:00.000Z",
  "author": "5c69be0e3a38412f022b4719",
  "isOfficial": false,
  "duration": {
    "value": 15,
    "unit": "minutes"
  },
  "contentHistory": [
    {
      "action": "created",
      "timestamp": "2026-09-11T10:00:00.000Z",
      "performedBy": {
        "type": "user",
        "value": "5c69be0e3a38412f022b4719"
      }
    },
    {
      "action": "published",
      "timestamp": "2026-09-11T10:00:00.000Z",
      "performedBy": {
        "type": "user",
        "value": "5c69be0e3a38412f022b4719"
      }
    }
  ],
  "lastUpdatedBy": {
    "id": "5c69be0e3a38412f022b4719",
    "email": "author@example.com",
    "reference": "UID30084022"
  },
  "provider": {
    "id": "thrive",
    "name": "Thrive"
  },
  "status": "published",
  "items": [
    {
      "id": "5c8936159ec2d00010cdd334",
      "type": "elearning",
      "status": "ok"
    },
    {
      "id": "5c8936159ec2d00010cdd335",
      "type": "rich_text",
      "status": "ok"
    }
  ]
}
Expand All
object

The published content, in the same shape as GET /contents/{id}. In the rare case that the content was published but could not be read back, the response is the minimal form: id and status only. The content is published in both cases — do not retry the request; a retry answers 409. A full body also satisfies the minimal shape, so this is anyOf. Both forms carry publishedAtApplied when the request sent publishedAt.

publishedAtApplied
boolean

Whether the publishedAt you sent was written. Present only when the request carried the field. false says the content is published but keeps the publish time of the call: the second write failed, it is not retried, and a repeat of this request answers 409.

Exampletrue
AnyOf
Content
object (Content)
id
string

Unique identifier for the content

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

Title of the content

Max length255
ExampleAn amazing video
description
string

Detailed description of the content, may contain HTML

Max length65536
ExampleThis content looked really interesting and relevant to recent conversations in the development team
tags
Array of string

List of tags associated with this content. Omitted when the tag source cannot be read, so an absent field means "tags unavailable" rather than "no tags".

string
Max length255
Examplelearning
skills
Array of string

The tags entries that the taxonomy marks as a skill. Additive: every name is also in tags. A tag the taxonomy does not hold is in neither skills nor topics. Both fields are absent when the taxonomy cannot be read.

string
Max length255
Exampleleadership
topics
Array of string

The tags entries that the taxonomy marks as a topic. Additive: every name is also in tags. A tag the taxonomy does not hold is in neither topics nor skills. Both fields are absent when the taxonomy cannot be read.

string
Max length255
Exampleonboarding
type
string

The kind of artifact associated with this content record.

Courses are represented as pathway. Pathways will be served by a dedicated Pathways API in a future release and will stop appearing on this endpoint at that point, so treat pathway as temporary here.

Valid values[ "article", "assessment", "broadcast", "cmi5", "elearning", "event", "file", "mixed", "pathway", "question", "quiz", "scorm", "url", "video" ]
Max length24
Examplevideo
createdAt
string (date-time)

Timestamp when the content was created

Example2019-03-13T16:15:24.000Z
updatedAt
string (date-time)

Timestamp when the content was last updated

Example2019-03-13T16:15:24.000Z
author
string | null

User ID who authored this content. Nullable for auto-curated content.

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

Whether the content has been recognized for its importance or validity by the customer organization

Exampletrue
cpdCategoryId
string | null

The CPD category the content is recorded against, or null when it has none. Present when a record is read by its id or returned by a v2 create or update, for content held by the content source that carries CPD data. The field is not present in the results of the list endpoint, nor on a record that exists only in the legacy content source.

Pattern^[0-9a-fA-F]{24}$
Example507f1f77bcf86cd799439012
duration
object | null

The expected time to complete the content. Null for Event content types. For SAP-imported content, this value may reflect a system default rather than a precise measured duration.

value
integer | null

Duration in the specified unit. Null when no duration is available (e.g. pathways).

Example15
unit
string

The unit of the duration value. Always 'minutes'.

Valid values[ "minutes" ]
Exampleminutes
contentHistory
Array of object (ContentHistoryEntry)

Chronological history of actions on this content (from content-service). May be empty when there is no recorded history.

object
action
string

Type of change or event recorded for the content.

timestamp
string (date-time)

When the action occurred.

performedBy
object (ContentHistoryPerformedBy)
type
string

Kind of actor (for example user or system).

value
string

Identifier or value associated with the actor.

lastUpdatedBy
object | null

Last actor who updated the content, derived from history (or author fallback). Null when neither history nor author resolves.

id
string | null

User ID or service identifier.

email
string | null

Email from the user view when available.

reference
string | null

External reference (e.g. HR id) when stored on the user view.

provider
object

The content provider. Defaults to Thrive for natively created content.

id
string

Provider identifier key.

Examplethrive
name
string

Human-readable provider display name.

ExampleThrive
status
string

Lifecycle status of the content. A legacy (content-service) record derives the value: archived content reports archived, content with a publish time reports published, and all other content reports created. A content-core record maps its lifecycle status onto the same values, where draft and ready both report created. deleted is a filter value only and is never returned: a legacy record returned by a deleted status filter reports its pre-deletion state, and a deleted content-core record is physically gone.

Valid values[ "created", "published", "archived" ]
Examplepublished
items
Array of object (ContentItemState)

The items that make up a content record, with per-item processing state. Only GET /contents/{id} returns this field, and only for records served from content-core; the GET /contents list never includes it.

object

One item within a content record, with its processing state.

id
string

Unique identifier for the item

Example5c8936159ec2d00010cdd334
type
string

The item type

Exampleelearning
rank
integer

The item's position within the content, lowest first

Example0
summary
string

A short label for the item, shown to learners

ExampleWelcome
status
string

The item's processing state. An item must reach ok before the content can become ready.

Valid values[ "processing", "ok", "error" ]
Exampleprocessing
error
string | null

Why processing failed, when status is error

Exampleopenapi-json-null-sentinel-value-2BF93600-0FE4-4250-987A-E5DDB203E464
ContentPublishMinimalResponse
object (ContentPublishMinimalResponse)
id
string

Unique identifier for the content

Example5f9d88d6e1b2a34c56d78902
status
string
Valid values[ "created", "published", "archived" ]
Examplepublished
400

Bad Request — a name appears in both skills and topics, or the upstream content source rejected the payload (see violations when present)

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
401

Unauthorized

object

Unauthorized

status
number
Example401
error
string
ExampleUnauthorized
message
string
403

Forbidden — the OAuth2 token does not carry the api/write or api/all scope

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
404

Not Found — no content with this id. Only content created through POST /contents or the authoring tool can be published here; legacy content of other types is not addressable by this endpoint.

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
409

Conflict — the content cannot be published. The message says why: it is not ready yet (an item is still processing or failed, or there is no thumbnail), it is already published, or it is archived. Nothing is changed.

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
415

Unsupported Media Type — Content-Type must be application/json. A body is required, because authorRef has no other transport.

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
422

Unprocessable Content — a field failed validation (see violations, for example a publishedAt in the future), authorRef does not match an active user in this organisation, or a skill is not an existing skill in this organisation. Nothing is published.

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
429

Too Many Requests — the upstream content source is rate limiting this organisation. Retry after the Retry-After header when it is present.

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
500

Internal Server Error — the request failed inside the API service before the publish was sent. The content was not published.

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
502

Bad Gateway. Read the message to learn what the state of the content is. When the message says the skills could not be verified, that the content could not be read, that the stored tags could not be classified, or that the tags could not be saved, the work ran before the publish and the content was not published. When the message names an upstream content source failure, the publish had already started: the upstream service sets the status before it announces the change, so the content may be live. Read it back before you retry — a retry answers 409 when the publish did land.

Expand All
object

The error envelope of the V2 endpoints.

error
object
status
integer
Example422
error
string
ExampleUnprocessable Entity
message
string
ExampleValidation failed
violations
Array of object (V2Violation)

Present on field validation failures. One entry per failed rule.

object

One failed validation rule.

reference
string

The dotted path of the field that failed, for example items.0.meta.body. Empty for a body-level rule.

Exampletitle
message
string

Why the field failed.

Example"title" length must be at least 3 characters long
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