Skip to content

Managing content

These endpoints look after what is already in your library: renaming a resource or changing its summary, editing a document’s text, saving a video’s or audio recording’s transcript, publishing or discarding a draft, rolling a recording back to an earlier version, changing who in your organization can read a resource, tagging it, and adding it to a team. The reads that go with them are under Content. Adding new material is under Creating documents and YouTube videos and the upload pages.

They need a token granted the content:manage scope, which is an OAuth path today. It can be granted by an organization admin or a team admin. The person behind the token must also be allowed to do the act in Mereon, checked on every call, so a demotion takes effect on the next one.

These endpoints do not delete a resource or share one outside your organization. Deleting is covered under Deleting content.

A resource is named by the id GET /v1/content returns, whatever its type. It has to be one you may edit, as in Mereon: an organization admin can change any resource, and a team admin the resources shared with the teams they administer, including ones not published yet. One you can see but not edit is a 403; one you can neither see nor edit is a 404.

Most of these return the resource as it now stands, the same shape GET /v1/content/{id} reads, so you do not have to read it back.

PATCH /v1/content/{id}

{ "title": "Forklift safety (2026)", "summary": "…" }
Field Type Notes
title string One line, up to 255 characters.
summary string or null Up to 5,000 characters. null clears a video’s or audio’s summary; a guide’s cannot be cleared.

For a video, audio or guide. Only what is given changes, and at least one is needed. A guide shows the change at once and is left with a draft, as in the guide editor: publish it to have the guide indexed again. A document’s title is part of its draft, so a document is a 400 here: use the draft below.

Editing a document saves a draft. Learners keep reading the published document until the draft is published, which is when it is indexed again and the people already trained on it can be retrained.

GET /v1/content/{id}/draft

{
"contentId": "…",
"hasDraft": true,
"title": "Returns policy",
"body": "# Returns\n\nWithin 30 days of purchase…"
}

The title and text a draft edit replaces: the document’s draft when it has one, and otherwise what a new draft starts from, its title and published text. hasDraft says which. Read this before sending a new body. The published text from GET /v1/content/{id}/text leaves out what a draft already changed, so a body rebuilt from it loses those edits. Only for someone who may edit the document.

PUT /v1/content/{id}/draft

{ "title": "Returns policy", "body": "# Returns\n\nWithin 30 days of purchase…" }
Field Type Notes
title string One line, up to 255 characters. The title the document takes when the draft is published.
body string The whole text, in markdown, up to 500,000 characters. It replaces the draft’s text, so read what it replaces with GET /v1/content/{id}/draft first and send everything that should stay.

At least one is needed. A document with no draft gets one. A document kept in step with another system, such as Confluence, cannot be edited and is a 403.

{
"content": { "id": "…", "title": "Returns policy", "…": "…" },
"draft": { "title": "Returns policy", "retrainingIntentRequired": true }
}

content is the document as learners read it, unchanged until the draft is published. draft.retrainingIntentRequired says whether publishing it needs a retrainingIntent: it does when the document has been published before and the draft changes its text.

PUT /v1/content/{id}/transcript

{
"title": "Safety talk",
"segments": [{ "text": "Check the valve.", "startMs": 0, "endMs": 2400 }]
}
Field Type Notes
title string One line, up to 255 characters. The title the recording takes when the draft is published. Omitted, an existing draft keeps its title.
segments object[] The whole transcript, in order, at least one segment. Each is { text, startMs, endMs }. startMs and endMs are whole milliseconds from the start of the recording, zero or greater, and endMs is not before startMs. The same fields GET /v1/content/{id}/transcript returns.

segments replaces the whole transcript. Read it first with GET /v1/content/{id}/transcript, and when a draft already exists pass that version’s id, because the published transcript leaves out what the draft already changed. Send every segment that should stay. Learners keep the published recording until the draft is published.

{
"content": { "id": "…", "title": "Safety talk", "…": "…" },
"draft": { "title": "Safety talk", "retrainingIntentRequired": true }
}

content is the recording as learners have it, unchanged until the draft is published. draft.retrainingIntentRequired is true when the recording has been published before, because publishing a transcript indexes it again. A transcript with no words cannot be published: that publish is refused before the recording’s status changes.

POST /v1/content/{id}/publish

{ "retrainingIntent": "changed_material" }

Makes a document’s, guide’s, video’s or audio recording’s draft the version learners read. A guide, a video, an audio recording, and a document whose text changed are indexed again, which takes a few minutes; the resource’s processingStatus shows it.

retrainingIntent decides what happens to the people already trained on it:

Value What happens
none Nothing. Use it for a fix that changes nothing anyone has to know.
full_retake They take the lesson again.
changed_material They get a short lesson on what changed.

It is required whenever the publish can retrain anyone: for a guide, a video or an audio recording, and for a document whose text changed, once it has been published before. Leaving it out is a 400 rather than a guess, since there is no safe default for someone else’s training. A document whose draft only renames it is renamed in place and retrains nobody, and a resource that has never been published has trained nobody, so neither needs it. Publishing with no draft is a 400.

DELETE /v1/content/{id}/draft

Throws away a document’s, guide’s, video’s or audio recording’s draft, leaving the published version as it is. For a video or an audio recording, the transcript goes back to the published chapters. Returns the resource. A resource with no draft, or one that has never been published and so has nothing to go back to, is a 400, and nothing changes.

POST /v1/content/{id}/rollback/{versionId}

Makes an earlier published version of a video or an audio recording the one learners read, and throws away its draft. versionId is a published version of this resource from GET /v1/content/{id}/versions. Returns the resource.

The transcript saved on the recording stays as it is. Save a transcript afterwards when it should match the version learners now read. A document or a guide uses the discard above for its draft.

PUT /v1/content/{id}/sharing

{ "visibility": "teams", "teamIds": ["…"] }
Field Type Notes
visibility string Required. Who in your organization can read it: everyone, teams or admins.
teamIds string[] With teams only. Up to 50, from GET /v1/teams.

Who can read the resource from now on, replacing who could before. Only an organization admin may share with everyone or make it admins only. A team admin names teams they administer, and changes only those: other teams the resource is shared with are left as they are. Sending teamIds with everyone or admins, or teams without any, is a 400.

Tags are named, never numbered: the same names GET /v1/tags lists and each resource’s tags carry. A name is stored lowercase, with spaces and punctuation turned into hyphens and anything other than the letters a to z, digits, hyphens and underscores dropped. So Fire Safety is the tag fire-safety, and accents are dropped too: Sécurité becomes scurit, so leave them out. A name in a path is read the same way.

PUT /v1/content/{id}/tags

{ "tags": ["returns", "customer-service"] }

The tags the resource carries from now on, replacing the ones it had; an empty list clears them. Up to 50. Each has to be one of your organization’s tags: a name it does not have is a 400. Returns the resource. To tag a lesson, see Authoring lessons.

POST /v1/tags

{ "name": "Fire Safety" }
{ "name": "fire-safety" }

Up to 40 characters once stored. A name your organization already has is a 409.

PATCH /v1/tags/{name}

{ "name": "fire-prevention" }

Renames the tag everywhere it is used. A new name that is already a tag is a 409. Needs an organization admin.

DELETE /v1/tags/{name}

Deletes the tag, which takes it off every resource and lesson that carries it. Returns the tag that was deleted. Needs an organization admin.

POST /v1/teams/{id}/content

{ "contentIds": ["…", "…"] }

Adds resources to what a team holds, alongside what it already has, so everyone on the team can read them. Up to 50, each a video, document, guide or audio you can open: anything else is a 404 naming the ids it could not use. Needs an organization admin. The response names every resource you sent, all of which the team now holds; it may have held some of them already.

{ "teamId": "…", "contentIds": ["…", "…"] }

DELETE /v1/teams/{id}/content/{contentId}

Takes a resource off what a team holds. People on the team can still read it if it is shared with everyone or with another team they are on. A resource the team does not hold is a 404. Needs an organization admin.

Status When
400 The body is invalid, teamIds does not match visibility, a document was sent to the title route, a guide’s summary was cleared, a type has no draft, there is no draft to publish or discard, a discard has no published version to go back to, a publish that can retrain people has no retrainingIntent, a transcript has no words or a segment ends before it starts, or a tag name is not one your organization has.
403 The token lacks content:manage, the person may not do this act, may not change this resource or team, or may not share with the audience asked, or the document is kept in step with another system.
404 The resource, team or tag is not one you can see.
409 A tag by that name already exists.
429 Over the rate limit. Retry-After says when to try again.

Everything but publishing is limited to 30 calls a minute and 300 an hour per connection. The MCP tools that do the same acts are held to the same numbers, counted per tool.