Skip to main content

Changelog

v1.89.0 - Add any part of the transcript to a clip​

  • New add and remove on PATCH /v1/remixes/:id/moments/:momentId: bring a span of transcript into a clip from anywhere in the webinar, or take one out. Adding text outside the clip skips what sits in between. See Update a moment. MCP: update_remix_moment.
  • Retrimming keeps your cuts. A new start or end no longer works out the cuts again for the whole clip: cuts already in it stay, and only the newly covered part gets new ones.

v1.88.0 - 16:9 clips for YouTube​

  • New clipStyle.ratio value 16:9 on PATCH /v1/remixes/:id: clips made 1920x1080, ready for YouTube. They show the whole webinar frame; set a moment's framing to crop in. See Style. MCP: update_remix.

v1.87.0 - Name a recording when you start it​

  • New name on POST /v1/recordings: give the recording its display name up front instead of renaming it after. See Start a recording. MCP: start_recording.

v1.86.1 - Sharper speakers in split clips​

  • A small camera gets a shorter top part in split clips (down to 30% of the clip), and the speaker is lightly sharpened, so a small thumbnail isn't blown up into a blur. A tiny camera shows as a card over the screen instead. Clips made before show as changed.

v1.86.0 - Split-screen clips​

  • New framing mode stack on PATCH /v1/remixes/:id/moments/:momentId: top and bottom boxes, each filling half the clip. See Update a moment. MCP: update_remix_moment.
  • Clips made automatically now split like Reels: the speaker on top, the shared screen below, captions on the line between. Two or three people on a call stack. Clips made before keep their look until you make them again; they show as changed.

v1.85.0 - Articles in one language​

  • New sourceLocale on POST /v1/knowledge-base/articles and POST /v1/knowledge-base/articles/from-video: write an article in any enabled locale, not only the knowledge base's main one. It is shown only to readers in that language until it is translated. The language is fixed once created; a different sourceLocale on PATCH returns 400. See Articles in one language. MCP: create_kb_article, kb_article_from_video.
  • Articles return sourceLocale, and GET /v1/knowledge-base/articles filters by it. MCP: list_kb_articles, get_kb_article.
  • POST /v1/knowledge-base/articles/:id/translate translates from the article's own language, so the main language can be a target. MCP: translate_kb_article.

v1.84.0 - Larger glossaries​

  • Glossary cap raised from 200 to 10,000 entries on PUT /v1/brand-kit/glossary and POST /v1/brand-kit/glossary/import (CSV up to 10 MB). Translation now uses only the terms that appear in the text being translated. See Glossary. MCP: set_glossary, import_glossary.

v1.83.0 - Webinar remixes​

  • New plan on POST /v1/remixes: makes a webinar remix. Clips of the real speaker, a 16:9 recap and posts, from up to 90 minutes of source. range picks the part to use, and only that part is billed. See Webinar remixes. MCP: create_remix.
  • New PATCH /v1/remixes/:id/moments/:momentId and POST /v1/remixes/:id/moments: keep, drop, rename and retrim moments, or add your own. MCP: update_remix_moment, add_remix_moment.
  • New PUT /v1/remixes/:id/recap: the recap's order, with title cards. MCP: set_remix_recap.
  • New outputType values clips and recap on POST /v1/remixes/:id/export: make the kept clips (or momentIds) and the 16:9 recap. Anything already made from the same edits is skipped and free; credits are charged when each video is ready. See Clips and the recap. MCP: export_remix.
  • New clipStyle and recapStyle on PATCH /v1/remixes/:id: shape, captions, headline, closing card and logo. MCP: update_remix. Moments take a hookText, and a framing to frame a clip by hand (a crop, a shared-screen window with the camera as a card, or the whole frame). MCP: update_remix_moment. Made clips' export rows carry the shots they were framed with.
  • GET /v1/remixes/:id now returns pipelineVersion, plan, recap, clipStyle, recapStyle and analysisProgress. get_remix returns them too, along with analysis. Export rows add progress, durationSec and creditsCharged (and shots on clips).
  • Remix credits: analysis is 3 credits per source minute. POST /v1/remixes/:id/regenerate now costs 1 credit per run (it was free) and returns 402 when the balance is short.

v1.82.0 - Module builds priced by video length​

  • doc_video_generate is now 50 credits per minute of finished video (was 5 credits per build), the same rate as assembly guides. A 3-minute module costs 150. The build checks the module's estimated length before it starts and is charged on the length it comes out. Applies to POST /v1/courses/:id/generate, POST /v1/courses/:id/build and POST /v1/videos/from-document.

v1.81.0 - Longer recording prompts​

  • POST /v1/recordings: prompt now takes up to 50,000 characters, up from 5,000, so a full training script fits. See AI Recorder.

v1.80.0 - Video from a document, one-step module builds​

  • New POST /v1/videos/from-document: one finished video from a document, unattended, with optional pace and aiVideo. See Create a video from a document. MCP: create_video_from_document.
  • New POST /v1/courses/:id/build: a module's video in one call (storyboard when needed, AI visuals, narration, slides), returning the videoId at once. See Build a module in one step. MCP: build_course_module.
  • Fixed POST /v1/courses: courses created through the API now read the document (or the video's narration) before planning. Before, planning failed with "No extracted pages to plan from."

v1.79.0 - Course pace and AI video​

  • New pace field on courses: snackable, course (default) or inDepth. It sets module length, scene pace, the narration's voice and, for snackable, big captions. Set it on POST /v1/courses or PATCH /v1/courses/:id; GET /v1/courses/:id returns it. See Pace.
  • New aiVideo field on courses: { enabled, stylePresetId } replaces stock photos with AI-generated pictures and a few short looping clips. Business and Enterprise only; other plans get 403 limit_exceeded. Generate makes the pictures, charged as ai_footage_still and ai_footage_clip, and checks the most a module can cost before it starts. See AI video. MCP: create_course, update_course, get_course.
  • ai_footage_clip now costs 16 credits (was 40), for courses and Studio alike.
  • Exported videos with AI pictures now carry the AI footage marking in their metadata.

v1.78.0 - Opening slide duration​

  • New openingSlideDuration field on the Brand Kit: how long the title slide at the start of a new video is held, 2-60 seconds (default 5). Returned by GET /v1/brand-kit, set with PATCH /v1/brand-kit (null resets it). See Opening slide. MCP: update_brand_kit.

v1.77.0 - Account context on saved logins​

  • New context field on saved logins: who the login is and what it can see and do, for example "Teacher account: manages classes, assigns lessons, sees grades". Every item of GET /v1/recorder-sessions has it (null when unset), and PATCH /v1/recorder-sessions/:session sets it (up to 2000 characters, null clears). It is never performed as steps: Pulse uses it to write docs for the right audience. See Account context. MCP: list_recorder_sessions, update_recorder_session.

v1.76.0 - Company sign-in for knowledge base readers​

  • New access.authMethod value oidc on PATCH /v1/knowledge-base: readers of a gated knowledge base sign in through your own identity provider (OpenID Connect). Enterprise plan; we set up the connection first, and until then oidc returns 400. GET /v1/knowledge-base can now return oidc in access.authMethod. See Company sign-in. MCP: update_knowledge_base.

v1.75.0 - Module translation uses credits​

  • POST /v1/modules/:id/translate now charges credits: module_translate per module per target language, on success only. If the balance can't cover every target, nothing is translated and the call returns 403 limit_exceeded. Listed in GET /v1/usage/credits. A module translated as part of a video translation isn't charged again. See Translation. MCP: translate_module.

v1.74.0 - Translation pricing: knowledge base articles and slide videos​

  • POST /v1/knowledge-base/articles/:id/translate now charges credits from the workspace credit balance: kb_article_translate per article per target language, on success only. If the balance can't cover every target, nothing is translated and the call returns 403 limit_exceeded. The price is listed in GET /v1/usage/credits. See Translation. MCP: translate_kb_article.
  • No limit on locales. A knowledge base can enable as many locales as you need. The dashboard no longer stops at 3.
  • Video translation is now 10 credits per minute per target language (was 8). See GET /v1/usage/credits.
  • A video made of slides is now priced on its full length when translated, including 3D assembly and assembly-guide videos. It used to be priced on its 1.5-second carrier clip, which billed 1 credit however long the slides ran.

v1.73.0 - Several saved logins per site​

  • A site can now have several saved sessions, for example "Sandbox" and "Admin user". Every item of GET /v1/recorder-sessions has new fields id, name, isDefault and createdBy. One session per site is the default. See Saved logins. MCP: list_recorder_sessions.
  • New sessionId field on POST /v1/recordings picks the session to start signed in with. Leave it out to use the site's default. See Pick a session. MCP: start_recording.
  • PATCH and DELETE /v1/recorder-sessions/:session take a session id. A domain still works and means that site's default session. PATCH has new fields name and isDefault. See Rename it, or make it the default. MCP: update_recorder_session.
  • DELETE by domain returns 409 when the site has more than one session, and lists them. Delete by id instead. The response now includes id. Deleting the default makes the most recently used remaining session the new default. See Delete a session. MCP: delete_recorder_session.

v1.72.0 - AI footage is for paid plans only​

  • Trials can no longer generate AI footage. Every /v1/ai-media generate endpoint (shot, stills, clips, scenes, scene translation) now returns 402 on a trial, as it already did on the free plan. Listing and fetching past generations still works. See AI footage.

v1.71.0 - Faster AI clips​

  • AI clips now run on HeyGen Video 1.0 (model: "heygen-video-1") instead of Veo 3.1 Lite. A clip usually finishes in under a minute and follows the motion in the shot more closely. Same request, same durationSec and resolution values, same credits. See Animate a still.
  • Clips carry generated ambient sound (no music or speech) and can run slightly past durationSec. Studio places them muted and cut to length; do the same on the timeline API.
  • At 720p the frame follows the still (1344×768 for a 16:9 still).
  • Scene items carry anchor (the narration words where each is spoken) when you send narration. Studio uses them to reveal each step as it is said; synced scenes carry revealAt. See Animated scenes.
  • New POST /v1/ai-media/scenes/translate translates a scene's on-screen text, keeping layout, numbers, colors and timing. Charged on success. MCP: translate_ai_scene.
  • Scenes show your Brand Kit logo (theme.logoUrl) unless showLogo is false.
  • Refine a shot: POST /v1/ai-media/shot accepts current and change and returns the same shot with only that changed. MCP: plan_ai_shot.

v1.69.0 - Animated scenes​

  • New POST /v1/ai-media/scenes designs a short motion-graphics scene in your Brand Kit colors and font from a description or narration: a process flow, checklist, key number, comparison, key points or timeline. Place it on a Studio timeline as a clip of type: "scene"; its text, colors and font stay editable. See Animated scenes. Charged on success. MCP: create_ai_scene.
  • New route field on POST /v1/ai-media/shot: footage, animation or screen, the tool that fits the request.
  • Studio timelines accept clips of type: "scene" in PUT /v1/studio/projects/:id/timeline.

v1.68.0 - AI footage​

  • New AI footage endpoints generate B-roll that matches your narration, stills first: POST /v1/ai-media/shot plans a shot and warns when AI video is the wrong tool (free), POST /v1/ai-media/stills generates 1-4 stills, and POST /v1/ai-media/clips animates one into a silent 4, 6 or 8 second clip (poll GET /v1/ai-media/:id). GET /v1/ai-media lists them. Twelve style presets, including illustrated ones for kids and teens, and a styleNote for your own look. Stills and clips are charged on success only; see Credit costs. Scopes: studio:read, studio:write. MCP: plan_ai_shot, generate_ai_stills, animate_ai_still, list_ai_media, get_ai_media.
  • Studio exports that use AI footage are marked as containing AI-generated content in the MP4's metadata.

v1.67.0 - Video Intro Maker​

  • New tool video-intro-maker: turns a logo into an animated 4, 6 or 8 second MP4 intro in 16:9, 9:16 or 1:1, in one of 11 styles. Run it with Execute a tool; GET /v1/tools/video-intro-maker lists every parameter. Each run is charged once, as a flat per-run tool charge. MCP: execute_tool.

v1.66.0 - Keep saved logins longer, cut page loads​

  • New ttlDays field on PATCH /v1/recorder-sessions/:domain: 14 (default), 30, 90, 180 or 365 days after the session was last used. setupPrompt is now optional on the same call, and a field you leave out stays as it is. See How long a session is kept. MCP: update_recorder_session.
  • New ttlDays field on every item of GET /v1/recorder-sessions.
  • New removeLoading option on POST /v1/recordings. When true, the waits while pages load are cut out of the video. Default false. See AI Recorder. MCP: start_recording.

v1.65.0 - Rename AI recordings​

  • New PATCH /v1/recordings/:jobId with {"name": "..."} sets a recording's display name, and null clears it. The dashboard's recordings list shows the name instead of the prompt. See AI Recorder. MCP: update_recording.
  • New name field on every recording response (null when unset). The prompt is unchanged.

v1.64.0 - Scripts checked against the screen​

  • Analysis now checks the script against the video. After Analyze writes the script, each line is checked against what is on screen during its window. An AI-written line that narrates the wrong moment is rewritten to describe its own window, with the same timing. A script you supplied keeps its words: a line may only be moved to the moment it describes.
  • New unverified field on subtitle lines in GET /v1/videos/:id/subtitles (and the MCP get_subtitles tool). It is only present, as true, on lines we could not confirm match the screen. Editing a line's text removes it. See Get Subtitles.
  • Scripts built from the video's own speech are not checked: their words are what was said at that moment.

v1.63.0 - Nine more languages​

GET /v1/languages now returns 83 languages, up from 74. Existing codes are unchanged, and the list comes from the same registry the product uses, so clients that read it need no change.

  • New codes - yue Cantonese, uz Uzbek, mn Mongolian, tg Tajik, mt Maltese, yo Yoruba, mi Māori, oc Occitan, ast Asturian. Accepted anywhere language is - Analyze, Translate, Voiceover and Update Video.
  • These nine are spoken by eleven_v4 only. A Voiceover request in one of them uses eleven_v4 even when you pin eleven_multilingual_v2 or eleven_v3, which can't speak them.
  • MCP: list_languages returns them with no change, because it reads the same endpoint. If you cached the list, refresh it.

v1.62.0 - Knowledge base redirects​

  • Redirects now forward readers. Redirects from POST /v1/knowledge-base/redirects (and the MCP create_kb_redirect tool) are now applied on your help site. Before, they were saved but readers still got a 404.
  • Renaming an article keeps its old link working. Changing slug in PATCH /v1/knowledge-base/articles/:id now adds a redirect from the old address.
  • Moved articles forward to their new section. Old links to an article moved to another section, or in a renamed section, now forward to its current address.

v1.61.0 - Eleven v4 for every language​

  • model: auto now uses eleven_v4 for every language in POST /v1/videos/:id/voiceover and the MCP generate_voiceover tool, English included. Pin eleven_multilingual_v2 to keep a video on the older model. speed and sentencePauseSeconds work on every model. See Voiceover.

v1.60.0 - Eleven v4 voiceover​

  • Languages the multilingual model can't speak now use eleven_v4 in POST /v1/videos/:id/voiceover (and the MCP generate_voiceover tool) with model: auto, the default. That covers Hebrew and 44 other languages, and translated voiceovers. Each line is now conditioned on its neighbours, so the voice stays steadier from line to line. eleven_v4 is also a new model value you can pin. eleven_v3 stays pinnable. See Voiceover.
  • New voices in GET /v1/voices (and the MCP list_voices tool): 11 native Hebrew voices and 6 English narrators. Voices made for one language carry a new language field. See Voices.
  • speed and sentencePauseSeconds now work in every language. Languages outside the multilingual model used to ignore both; Vidocu now applies them to the generated audio.
  • speed is now 0.7-1.2. The voice engine rejected anything above 1.2, so those requests failed. Values up to 1.3 are still accepted and spoken at 1.2.

v1.59.0 - Max length for AI recordings​

  • POST /v1/recordings takes maxDurationSec, and so does the MCP start_recording tool. The agent plans to fit that many seconds, the replay speeds up when the plan runs over, and the finished video is cut at the limit, so it also caps what the job is charged. 0 or absent means no limit, as before. See AI Recorder.
  • GET /v1/videos/:id/download returns downloadUrl and downloadUrlExpiresAt, and so does the MCP get_download_url tool. Fetch files from downloadUrl. In a workspace with private media it is a signed link that works for one hour. Everywhere else it is the same as url and never expires. See Download Files.
  • The export.completed webhook carries downloadUrls and downloadUrlsExpireAt, on the same terms, with links that work for 24 hours in a workspace with private media.
  • Docs correction: the export.completed payload was documented with the wrong field names. It sends projectId, videoUrl, srtUrl and voiceoverUrl, and always has. See Webhook events.
  • Saving a signed or view link stores the file's permanent address instead. If a write sends back a link Vidocu handed out, such as a downloadUrl, the file's permanent address is saved, so a saved link never stops working.

v1.57.2 - Deleting a video deletes its files​

  • DELETE /v1/videos/:id now removes the video's stored files, in the API, the app and the MCP delete_video tool alike: the source upload, exports, screenshots and voice clips. Files that something else in the workspace still uses stay, such as a duplicate of the video or a Knowledge Base article that shows its screenshots.
  • Fix: deleting a video, or a folder with cascade=true, used to delete the video's upload and exports outright, which broke any duplicate that shared them, and could delete brand-kit intros, outros and music that other videos use. Both now go through the same reference check.

v1.57.1 - Take one language down; each language names its own approvers​

  • Archiving a translated package withdraws its language from the Knowledge Base. The master and the other languages stay live. Unarchiving publishes it again; approving a new package in that language replaces it. An older approved version never comes back on its own. See Languages of an approved article.
  • Archiving a translated package no longer archives the training. The training is shared with the master and every other language, and archiving one language used to retire it for all of them. It is archived only with the last package that uses it.
  • A language page shows that language's own approvers and approval date, not the master's.
  • Docs correction: a language version is changed by translating the video again and approving the new package. Approving a duplicate of a translated package does not change the Knowledge Base.

v1.57.0 - An approved article's languages come from approved packages​

  • Approving a translated package publishes its language in the Knowledge Base. On an article published from an approved package, each language comes from the article of the translated package in that language, copied word for word, once that package is approved. No separate publish step. See Languages of an approved article.
  • Until then, that language is hidden from readers everywhere, including search, Ask, the sitemap, hreflang and the offline export. Older machine translations of such an article stay in the dashboard, hidden.
  • An approved language version can't be edited, re-translated or deleted (409).
  • Publishing an approved package through the API now records its approvers, as the dashboard always has. Articles published through POST /v1/knowledge-base/articles, PATCH /v1/knowledge-base/articles/:id or from-video showed no "Approved by" before.

v1.56.1 - Filter the planner by withdrawn or blocked​

  • GET /v1/modules/planner now accepts status=revoked and status=blocked. Both statuses were added in v1.56.0 but the filter still rejected them. The MCP planner tool takes them too.
  • POST /v1/modules/planner/qualification is now in the OpenAPI spec and the Postman collection. It shipped in v1.56.0 without them.

v1.56.0 - Withdraw or block a training qualification​

  • New: POST /v1/modules/planner/qualification takes a qualification away from one or more people, with a reason that is required and recorded. action: "withdraw" lets them earn it back by re-taking the training; action: "block" bars them from it, and no later pass or trainer sign-off lifts it. Up to 200 people per call. Needs the canLock permission, the same one as signing somebody off.
  • POST /v1/modules/:id/qualifications now answers 409 for somebody who has been blocked, instead of signing them off.
  • Two new planner statuses, revoked and blocked, on GET /v1/modules/planner and its CSV. They outrank every other status, including a later pass. Rows in either state carry a sanction object with kind, at, reason and byName. A blocked cell never counts as overdue; a withdrawn one does.
  • Somebody who re-qualifies after a withdrawal reads as renewed, not passed. The withdrawal is a documented gap and stays visible in the record after it has been put right.
  • GET /v1/modules/:id/qualifications still lists sign-offs only. Withdrawals and blocks are stored alongside them and were previously indistinguishable from a sign-off to any caller reading the newest record.
  • New MCP tool withdraw_qualification.

v1.55.3 - Analysis is billed by the video's real length​

  • Analyze and /process check your balance before any work. A fresh upload's length isn't known when you call them, so they need at least one minute of analysis in your balance, and the analysis measures the file before it starts. A video longer than your balance covers is refused before anything is processed: the job fails with a "Not enough credits" error and nothing is charged. Before this, a new upload skipped the balance check.
  • Analysis is charged by the measured length of the file, never less than the length you sent. Remix works the same way.
  • Charges larger than your balance now count against it. An operation that finishes after your balance ran out (for example a recording longer than expected) leaves the balance below zero, so the next request is refused until you buy credits or your plan renews. Before this, the difference was dropped.
  • Course actions check credits. POST /v1/courses, planning, storyboards and module generation answer 403 limit_exceeded when the balance can't cover the step, like the dashboard. Before this they started anyway.
  • Voiceover and tool runs draw on purchased credits once your plan's credits are used up, the same way the dashboard does.

v1.55.2 - No length limit per video​

  • Removed: the per-video duration limit. Analyze, translate, voiceover, export and /process no longer refuse a video for being longer than your plan's maximum, on any plan. A long video costs more, and a request your balance can't cover is still refused with limit_exceeded.
  • limits.maxVideoDuration on GET /v1/usage is now always 0 and is deprecated.

v1.55.1 - Fixes to section permissions and history​

  • Fixed: /articles/from-video refused by a section's publishers answered 400. It now answers 403 insufficient_role, as documented in v1.54.0. A 409 from its approval gate now keeps approvalBlocked, approvalStatus and requestId next to the message.
  • Sending back a section's current sharedWith or publishers is not a change. A caller that is not an owner or admin can include the stored value (or null on create) without a 403.
  • A move writes one history entry. PATCH /v1/knowledge-base/articles/:id with only sectionId records the move and no separate "edited" entry, like the dashboard.
  • Fixed: aiGeneratedAudio on PATCH /v1/videos/:id kept the last export. Changing it now marks the export out of date, so the next export renders the file again with the label.

v1.55.0 - AI-generated audio is marked​

  • Exported videos carry an AI-audio marking in their MP4 metadata when the sound contains a Vidocu voiceover, AI avatar speech, or audio the owner declared as AI-generated. The package manifest carries the same record in aiProvenance.audio. See AI-generated audio.
  • aiGeneratedAudio on POST /v1/videos/upload and PATCH /v1/videos/:id declares that an imported video's own audio is AI-generated; video responses return it. Refused on a locked video.
  • MCP: upload_video and update_video take aiGeneratedAudio; video results carry it.
  • Dashboard: a checkbox next to "Keep my own voice" in the upload window, and an "AI audio label" block in the editor's Video tab.

v1.54.0 - Who can publish in a section, and a clearer history​

  • Sections take publishers. A list of workspace members who may create, move or publish articles in that section; owners and admins always may, and an empty list means only them. POST/PATCH /v1/knowledge-base/articles, /articles/from-video and publishing answer 403 insufficient_role for anyone else. See Who can publish in a section.
  • Removed: a locked article's sectionId no longer returns 423 when shared sections require approval (added in v1.52.0). Where an article may go is now decided by the section's publishers, not by the lock.
  • sharedWith and publishers are owner and admin information. Other callers no longer receive them, and setting either needs an owner or admin (403).
  • Change history from the API. Creating, publishing, unpublishing and moving an article through the API now write the same history entries as the dashboard. A move is its own entry naming both sections.
  • Fixed: /articles/from-video with state: "published" skipped the package's approval gate. It now answers 409 until the approval is complete, like the dashboard.
  • MCP: create_kb_section and update_kb_section take publishers.

v1.53.1 - Export music volume overrides work​

  • Fixed: music.volume and music.duckVolume on POST /v1/videos/:id/export played almost silent. Both take 0 to 1, but the value reached the renderer as a percentage, so 0.3 played the music at 0.3% instead of 30%. They are now converted, and the documented 0 to 1 scale is unchanged, so requests that already send it need no change. The same applies to the MCP export_video tool. See Export.

v1.53.0 - Background music defaults that new videos actually use​

  • POST /v1/videos now starts a video with the Brand Kit's music: the workspace's default track when it has one, and its volume, volume while a voiceover plays, fade length and "play in scenes" values. Before, a video created through the API had none of them. See Background music defaults.
  • The defaults are copied at creation, never inherited later. Changing backgroundMusicVolume and the related fields on the Brand Kit affects videos created afterwards only.
  • Dashboard: the controls, which had no reachable screen, are now under Brand Kit -> Music -> Settings. In the editor, a video's Music settings say whether they still match the Brand Kit and offer "Reset to Brand Kit defaults". The fade can go up to 10 seconds there too.
  • Fixed: a video generated from a document in a workspace with a default track came out with no music at all.

v1.52.0 - Share a section with outside readers​

  • Sections take sharedWith. POST /v1/knowledge-base/sections and PATCH /v1/knowledge-base/sections/:id accept email domains and addresses that may read a top-level section of a gated knowledge base. Those readers sign in and see only the sections shared with them; the knowledge base's own access list keeps full access. Sub-sections follow their parent. See Sharing a section with outside readers.
  • PATCH /v1/knowledge-base takes access, and the knowledge base answers it: openSignup, allowedEmailDomains, explicitAllowedEmails, authMethod and the new sharedSectionsRequireApproval. With that on, an article in a shared section shows only while its package is approved and locked, and a locked article's sectionId returns 423. See Approval for shared sections.
  • Fixed: visibility on the knowledge base is public or gated. The endpoint validated it against the article values (public, staff, customers), so a gated knowledge base could not be set through the API at all.
  • Access is checked on every request. Removing a domain or address used to leave its readers signed in until their session expired. They now lose access on their next page.
  • MCP: create_kb_section and update_kb_section take sharedWith; update_knowledge_base takes access and the correct visibility values; results carry both.
  • Dashboard: a "Share with outside readers" block on each top-level section's edit row, and an "Approval for shared sections" switch under Settings -> Access.

v1.51.0 - A locked article's address is frozen too​

  • slug is no longer accepted on a locked article. PATCH /v1/knowledge-base/articles/:id now returns 423 for a slug change while the article is locked, the same as for its content. sectionId, state, seo and tagIds still work. The slug was editable for one release on the reasoning that a frozen article is the one whose URL outlives it; a regulated customer pointed out the cost - an approved path could be aimed at different content with nothing in the approval record to show for it. Changing an address now takes an unlock, and therefore a fresh approval round. See Locked articles.
  • MCP: update_kb_article says so, and a locked slug change returns the same 423.
  • Dashboard: the URL slug field is disabled on a locked article, and the locked banner says the address is frozen.

v1.50.0 - Section names in every language​

  • Sections take translations. POST /v1/knowledge-base/sections and PATCH /v1/knowledge-base/sections/:id accept a section's name and description per locale, keyed by locale code. PATCH merges by locale and null removes one. Only enabled, non-default locales are accepted; anything else is a 400. See Section names in other languages.
  • The public site reads them. A reader on a translated locale used to see every section name in the main language, in the navigation, on the cards, in breadcrumbs and in search results. Those now use the translation, falling back to the main-language text per field. Offline exports do the same in each language they carry.
  • MCP: create_kb_section and update_kb_section take translations, and section results carry them.
  • Dashboard: the section edit row has an "Other languages" block for every enabled locale, with a button that drafts the names by machine translation. Nothing is saved until you save the row.

v1.49.0 - Articles from slide-built videos​

  • A video built from a deck can generate an article. POST /v1/videos/:id/article read only the video's transcript, so anything made from a deck - Course Builder, "Keep my slides", the document and presentation converters - had nothing to read and returned Video must be analyzed before generating an article. It now writes from the slides themselves: each slide's heading, on-screen text and the narration spoken over it, in deck order. Recorded videos are unchanged and still write from their transcript.
  • The 400 now reads Video must be analyzed, or have slides with narration, before generating an article, and only fires when there is neither.
  • Dashboard: the same fix. The Article tab in the video editor refused deck-built videos with Either keyEvents or subtitles are required to generate a help article; it now generates from the slides, illustrated with the slides' own pictures rather than frames of the source file.

v1.48.0 - A locked article keeps its address editable​

  • slug joins the fields a locked article accepts. PATCH /v1/knowledge-base/articles/:id on a locked article now takes slug alongside sectionId, state, seo and tagIds; everything else still returns 423. A locked article is exactly the one whose URL outlives it, and correcting a path should not require breaking the permanent record to do it. The change still registers a redirect from the old slug.
  • Dashboard: the article editor saves those fields while locked. Section, URL slug and the SEO fields sat enabled on a locked article but were dropped before the request went out, so the edits vanished without an error. They save now, and the save indicator stays visible while locked. The title, body and excerpt are frozen and read-only, as before.
  • MCP: update_kb_article says the same in its description.

v1.47.0 - Section thumbnails​

  • Sections take a thumbnailUrl. POST /v1/knowledge-base/sections and PATCH /v1/knowledge-base/sections/:id accept a publicly reachable image URL, shown on the section's card on the public help site. PATCH with null removes it. Every section in the GET response now answers thumbnailUrl - null when it has none - so clients see one shape.
  • The shape is a knowledge-base setting, not a per-section one. Squares (a tile beside the section name) or cover (a 16:9 banner across the top of the card), chosen in the dashboard under Settings -> Homepage. A section with no image keeps a tinted initial in either shape, so a grid never renders a gap.
  • MCP: create_kb_section and update_kb_section take thumbnailUrl; list_kb_sections returns it alongside description.

v1.46.0 - Courses know who took them​

  • PATCH /v1/courses/:id takes a settings object. It decides who may open the course link (access.identity from anonymous through to a verified email or a Vidocu account, plus additive audience rules: open enrollment, email domains, an invite list, the training roster) and what marks a module complete (completion.rule of watch, watch_quiz or manual, with a watched threshold, and completion.sequential to lock each module until the one before it is done). Settings are merged, so a partial patch leaves the fields it does not name alone. An optional accessCode is a shared code checked at the door; it is stored hashed and never returned.
  • New GET /v1/courses/:id/progress. Per-module completion rates, a per-learner grid, a drop-off funnel (funnel), weekly completions (completionsByWeek), neverStarted and the median time to finish. The dashboard renders the same figures on a course's Analytics tab. The people list is a union of everyone with progress, everyone invited, and the training roster when the course admits it - so somebody who was invited and never started is a row, not an absence.
  • Course learners join the training roster. A learner who identifies with a verified email is upserted into the workspace's people list, so GET /v1/modules/planner sees them. Completing a course does not create a qualification; that stays a trainer sign-off.
  • Quiz attempt emails are stored lowercased. Every reader of the submissions collection normalizes, so an address recorded as typed joined to nothing - a mixed-case learner could pass a quiz and never complete the module gated on it.

v1.45.0 - Articles can attach and detach their video​

  • PATCH /v1/knowledge-base/articles/:id accepts linkedVideoId. Attach the video an article documents, or send null to detach it. Detaching clears linkedVideoTimestamp too and leaves the video itself untouched. The field is frozen on a locked article (423), and a call that attaches a video and publishes at once is gated on the video it is attaching.
  • Removing an opening/closing scene, background music, a video background, captions or an avatar from a video now persists. A cleared field reached the save as "not mentioned" rather than as a clear, so the old value survived every write - including on the article pages that render from the same document.

v1.44.0 - Transcript runs stop when there is no speech; pasted scripts are timed by sentence​

  • scriptSource: "transcript" no longer falls back to AI narration. A recording with no usable speech fails the analysis job with errorCode: "no_speech" (transcription_failed for a transient error) instead of quietly writing a script the caller did not ask for. GET /v1/jobs/:id and the jobs list carry the new errorCode field.
  • customScript is timed sentence by sentence. A plain-prose script used to land on the timeline paragraph by paragraph. The job result's scriptNotice now also says when the script is longer than the footage.

v1.43.0 - Planner rows name their package; locked articles stay in their languages​

  • GET /v1/modules/planner rows carry videoId. The package to open the training in - the master of its language family when it has one. Absent when the training is attached to no package. The dashboard's Planner links each training through it.
  • POST /v1/knowledge-base/articles/:id/translate refuses a locked article (423, article_locked). A lock freezes the article in every language: no new translation is generated, and an existing one can no longer be edited or marked reviewed. Before, the source could be signed off and a machine translation of it appear in the Knowledge Base with nobody having approved it.

v1.42.0 - Course modules are editable slides​

  • POST /v1/courses/:id/generate builds slides, not a rendered file. A generated module is now a video made of one editable slide per storyboard scene, each carrying its own voiceover and word timings. Nothing is rendered until export, so every scene's layout, text, picture and narration can still be changed in the video editor. Before, the module was rendered straight to an MP4 and only the narration stayed editable. The job, the videoId it produces, and everything you can do with that video are unchanged. See Generate.

v1.41.1 - Cloned voices stay in their workspace​

  • GET /v1/voices lists only your own clones. The stock library came straight from the voice provider's account, which is shared by every workspace, so it also carried every workspace's cloned voices. Clones are now listed only for the workspace that made them; the stock library is unchanged. The dashboard's voice pickers had the same leak and are fixed the same way.

v1.41.0 - Ask holds a conversation​

  • POST /v1/knowledge-base/ask takes history. Pass the earlier turns of a conversation (oldest first, up to 20) and a follow-up is answered in the light of them, with retrieval kept on the same subject. The answer is markdown now - steps, bold control names, and an article's screenshot when one shows the step - and is grounded in the matched articles in full rather than a short preview of each. See Ask.

v1.40.0 - No placeholders go live​

  • Publishing refuses unfinished articles. PATCH /v1/knowledge-base/articles/:id with state: "published" (and POST .../articles created as published) returns 409 while the body still carries a [TODO: ...] placeholder outside an HTML comment; the message lists the first few. Generated drafts leave one wherever a detail could not be confirmed, and resolving them is part of finishing the article. See Publishing and approvals.
  • Publishing indexes the article for Ask AI. It always said so; in fact only an edit to an already-published article was indexed, so a draft published untouched stayed invisible to Ask AI (while search found it). Publishing, editing a published article and unpublishing now each update the index, from the dashboard and from the API alike.

v1.39.0 - Offline copies are versioned​

  • Every offline export has a version. Builds with the same tag selection form a series and are numbered in order; the number is in the ZIP filename and in the footer of every exported page. Failed builds don't consume a number. See Offline export.

v1.38.0 - Offline copies of the knowledge base​

  • A knowledge base can be exported as a static site. POST /v1/knowledge-base/exports builds a ZIP with branding, sections, articles, images, each article's exported video and a client-side search index; it opens from index.html with no server. Pick languages, restrict to tagged articles, and choose whether to include videos (a missing or stale export is rendered first and metered like any export). Poll GET /v1/knowledge-base/exports/:id; the list carries presigned download links and a stale flag once the content moves on. See Offline export.

v1.37.0 - Tags on knowledge base articles​

  • Articles can carry tags. Tags are private labels for filing, never shown on the public site, and the way you will pick articles for an offline export. The list lives on the knowledge base: PATCH /v1/knowledge-base with the full tags array creates, renames and deletes in one call, and a deleted tag is pulled from every article. Assign with tagIds on article create and update; filter the list with tagId. tagIds stays editable on a locked article. See Tags.

v1.36.0 - Language families, a shared training across languages, and the whole planning half of a lock​

  • Translating a package now links the two. Both carry a languageGroup — the id of the first package in the family — so "this instruction in every language" is one request: GET /v1/videos?languageGroup=vid_abc123. translatedFrom records the one hop. Both are null on packages translated before this shipped; no migration invented families for them. See Language families.
  • A translated package now has a training, and it is the SAME training. It used to have none. The translated package points at the source's module and the module gains a translation in the target language, rather than being copied. A copy per language would double every row of the Training Planner and split one requirement's qualification records across several trainings. See What a translation creates.
  • In the Knowledge Base, a translated article now plays the video in the reader's language when the package was translated into it, instead of the source-language video under translated text.
  • The lock carve-out covers the whole planning half. settings.availableFrom, settings.availableUntil, settings.qualificationValidDays, settings.requiresTrainerApproval and settings.recordAttempts join settings.mandatory and keywords as editable on a locked module. settings.participantIdentity deliberately stays frozen: it decides whether a result can be attributed to a person, which is evidence. See Planning metadata and locks.
  • Approvers are emailed when a planning field moves after they approved it, through the API as well as the app. A signature should never be quietly amended underneath the person who gave it.
  • Every one of those fields is now in the change history, not just mandatory and keywords.
  • Fixed: a saved planning change could look unsaved in the app. Switching to another tab in the training editor and back rebuilt the fields from the module as it was when the page loaded, so a change that had been saved showed its old value — and setting it "again" then recorded nothing, because nothing had changed. The values now survive a tab switch.
  • MCP: list_videos accepts languageGroup; languageGroup and translatedFrom added to the video output shape.

v1.35.0 - Archived trainings, several keywords, and a record of what a lock lets through​

Three changes to the planner and to what a locked module keeps.

  • includeArchived=true now actually shows archived trainings. It did not: readiness, which defaults to the narrowest option with trainings behind it, ran afterwards and removed them again, because a retired training is rarely still published. Readiness no longer applies to archived trainings, since a training answers "is this finished" by being archived. See Which trainings belong in a plan.
  • The keyword filter takes several. ?keyword=Safety&keyword=Annual or ?keywords=Safety,Annual. Several keywords are an OR. One keyword behaves exactly as before. See Keywords.
  • Changes to settings.mandatory and keywords on a locked module are recorded - who, when, and whether the module was locked at the time. Those two fields are the only ones a lock lets through, so an approval that can be amended afterwards needs a trail. Visible in the app on a training's Change history tab. See Planning metadata and locks.
  • Fixed: PATCH /v1/modules/:id replaced the whole settings object. A body carrying only {"settings": {"mandatory": true}} dropped recordAttempts, participantIdentity and qualificationValidDays from that module. Settings now merge, so a partial update changes only the keys it names.
  • MCP: training_planner accepts an array for keyword.

v1.34.0 - Twenty more languages​

GET /v1/languages now returns 74 languages, up from 54. Nothing else changed: the same codes still work, and the list is served from the registry the product uses, so no client that reads it needs a change.

  • New codes - az Azerbaijani, kk Kazakh, ky Kyrgyz, mk Macedonian, be Belarusian, bs Bosnian, is Icelandic, ga Irish, cy Welsh, gl Galician, lb Luxembourgish, as Assamese, sd Sindhi, ps Pashto, jv Javanese, ceb Cebuano, ha Hausa, so Somali, ny Chichewa, ln Lingala. Accepted anywhere language is - Analyze, Translate, Voiceover and Update Video.
  • ps and sd are right-to-left, and are treated as such in subtitles and articles.
  • MCP: list_languages returns them with no change - it reads the same endpoint. If you cached the list, refresh it.

v1.33.0 - A decision, not a checkbox​

The planner's "not applicable" checkbox answered two questions with one blank box: this combination was ruled out, or nobody had looked at it yet. It is now three states. See Which combinations have to be trained.

  • applicability on every planner row - undecided (the state every combination starts in), applicable (somebody deliberately ruled it in) or not_applicable (somebody deliberately ruled it out). Filter the grid with GET /v1/modules/planner?applicability=undecided.
  • PUT /v1/modules/planner/deadline accepts applicability. Send undecided to erase a decision. dueAt and the decision stay independent.
  • notApplicable still works, everywhere it did. true is not_applicable, false is undecided, and it is still returned on every row. applicability wins when both are sent. Cells written before this release read correctly with no migration.
  • includeArchived=true brings retired trainings back into the planner, flagged archived on the row. They stay out by default - a retired training is not work still to do - but a training somebody completed before it was withdrawn is still a record they may have to produce. The response carries archivedCount either way. This reverses "archived trainings are no longer in the planner at all" from v1.31.0: the right answer was a filter, not a rule.
  • The CSV export's N/A column is now Decision (-, A, N/A), plus an Archived column.
  • MCP: training_planner takes applicability and includeArchived; set_training_deadline takes applicability.

v1.32.0 - One package, one name​

A workspace can now insist that a video, its help article and its training carry the same title. Off by default, so nothing you already named moves.

  • naming.syncPackageTitles workspace setting. With it on, an article or training generated from a video takes the video's title verbatim instead of writing its own.
  • PATCH /v1/videos/:id cascades a rename. Renaming a video renames its help article and its linked training module in the same request. A locked module is skipped - a lock is irreversible, and an approved round is locked - and the rename still succeeds. Renaming a locked video is still refused with 423, unchanged.
  • Titles edited by hand are left alone. The setting governs generation and rename, nothing else.
  • Knowledge Base articles are deliberately not renamed. A published article's title drives its public slug, and a video rename must not churn live URLs.
  • MCP: update_video renames the package when the setting is on. No request or response fields changed.

v1.31.0 - A planner you can narrow, and a lock that knows what it protects​

The planner shows every training against every person, which is what makes it useful and what makes it long. See The planner.

  • readiness=published|approved|all on GET /v1/modules/planner. A training appears the moment it is defined, so the default now excludes work in progress. Omit the parameter and the server picks the narrowest option that actually has trainings behind it, then reports it back as readiness with readinessCounts for all three.
  • Archived trainings are no longer in the planner at all. Archived means retired. The history stays in the analytics endpoints.
  • keywords on a module — up to 20 tags, 40 characters each, set with PATCH /v1/modules/:id. GET /v1/modules/planner?keyword=Safety filters by one, matched whole and case-insensitively, and the response carries every keyword in use in the workspace.
  • settings.mandatory and keywords are now editable on a LOCKED module. A lock freezes the evidence; who must take a training and how it is filed is not evidence. Everything else still returns 423, and a request mixing the two is rejected whole. See Planning metadata and locks.
  • MCP: training_planner takes readiness and keyword; update_module takes keywords.

v1.30.0 - Filtering for what is missing​

Two filters on GET /v1/videos, both for finding the gap rather than the record. See List videos.

  • approval=pending|approved|rejected|withdrawn — the latest approval decision on the package. Paired with backup=none it is the pre-audit check: signed off, with nothing filed offline.
  • kb=published|not_published|none — Knowledge Base state. not_published covers a video with no article and one whose article is still a draft; none is the narrower "no article was ever created from it". Resolved from the articles on every request, so it cannot go stale after one is archived or deleted.
  • MCP: list_videos takes approval and kb.

v1.29.0 - Planning a large grid​

The planner is a full cross-product, which is what makes it useful and what makes it long. These are the four ways to cut it down. See Training Modules.

  • mandatory on a training — PATCH /v1/modules/:id with { "settings": { "mandatory": true } } marks it compulsory for everyone. GET /v1/modules/planner?mandatory=true then answers "who is missing something they must have" in one call.
  • notApplicable on a cell — PUT /v1/modules/planner/deadline with { "notApplicable": true } rules out a combination that will never be relevant. Ruled-out cells are hidden unless you pass includeNa=true, and never count as overdue. Reversible at any time. dueAt and notApplicable are independent: sending one leaves the other alone.
  • Sorting — sort=urgency|training|person|status|expires|deadline with dir=asc|desc. Rows with no value for the sorted column sort last in both directions.
  • Planner rows now carry mandatory and notApplicable.
  • MCP: training_planner takes mandatory, includeNa, sort and dir; set_training_deadline takes notApplicable, and dueAt is now optional.

v1.28.0 - Setup instructions on saved logins​

A saved login now carries the steps the recorder performs on that site before every recording and screenshot, off camera. See Setup instructions.

  • setupPrompt on every item from GET /v1/recorder-sessions - null when unset.
  • New PATCH /v1/recorder-sessions/:domain - { "setupPrompt": "..." } sets it, null clears it. Up to 2000 characters. 404 when the domain has no saved login. Requires recordings:write.
  • A recording started with its own setupPrompt still wins for that job; the session's is unchanged.
  • MCP: list_recorder_sessions returns setupPrompt; new update_recorder_session tool.

v1.27.0 - The training planner​

Who has not done a training, which every other training endpoint is structurally unable to answer. See Training Modules.

  • New GET /v1/modules/planner — one row per (training, person), most urgent first. Includes people who have never attempted anything, so status: "not_started" finally has somewhere to appear. daysToDue is negative once a deadline has passed.
  • New PUT /v1/modules/planner/deadline — the date by which one person must complete one training. dueAt: null clears it. A deadline is an expectation, not a record, so it can be set on somebody who has never attempted the training.
  • New GET and POST /v1/modules/people — the training roster. Adding somebody before they have trained at all is what makes "has never done it" visible for a new employee. Idempotent on email.
  • Statuses: passed, renewed, expired, awaiting_signoff, in_progress, not_started. renewed means a qualification lapsed and was earned again; renewing early, before expiry, is not a gap and stays passed.
  • unattributedAttempts reports attempts recorded without an email, which belong to nobody on the grid rather than being silently dropped.

v1.26.0 - Knowledge Center sub-sections​

Sections in the Knowledge Center can now nest one level deep. See Sub-sections.

  • New parentId on every section returned by GET /v1/knowledge-base/sections, create, update and reorder. null for a top-level section, otherwise the id of its top-level parent. Sections created before this release come back with parentId: null.
  • Accepted on POST and PATCH /v1/knowledge-base/sections. The parent must exist, belong to the same knowledge base, and be top-level; a section cannot be its own parent; a section that already has sub-sections cannot be nested. Each of these is a 400 validation_error.
  • DELETE /v1/knowledge-base/sections/:id now returns 400 while the section still has sub-sections, alongside the existing 409 while it still holds articles.
  • position orders siblings. reorder numbers top-level sections and each parent's sub-sections as separate groups, so a mixed list never interleaves them.
  • Nothing moves: slugs stay unique across the whole knowledge base and public URLs stay flat, so nesting a section never breaks a link.
  • MCP: list_kb_sections, create_kb_section, update_kb_section and reorder_kb_sections carry parentId in their input and structured output.

v1.25.1 - Package backup state on videos​

Which published content has no offline backup yet, in one query.

  • New packageDownload object on list and get, recording when a video's package ZIP was last built and by whom. null means never.
  • New backup filter on list: none for videos that have never had a package built, taken for those that have.
  • Stamped when the ZIP finishes building and its link is sent, not when someone clicks that link. The build is what answers "has a backup been taken".

v1.25.0 - Read-only projects​

A project folder can now be readable by the whole workspace while only a named few can change what is in it. See Project Access.

  • New "readonly" value for access.mode on create and update, alongside the existing workspace and restricted. memberIds now means "who may see it" under restricted and "who may change it" under readonly.
  • New folder_read_only error code, 403. Returned by every write against an item in a read-only project: editing, generating, translating, voiceover, locking, archiving, requesting approval, publishing, moving, duplicating and deleting, plus renaming or deleting the project.
  • Reads are untouched. Listings include the project, videos open, and the package ZIP still downloads - a read-only project is meant to be consumed.
  • Deciding an approval and commenting stay open to anyone who can see the project. Approvers are named explicitly, and a comment is feedback about a package rather than part of it.
  • 403 here, not 404: the caller can see the item, so it says why. A restricted project keeps answering 404.
  • Filing something new into a project - a video, a Studio project, a remix - is now checked as a write against the target project on every path that accepts a projectId.

v1.24.1 - Studio projects know where they came from​

A video in the app can now be opened directly in Studio, which rebuilds it as editable layers. When that happens the resulting Studio project records which video it came from.

  • New sourceVideoId on GET /v1/studio/projects and the single-project endpoint. It is the id of the video the project was imported from, or null.
  • Informational only. The video is a separate object: it is not locked, hidden or otherwise changed by the Studio project pointing at it, and it may since have been deleted.
  • Set once, when the project is created. It is not writable, and duplicates and templates made from such a project do not inherit it - the relation belongs to the one project built from that video.

v1.24.0 - Project access​

A project folder can now be limited to specific people instead of the whole workspace. See Project Access.

  • New access object on create, update, get and list. { "mode": "workspace" } is the default every project already had; { "mode": "restricted", "memberIds": [...] } limits it.
  • A restricted project, and every video filed in it, returns 404 not_found for anyone who can't reach it - not 403. Listings simply omit it.
  • Workspace owners and admins always have access, listed or not. Otherwise a project could be stranded with nobody able to reach it.
  • API keys are not subject to restrictions - a key authenticates as a workspace, not a person. OAuth tokens are, and see exactly what their user sees in the app.
  • Setting access needs the permission to manage members. An update that omits it leaves the current setting alone, so a rename can't quietly open a project up.
  • Videos that aren't filed in a project stay visible workspace-wide, and existing share links keep working.
  • The check covers every endpoint that reads or acts on a video - analyze, export, translate, voiceover, article, subtitles, script, download, update, delete, governance, and job polling - plus the ones that pull a video's content elsewhere: remixes, module generation, course creation and knowledge-base/articles/from-video. Moving a video into a restricted project is checked too.
  • Studio projects, remixes and courses are filed in projects as well, and hide with them - on GET /v1/studio/projects, GET /v1/remixes, GET /v1/courses and each of their single-item endpoints.

v1.23.0 - AI Recorder on every paid plan​

The AI Recorder is no longer an add-on you have to ask for. Every paid plan includes it, so POST /v1/recordings works on any workspace that isn't on the free plan.

  • A free-plan workspace still gets 403. So does a workspace whose subscription has lapsed, for as long as it stays lapsed.
  • New 429 - two recordings run per workspace at a time. Recording browsers are a shared pool, so a start beyond that is refused rather than queued indefinitely. A job that is waiting for a free browser reports progress.step: "queued".
  • A 429 returns rate_limit_exceeded, not limit_exceeded. It means wait, not buy credits.

v1.22.1 - AI provenance for courses and Studio​

The Art. 50(2) marking now covers the two remaining places Vidocu writes text.

  • Courses - aiProvenance on GET /v1/courses, GET /v1/courses/:id and GET /v1/courses/:id/storyboard (per module), plus an X-Content-Provenance header on the storyboard response and the record on the public course share payload. Generating a module now also marks the narration on the resulting video, so every video surface inherits it.
  • Studio - aiProvenance on GET /v1/studio/projects/:id when the assistant wrote text in the project. Recorded at project level and server-owned: the editor only reports that the assistant acted, and the record is stamped server-side so it can't be forged.

Still nothing visible, still no setting.

v1.22.0 - AI provenance​

Text Vidocu generates is now marked as machine-generated in a machine-readable form, meeting our Article 50(2) obligation under the EU AI Act ahead of the 2 December 2026 date.

  • aiProvenance on the article and subtitles responses, plus an X-Content-Provenance header on both and on download.
  • The same record is written into each export format's own metadata slot - XMP for PDF, custom document properties for DOCX, <meta> tags for HTML and knowledge-base pages, a NOTE block for WebVTT, and manifest.json for the package ZIP.
  • humanEdited is derived by comparing the stored text against the hash taken at generation, so it reflects what actually happened rather than a flag someone had to remember to set.

Nothing visible changed. No badge, no label, no difference in how an article, PDF or knowledge-base page renders. There is no setting: the duty falls on us as the provider of the generation system, not on the workspace.

SRT and Notion have no metadata slot that leaves the content untouched, so their marking travels alongside - header, manifest and sidecar. The reasoning is written up in full.

v1.19.1 - Custom caption height​

  • captions.positionPercent on export - with position: "custom", place burned-in captions anywhere from 0 (as high as they go) to 100 (as low as they go) instead of only top, middle or bottom.
  • Both ends stop at a safe margin and the caption is anchored by its own edge, so no value can push it off screen. Out-of-range numbers are clamped.
  • top, middle and bottom render exactly where they always did.

v1.21.0 - Studio export​

POST /v1/studio/projects/:id/export renders a Studio timeline to video. Returns a job to poll; counts as one export against the plan.

Together with the timeline endpoint this closes the loop: build a timeline over the API, render it, download the result - no editor required.

The render logic moved to lib/studioExport.ts so the dashboard and the API run the same code rather than two copies of the Remotion composition props.

v1.20.0 - Studio​

The multi-track editor's projects.

  • Projects - list, create, get, rename, delete. The list returns timeline counts; a single project returns tracks, clips and media in full.
  • Duplicate - copies the timeline, never the share link or export state.
  • Share links - idempotent minting, with visibility as a separate switch so revoking doesn't break the URL.
  • Templates - the workspace's own plus the global gallery.

The timeline is read-only through the API: it's a large interdependent document the editor owns. Exporting isn't exposed yet - it needs the same extraction Remix export went through.

New scopes: studio:read, studio:write.

v1.19.0 - Remix export and regenerate​

The two Remix endpoints v1.17.0 shipped without.

  • POST /v1/remixes/:id/export - blog, LinkedIn and X return their content immediately; shorts and trimmed cuts start a render and return a job.
  • POST /v1/remixes/:id/regenerate - rewrite a text output from the analyzed transcript. Free, and it replaces the existing draft.

Both run the same code as the dashboard rather than a second copy, so the lambda payloads and the generation prompts exist in exactly one place.

v1.18.0 - Training Modules + Brand Kit assets​

  • Training modules - generate, edit, lock, translate, and the compliance records around them: attempts, passers, trainer sign-offs and workspace analytics.
  • POST /v1/modules/:id/submit records a graded attempt. Grading runs server-side against the answer keys, so an LMS integration never needs them.
  • Answer keys are included by default on a single module and excluded everywhere else; pass forLearner=true for the learner-safe shape.
  • Brand Kit assets - GET|POST /v1/brand-kit/assets, DELETE /v1/brand-kit/assets/:id.

New scopes: modules:read, modules:write.

v1.17.0 - Remix​

One long video into shorts, a blog post and social copy.

  • Create and analyze - POST /v1/remixes, POST /v1/remixes/:id/analyze. Sourced from a workspace video or an external URL.
  • One analysis per workspace at a time. A second request returns status queued rather than failing, and starts on its own.
  • Read, update, delete plus GET /v1/remixes/:id/exports.

Starting an export and regenerating drafted copy are not exposed yet - both need extracting out of their session-bound handlers first, so the API and dashboard run the same code.

New scopes: remixes:read, remixes:write.

v1.16.0 - AI Recorder​

Describe a flow, get a screen recording.

  • Start a recording - POST /v1/recordings. Long-running; poll the job.
  • Status carries two distinct failure shapes: aborted (no usable video) and partial (a video that doesn't show the whole flow). Don't treat a partial recording as finished.
  • POST /v1/recordings/:jobId/input - hand over a one-time code or 2FA challenge when the agent pauses for one.
  • Download returns a time-limited URL rather than the bytes.
  • Saved logins - GET /v1/recorder-sessions, DELETE /v1/recorder-sessions/:domain. Stored cookies are never returned.

Charged per minute of finished video on completion, with a preflight balance check. Failed and aborted jobs are free.

New scopes: recordings:read, recordings:write.

v1.15.0 - Knowledge Center​

The hosted help site, end to end.

  • Knowledge base - GET|POST|PATCH|DELETE /v1/knowledge-base. One per workspace, so it's a singular resource.
  • Sections and articles - full CRUD, cursor-paginated listing, Markdown content.
  • POST /v1/knowledge-base/ask - retrieval-grounded answers with citations, for support-bot integrations. Answers from your published articles, not from general model knowledge.
  • Import from video, translation, reindex, redirects and analytics.

Locked articles are frozen to sectionId, state and seo; publishing respects the source video's approval round.

Knowledge Center AI operations bill against the add-on's own AI credit pool, not the workspace credit balance.

New scopes: knowledge_base:read, knowledge_base:write.

v1.14.0 - Locks & Approvals​

The controls that make a video library auditable.

  • Locks - POST|DELETE /v1/videos/:id/lock. Freezing cascades to the linked training module and KB articles. Unlocking is refused while an approval round is open.
  • Approvals - request, list, decide, withdraw. Every named approver must approve; one rejection fails the round. Only a named approver can decide, and only once.
  • Comments - review notes, optionally pinned to a timestamp.

Actions record who performed them: OAuth tokens are attributed to their user, API keys to the workspace owner. For per-person attribution in an audit trail, use OAuth rather than a shared key.

New scopes: locks:write, approvals:read, approvals:write, comments:read, comments:write.

v1.13.0 - Courses​

Turn a document or an existing video into a set of modules, each of which becomes one video.

  • Courses - POST /v1/courses from a PDF, DOCX, PPTX, Keynote, TXT or an existing video; list, read, update and delete.
  • Storyboard - GET|POST /v1/courses/:id/storyboard. The read includes omissions (source content deliberately left out) and warnings, so a review can see what was skipped on purpose.
  • Generate - POST /v1/courses/:id/generate renders one module into an ordinary video, which then works with export, translate and everything else.

Each step is async and returns a jobId. Only one step runs per module at a time - a second request gets 409.

New scopes: courses:read, courses:write.

v1.12.0 - Brand Kit​

The settings that keep generated output on-brand are now readable and writable by API. They were always applied - nothing could inspect or change them, so an API-only integration was stuck with whatever the dashboard had.

  • Brand Kit - GET|PATCH /v1/brand-kit: music, scenes, watermark, background, caption and article styling, default voice and language.
  • Pronunciations - how words should be said in narration.
  • Brand skills - reusable style guides, applied per video via scriptSkillId on analyze.
  • Glossary - brand terms and their translations, including CSV import with merge or replace. This feeds the translation pipeline, so it directly improves translation quality.

New scopes: brand_kit:read, brand_kit:write.

v1.11.0 - Phase 1 complete​

  • Create a photo avatar - POST /v1/avatars, from a public image URL. Enterprise plan; new scope avatars:write.
  • Article styling - style on generate and fetch, returned as articleStyle so embedded HTML can be rendered on-brand. Presentation only; the wording is unchanged.
  • Cursor pagination on projects - now on all three collection endpoints.

API keys created in an admin or internal workspace now carry admin status. Previously only OAuth tokens did, so an admin's API key was metered and throttled like any customer's.

v1.10.0 - The rest of the options​

  • Voiceover: model, speed, stability.
  • Analyze: keyEvents, analysisModel, subtitleModel, autoZoom.
  • Export: reframe, slides, avatarSettings.
  • Get the article - GET /v1/videos/:id/article, in Markdown or HTML, without regenerating it.
  • Get one avatar - GET /v1/avatars/:id.
  • Delete a cloned voice - DELETE /v1/voices/:id. New scope voices:write.
  • WebVTT - download?type=vtt, generated from the subtitles, so it works without an export.
  • Cursor pagination on /v1/videos and /v1/jobs. page still works; cursor is what to use when walking a collection that's changing under you.

Not available through the API, with reasons: article PDF/DOCX export and auto-extracted screenshots (both need capabilities the API service doesn't carry), voice cloning (needs recorded consent), and voiceover loudness matching (only applies to single-clip regeneration).

v1.9.0 - Scripts, downloads, webhooks and idempotency​

The last of the gaps that made the API awkward to build against.

Edit the script​

  • Get and replace a video's timed lines - the text burned in as subtitles and spoken as narration. Fix a typo without re-analyzing.
  • Editing a line's text drops the narration generated from it, so an export can't say something the subtitles no longer show. voiceoverInvalidatedLines tells you how many lines were affected; untouched lines keep their audio.

Get your files​

  • Download - GET /v1/videos/:id/download?type=… for the exported video, the source, the voiceover audio, the SRT, or the thumbnail. Returns a clear reason when the file isn't ready instead of a null field.

Manage webhooks​

  • Create, update and delete webhooks through the API. Previously dashboard-only, so an integration couldn't provision its own delivery endpoint.
  • The signing secret is returned once, at creation. Re-enabling a failed webhook resets its failure count.

Retry safely​

  • Idempotency - send Idempotency-Key on any POST and the request runs once, however many times it arrives. A duplicate export or tool run costs real money; this is the general fix.

More options​

  • Voiceover gained sentencePauseSeconds and applyPronunciations (on by default, matching everywhere else in Vidocu).
  • Article gained language, instructions and format (markdown or html).

New scopes: webhooks:read, webhooks:write. subtitles:write now also covers script edits.


v1.8.0 - Look up voices, avatars and what things cost​

Three reference endpoints, plus a machine-readable spec.

  • Voices - GET /v1/voices. voiceId was a required parameter with no way to discover a valid value, so callers copied one out of the dashboard. Your own cloned voices come first, marked custom: true.
  • AI Avatars - GET /v1/avatars. Searchable by name; your workspace's photo avatars always come first.
  • Credit costs - GET /v1/usage/credits. What each operation costs alongside your balance, so you can price a job before starting it.
  • OpenAPI spec - openapi.json, generated from the server's own validation schemas rather than written by hand, so it can't drift from what the API accepts.

New scopes: voices:read, avatars:read. Available through MCP as list_voices, list_avatars and get_credit_costs.


v1.7.1 - Tool runs are charged on completion​

Tool runs started through the API were checked against your balance at dispatch and then never deducted - the run happened, the balance didn't move. They're now charged when the run succeeds, using the quantity the renderer reports rather than the estimate in your request.

  • A failed run costs nothing. Charging happens on success, not dispatch.
  • The billed quantity is the real one - actual output duration, actual slide count.
  • Grandfathered minutes plans are unaffected - tool runs remain uncharged there.

If you've been running tools through the API, expect balance.used in GET /v1/usage to start moving where it previously stood still.


v1.7.0 - Control how videos are analyzed and rendered​

Export accepted one field, language. Everything else came from the stored video and your Brand Kit, so "render this one vertical without a watermark" wasn't expressible.

It now takes per-request overrides: ratio, watermark, captions, music, openingSceneUrl, closingSceneUrl, zooms and elements. Precedence is request > video > Brand Kit, and the overrides apply to that render only - nothing stored changes. Pass false to drop a whole track: {"watermark": false, "music": false}.

Analyze gained the options the product has had all along: contentStyle (how-to, sop, promotional, training, announcement), productName, scriptSource (analyze the footage, reuse the transcript, keep the original audio, or supply your own script), customScript, scriptSkillId for a Brand Kit style guide, and forceRefresh.

Both are available through the MCP server on export_video and analyze_video.


v1.6.0 - Manage videos, find jobs, look up languages​

Four endpoints that were missing rather than broken.

  • Update Video - PATCH /v1/videos/:id. Rename, move between projects, or adjust the context used when generating content. A locked video can still be moved; anything else returns 423.
  • Delete Video - DELETE /v1/videos/:id. Removes the video and its stored files. Until now you could create videos through the API and never remove them. Locked videos are refused.
  • List Jobs - GET /v1/jobs. Recent jobs newest first, filterable by status and type, for when you've lost a jobId.
  • Languages - GET /v1/languages. All 54 supported languages with the exact codes analyze, translate, voiceover and subtitleLanguage expect, so you don't have to guess one and get a 400.

All four are available through the MCP server as update_video, delete_video, list_jobs and list_languages.


v1.5.0 - Locks, roles, and honest tool pricing​

Four corrections to existing endpoints. Nothing was renamed or removed.

Locked content is now respected​

A locked video's content is frozen while it moves through an approval cycle. The API previously ignored locks entirely: analysis, voiceover, translation and article generation would all overwrite a locked package, and DELETE /v1/projects/:id?cascade=true would delete locked videos along with their stored files.

  • Those four operations now return 423 Locked
  • A cascading project delete is refused if the project holds any locked video
  • GET /v1/videos and GET /v1/videos/:id return locked and lock
  • Exporting a locked video still works - rendering from frozen content is the point

Workspace roles apply to OAuth tokens​

Scopes describe what an integration asked for; the workspace role behind the token describes what that person may do. Both are now enforced, so a token minted by a viewer can hold videos:write and still be refused with 403 insufficient_role.

API keys are unchanged - a key authenticates as a workspace, not a user, and keeps full workspace permissions.

Tools bill at their real rate​

Every tool executed through the API used to bill the same flat per-run rate regardless of what it did. Each tool now bills against its own entry: per minute, per slide, or per run. Most are unchanged at 1 credit per minute; the expensive ones are now charged properly (AI avatar generation is 150 credits per minute). See Execute Tool.

Presentation tools (PDF, PPTX, Keynote, Google Slides to video) now execute correctly through the API; previously they were dispatched to the wrong backend.

AI avatar generation and subtitle burning now work through the API. Both render inside Vidocu rather than on the tool backend, so they were previously unreachable. They behave like any other tool - 202 with a jobId - except they don't fire a tool.completed webhook, so poll GET /v1/jobs/:id for those two. List Tools reports apiSupported for every tool; it's false only when a tool genuinely can't be reached.

Usage reports your actual unit​

GET /v1/usage now returns billingModel and a unit-tagged balance block. Most workspaces are billed in credits, which the old videoMinutes field labelled as minutes. That field is kept with the same values and is now deprecated.


v1.4.1 - API keys on every plan​

The REST API is no longer gated to Business. API keys now work on every Vidocu plan, including Free - same as the MCP integration. Your plan only determines your monthly allowance and max video duration per request. See Plans & Limits.


v1.4.0 - API access included with Business​

The standalone API plans (Starter, Growth, Scale) are gone. API access is now part of your Vidocu plan.

  • REST API (API keys) is available on the Business plan and above. There's no separate API subscription to buy.
  • The Claude / MCP integration works on any plan when you connect with OAuth.
  • One shared meter. API usage now draws from the same monthly video-minute allowance as the app, instead of a separate API quota.
  • Fixed rate limit of 60 requests/minute for every plan.
  • No more per-feature gating. Translation, voiceover, and /process are available to every API caller. Within /process, only analysis and translation consume minutes; voiceover, export, and help articles are free.

See Plans & Limits for the full breakdown.


v1.3.0 — MCP Server​

Connect Vidocu to AI assistants like Claude via the Model Context Protocol.

  • MCP server at mcp.vidocu.ai — 15 tools covering all API operations
  • Streamable HTTP transport, stateless, works with Claude Desktop and other MCP clients
  • Authenticate with your existing API key via X-Vidocu-Api-Key header

v1.2.0 — One-Shot Processing​

Process a video end-to-end with a single API call. Upload a video URL and specify which operations to run — the API chains them automatically and reports progress via job polling or webhooks.

New endpoint​

  • Process video — POST /v1/videos/process — upload + analyze + optionally generate voiceover, help article, export, and translations in one request (Business+)

New webhook events​

  • process.completed — all pipeline steps finished successfully
  • process.failed — a step in the pipeline failed

Improvements​

  • Analysis results (subtitles, key events, status) are now persisted to the video document immediately after analysis completes, so GET /v1/videos/:id/subtitles returns results without requiring a separate save step

v1.1.0 — Tools API​

Execute free tools (video trimmer, subtitle translator, transcript extractor, etc.) programmatically through the API.

New endpoints​

  • List tools — GET /v1/tools — discover all available tools and their parameter schemas
  • Get tool — GET /v1/tools/:type — get a single tool's full definition
  • Execute tool — POST /v1/tools/:type/execute — run a tool and get a job ID

Improvements​

  • tool.completed and tool.failed webhook payloads now include toolType and result fields
  • Job polling (GET /v1/jobs/:id) now supports tool jobs
  • Usage tracking includes toolExecutions counter

v1.0.0 — Initial release​

The Vidocu API v1 is now available.

Endpoints​

  • Upload — POST /v1/videos/upload
  • List videos — GET /v1/videos
  • Get video — GET /v1/videos/:id
  • Analyze — POST /v1/videos/:id/analyze
  • Export — POST /v1/videos/:id/export
  • Translate — POST /v1/videos/:id/translate (Growth+)
  • Subtitles — GET /v1/videos/:id/subtitles
  • Voiceover — POST /v1/videos/:id/voiceover (Growth+)
  • Article — POST /v1/videos/:id/article
  • Jobs — GET /v1/jobs/:id
  • Usage — GET /v1/usage

Features​

  • API key authentication (vdo_live_ format)
  • Per-key rate limiting with response headers
  • Webhook delivery with HMAC SHA-256 signatures
  • 4 plans: Starter, Growth, Scale, Enterprise
  • JSON and SRT subtitle formats