Skip to main content

Knowledge Center

A hosted, public help site fed by your videos. Articles live in sections, get translated into your enabled locales, and answer questions through a retrieval endpoint you can wire into a support bot.

The Knowledge Center is a paid add-on. Without it, every endpoint here returns 403. One knowledge base per workspace, so this is a singular resource - /v1/knowledge-base, not /v1/knowledge-bases.

The knowledge base​

GET /v1/knowledge-base · POST /v1/knowledge-base · PATCH /v1/knowledge-base · DELETE /v1/knowledge-base

curl -X POST https://api.vidocu.ai/v1/knowledge-base \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Help", "slug": "acme", "defaultLocale": "en"}'
{
"id": "0b0f...",
"name": "Acme Help",
"slug": "acme",
"url": "https://acme.vidocu.help",
"defaultLocale": "en",
"enabledLocales": ["en"],
"visibility": "public"
}

The slug is the public address. Changing it changes every URL on the site, so add redirects for anything already published. 409 if the workspace already has a knowledge base or the slug is taken.

DELETE is a soft delete: the site stops resolving, the articles are kept.

Sign-in access​

visibility is public (anyone can read) or gated (readers sign in first). On a gated knowledge base, access decides who gets in:

curl -X PATCH https://api.vidocu.ai/v1/knowledge-base \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"visibility": "gated", "access": {"openSignup": false, "allowedEmailDomains": ["acme.com"]}}'
FieldMeaning
openSignuptrue lets any email in. false limits sign-in to the lists below and to shared sections.
allowedEmailDomainsDomains with full access to every section, e.g. ["acme.com"].
explicitAllowedEmailsIndividual addresses with full access to every section.
authMethodmagic-link emails a one-time link; vidocu signs readers in with a Vidocu account; oidc is company sign-in.
sharedSectionsRequireApprovalSee Approval for shared sections.

access is merged: fields you leave out are kept. Access is checked on every page, not only at sign-in, so removing a domain locks its readers out on their next click.

Company sign-in​

On the Enterprise plan, readers can sign in through your own identity provider over OpenID Connect (Keycloak, Okta, Microsoft Entra ID and others). Anyone who can sign in there can read the knowledge base. They don't need a Vidocu account.

We set up the connection with your IT team. Once it's active, switch to it:

curl -X PATCH https://api.vidocu.ai/v1/knowledge-base \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"visibility": "gated", "access": {"authMethod": "oidc"}}'
  • Before the connection is active, setting oidc returns 400.
  • With oidc on, openSignup and the email lists are not used, and section sharing has no effect: every signed-in reader sees every section.
  • Switching to or from oidc signs out everyone who signed in the other way.
  • A reader session lasts 24 hours, so someone removed from your identity provider loses access within a day.

Sections​

GET /v1/knowledge-base/sections · POST /v1/knowledge-base/sections · PATCH /v1/knowledge-base/sections/:id · DELETE /v1/knowledge-base/sections/:id · POST /v1/knowledge-base/sections/reorder

Every article belongs to a section. New sections are appended to the end of their siblings; reorder takes the full list of section ids in the order you want them displayed.

Sub-sections​

Sections nest one level deep. A section carries parentId: null (or absent, on sections created before this existed) for a top-level section, or the id of the top-level section it sits under.

curl -X POST https://api.vidocu.ai/v1/knowledge-base/sections \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Billing", "slug": "billing", "parentId": "sec_account"}'

The rules, each answered with 400 when broken:

  • The parent must exist and belong to the same knowledge base.
  • The parent must itself be top-level. A sub-section cannot have sub-sections.
  • A section cannot be its own parent.
  • A section that already has sub-sections cannot be moved under another section.
  • Deleting a section that still has sub-sections is refused. Move them (PATCH with a new parentId, or null to make them top-level) or delete them first.

What does not change: slugs stay unique across every section of the knowledge base, and public URLs stay flat (/{section-slug}), so nesting a section never breaks a link. Articles can live in a top-level section or a sub-section. position orders siblings, so reorder numbers top-level sections and each parent's sub-sections as separate groups. On the public site a sub-section is never shown to someone who cannot see its parent: the effective visibility is the more restrictive of the two.

Deleting a section that still holds articles returns 409 - orphaned articles would vanish from the site with no trace, so move them first.

Thumbnails​

A section can carry a thumbnailUrl: a publicly reachable image shown on its card, on the homepage grid and on the cards for sub-sections.

curl -X PATCH https://api.vidocu.ai/v1/knowledge-base/sections/sec_billing \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"thumbnailUrl": "https://cdn.example.com/billing.png"}'

null removes it. Every section answers thumbnailUrl - null when it has none - whatever era it was created in.

The shape is one setting for the whole knowledge base, not a field on the section, because a grid that mixes both reads as broken rather than as variety. It lives in the dashboard under Settings -> Homepage:

ModeWhat the card looks likeImage to supply
Squares (default)A square tile beside the section name200x200 or larger, square
CoverA 16:9 banner across the top of the card800x450 or larger, 16:9

Images are cropped to fill, never letterboxed. A section with no thumbnail is not a gap: it keeps the tinted tile in squares mode and gets a tinted panel carrying its initial in cover mode, so every card in a row stays the same height.

Thumbnails travel into an offline export with the rest of the site's images.

Section names in other languages​

A section's name and description are in the knowledge base's defaultLocale. For its other enabled locales, a section carries translations, keyed by locale code:

curl -X PATCH https://api.vidocu.ai/v1/knowledge-base/sections/sec_start \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"translations": {"fr": {"name": "Premiers pas"}, "it": {"name": "Primi passi", "description": "Da dove iniziare"}}}'
  • PATCH merges by locale. Locales you leave out are kept; null (or a row with both fields empty) removes one.
  • A missing field falls back to the main-language text, so a reader on /fr/ sees the French name with the original description until you add one.
  • Only the knowledge base's enabled, non-default locales are accepted. The default locale is refused with 400: its text is the section's own name and description.
  • Slugs are not translated. /fr/start and /start are the same section, so a link never breaks when a reader switches language.
  • POST takes translations too. Sections without any answer no translations field.

The public site, its search results and the offline export all use the reader's language. In the dashboard the same fields sit in the section's edit row, with a button that drafts them by machine translation for you to review before saving.

Sharing a section with outside readers​

A gated knowledge base can open single sections to people outside its access list, such as a customer's team, without opening the rest. Set sharedWith on a top-level section:

curl -X PATCH https://api.vidocu.ai/v1/knowledge-base/sections/sec_customer_docs \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sharedWith": {"emailDomains": ["customer.com"], "emails": ["[email protected]"]}}'
  • Full access stays full. Anyone on the knowledge base's own access list still sees every section.
  • Outside readers see only what is shared with them. Someone matching only a section's sharedWith can sign in, and sees that section, its sub-sections and their articles. Every other section is absent for them: not in navigation, search, Ask answers or direct links (404).
  • Nothing is shared by default. A new section is invisible to outside readers until you add them to it.
  • Sub-sections follow their parent and can't carry sharedWith themselves. Nesting a shared section, or sharing a sub-section, is a 400.
  • null or empty lists stop sharing. Entries are lowercased and deduplicated; a domain may be written with or without @.
  • It applies to gated knowledge bases. On a public one every reader already sees every public section.

Every section answers sharedWith: null when it isn't shared.

Approval for shared sections​

With access.sharedSectionsRequireApproval set to true, a shared section only ever shows approved work:

  • An article in a shared section (or its sub-sections) is shown, to every reader, only while its package is approved and locked. Until then it stays published but out of sight, including from search, Ask, the sitemap and offline exports. Unlocking a package takes its article out of view again until the next approval.
  • An article with no video has no package to approve, so it never shows in a shared section.

To control who can put an article in a shared section at all, use the section's publishers.

Who can publish in a section​

publishers on a section restricts who may create an article in it, move an article into it, or publish an article while it sits there:

curl -X PATCH https://api.vidocu.ai/v1/knowledge-base/sections/sec_customer_docs \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"publishers": {"userIds": ["usr_quality_lead", "usr_docs_owner"]}}'
  • Workspace owners and admins always may, so {"userIds": []} means owners and admins only.
  • null removes the restriction: anyone who can edit articles may publish there again.
  • A sub-section without its own list follows its parent's.
  • Anyone else gets 403 insufficient_role naming the section. Editing an article that already sits in the section is still allowed; unpublishing is too.
  • Every move is recorded in the article's change history as its own entry naming both sections, and every publish names the section it went live in. This includes changes made through the API.
  • publishers and sharedWith are only returned to, and only accepted from, owners and admins (an API key acts as the owner). Other callers see sections without them.

Tags​

Tags are private labels on articles. They never appear on the public site; they are how you file articles for yourself, and how you choose which articles go into an offline export. The list lives on the knowledge base and articles point at it by id.

PATCH /v1/knowledge-base takes the full tags list: a row with a known id is renamed or recolored, a row without one is created, and a tag left out of the list is deleted and removed from every article that carried it. A knowledge base holds up to 50 tags; names are unique (case-insensitive) and up to 40 characters; color is a hex value.

curl -X PATCH https://api.vidocu.ai/v1/knowledge-base \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"tags": [
{"id": "tag_existing", "name": "Customer: Acme", "color": "#007A7A"},
{"name": "Internal", "color": "#7C3AED"}
]}'

Assign tags with tagIds on POST /v1/knowledge-base/articles and PATCH /v1/knowledge-base/articles/:id. Unknown ids return 400. Filter the list with GET /v1/knowledge-base/articles?tagId=.... Tags are filing metadata, so they stay editable on a locked article.

Articles​

GET /v1/knowledge-base/articles · POST /v1/knowledge-base/articles · GET /v1/knowledge-base/articles/:id · PATCH /v1/knowledge-base/articles/:id · DELETE /v1/knowledge-base/articles/:id

Content is Markdown. The list endpoint returns metadata without article bodies - fetch a single article for its content. It's cursor-paginated and filterable by state, sectionId, visibility, linkedVideoId, tagId and sourceLocale.

curl -X POST https://api.vidocu.ai/v1/knowledge-base/articles \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"sectionId": "sec_...",
"title": "Resetting your password",
"slug": "resetting-your-password",
"content": "## Steps\n\n1. Open Settings...",
"state": "published"
}'

Articles in one language​

An article is written in the knowledge base's main language unless you say otherwise. Set sourceLocale on create to write it in another enabled locale. Its title and content are then in that language, and readers see it only there: /de/billing/rechnungen-exportieren works, and the main-language URL returns 404. It stays out of the main language's section pages, search and sitemap too.

curl -X POST https://api.vidocu.ai/v1/knowledge-base/articles \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"sectionId": "sec_...",
"title": "Rechnungen exportieren",
"slug": "rechnungen-exportieren",
"content": "## Schritte\n\n1. Öffnen Sie die Einstellungen...",
"sourceLocale": "de"
}'

To reach more readers, translate it. A translation into the main language makes it readable there like any other article.

  • sourceLocale must be enabled on the knowledge base, or you get 400.
  • Every article returns sourceLocale, resolved: an article in the main language returns the main language's code.
  • The language is fixed once the article exists. Sending a different sourceLocale on PATCH returns 400. To move an article to another language, create a new one.
  • POST /v1/knowledge-base/articles/from-video takes sourceLocale too. Set it to the video's language when that isn't the main one.

Attaching a video​

An article can carry the video it documents. Set linkedVideoId on create or update; send null on update to detach it. Detaching removes the link only - the video itself stays in the workspace.

# Attach
curl -X PATCH https://api.vidocu.ai/v1/knowledge-base/articles/art_... \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"linkedVideoId": "vid_..."}'

# Detach
curl -X PATCH https://api.vidocu.ai/v1/knowledge-base/articles/art_... \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"linkedVideoId": null}'

The link is frozen on a locked article, and a request that attaches a video and publishes in the same call is gated on the video it is attaching, not the one it is replacing.

Locked articles​

An article that came from a locked video is a permanent record. It stays organizable - sectionId, state, seo and tagIds can still change, so it can be filed and surfaced - but everything else is frozen and returns 423.

The slug is frozen too. It is the article's address, and re-pointing an approved path at other content would leave nothing behind in the approval record. Changing the address means unlocking the article first, which starts a fresh approval round. Locked articles can't be deleted at all.

Publishing and approvals​

Publishing an article whose source video is under review is blocked with 409 until every approver has approved. An approval also goes stale: if the source video was unlocked and edited after being approved, publishing is refused until a fresh approval round passes.

Publishing is also refused with 409 while the body still carries an unfinished placeholder - any [TODO: ...] marker outside an HTML comment. Vidocu's own draft writer leaves one wherever it could not confirm a detail, and a generated article is not finished until every one is resolved. The error message lists the first few. Creating an article directly as published is checked the same way.

Import from a video​

POST /v1/knowledge-base/articles/from-video

curl -X POST https://api.vidocu.ai/v1/knowledge-base/articles/from-video \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"videoId": "vid_abc123", "sectionId": "sec_...", "state": "published"}'

The video must already have a help article - generate one with POST /v1/videos/:id/article first, or this returns 409. The dashboard generates one on the fly; the API deliberately doesn't, because that's a metered job that shouldn't fire as a side effect of an import.

The imported article is linked back to its video, and a locked video produces a locked article. Pass sourceLocale when the video is in a language other than the knowledge base's main one (see Articles in one language).

Translation​

POST /v1/knowledge-base/articles/:id/translate with {"targetLocales": ["es", "de"]}

Targets must be locales enabled on the knowledge base. A knowledge base can enable as many locales as you need. An article translates from the language it is written in, so an article written in German can be translated into the main language too. Its own language is skipped as a target. Each target language is charged kb_article_translate credits from the workspace credit balance (see Billing). The whole request is priced up front: if the balance can't cover every target, nothing is translated and you get 403 limit_exceeded.

A locked article can't be translated (423): its languages are frozen with it.

Languages of an approved article​

An article published from an approved package is a controlled document, and so is every language of it. Its languages don't come from machine translation. Each one comes from its own translated package, which goes through its own approval:

  1. Translate the video into the language. That creates a translated package in the same language family, with its own article.
  2. Send that package for approval.
  3. When the last approver approves, the package's article becomes the article's version in that language, copied word for word. Readers see it from that moment. There is no separate publish step.

Until then, readers don't see that language anywhere: no page, no entry in a section or search, no Ask answer, no sitemap or hreflang entry, and nothing in the offline export. A machine translation made before the article was approved stays in the dashboard, marked as hidden, and is replaced once the package in that language is approved. The language page plays that approved package's video.

The copy is the approved text, so it can't be edited, re-translated or deleted (409). To change it, translate the video into that language again and send the new package for approval. Once it is approved, it replaces the old version. The most recently created approved package in a language is the one readers see. A duplicate of a translated package is not part of the language family, so approving a duplicate does not change the Knowledge Base.

To take one language down without touching the others, archive that language's package. The language disappears for readers at once, and the dashboard shows it as withdrawn. Unarchive the package to publish it again, or approve a new package in that language to replace it. An older approved version never comes back on its own. Archiving a translated package leaves the training alone, because the master and the other languages share it.

Unpublishing, archiving or deleting the article itself still takes every language with it.

The article page for a language shows that language's own approvers and approval date, not the master's.

Articles that were never published under approval are unaffected: their languages work as before.

Ask - retrieval-grounded answers​

POST /v1/knowledge-base/ask

The endpoint to wire a support bot into. It answers from your published articles, with citations, rather than from a model's general knowledge.

curl -X POST https://api.vidocu.ai/v1/knowledge-base/ask \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"question": "How do I reset my password?"}'
{
"answer": "Open Settings, choose Security, then...",
"citations": [{ "articleId": "...", "title": "Resetting your password", "slug": "..." }],
"noMatches": false,
"matchedCount": 3
}

Retrieval is cross-lingual: the question is matched against every locale's content, and the answer is written in the requested locale (defaulting to the knowledge base's).

The answer is markdown: numbered steps, bold control names, and, when an article's screenshot shows the step being explained, that image's markdown copied from the article. Render it, don't print it.

For a conversation, pass the earlier turns in history (oldest first, up to 20 turns of { "role": "user" | "assistant", "content": "..." }, not including the current question). A follow-up such as "and on mobile?" is then answered in the light of what came before, and retrieval stays on the same subject. Without history every question stands alone, as before.

When nothing relevant is found, noMatches is true and answer is empty - surface that as "I don't know" rather than inventing one.

Reindex​

POST /v1/knowledge-base/reindex

Rebuilds search embeddings for every published article. Publishing indexes automatically, so this is only for after a bulk import or if search looks stale. It walks the whole knowledge base and can take a while.

Redirects​

GET /v1/knowledge-base/redirects · POST /v1/knowledge-base/redirects · DELETE /v1/knowledge-base/redirects/:id

Keep old links working after a slug change. Both paths start with /:

curl -X POST https://api.vidocu.ai/v1/knowledge-base/redirects \
-H "Authorization: Bearer vdo_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"fromPath": "/articles/old-slug", "toPath": "/articles/new-slug"}'

Analytics​

GET /v1/knowledge-base/analytics?days=30

Article counts by state, most-viewed articles, top searches, and searches that returned nothing - the last being the most direct signal of what your documentation is missing.

Offline export​

POST /v1/knowledge-base/exports · GET /v1/knowledge-base/exports · GET /v1/knowledge-base/exports/:id

A static copy of the help center as a ZIP, for customers who run documentation on their own network. It carries your branding, sections, articles, images, the exported video for each article and a client-side search box on every page. It opens from index.html with no server and also serves from any static host. AI Ask, reader feedback and quiz scoring need the live site and are left out.

curl -X POST https://api.vidocu.ai/v1/knowledge-base/exports \
-H "Authorization: Bearer $VIDOCU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"locales": ["en", "de"], "tagIds": ["tag_acme"], "includeVideos": true}'
  • locales - a subset of the enabled locales; the default locale is always included. An extra language only carries the articles that have a translation.
  • tagIds - restrict to tagged articles. Empty means every published, public article. tagMatch is any (default) or all.
  • notes - optional, up to 500 characters: what changed in this version. Kept on the export and in the bundle's manifest.json; never shown on a page.
  • includeVideos - default true. Each article's exported video is copied in. A video with no export, or one that changed since, is exported first through the normal export path and counted like any other export. A video that cannot be rendered in time is skipped and listed under skippedVideos with the reason; the article gets a "watch it online" link instead.

The build runs in the background: the response is 202 with the export record, and GET /v1/knowledge-base/exports/:id reports progress until status is completed or failed. One build runs at a time per knowledge base; a second POST returns 409.

Each export gets a version: builds with the same tag selection (or none) form a series, and every completed build in it is numbered one higher. The number is in the ZIP filename and in the footer of every page of the exported site, so a customer can tell you which copy they are holding.

A completed export carries a presigned downloadUrl (valid one hour; call the list or get endpoint again for a fresh one), sizeBytes, counts and skippedVideos. Every image an article shows is packaged, including ones hosted outside Vidocu (fetched over HTTPS from public hosts, up to 25 MB each); the few that cannot be fetched stay as online links and are listed in externalImages. The list marks an export stale: true when the articles, sections or translations it selected have changed since it was built.

Billing​

Article translation is charged to the workspace credit balance shown in GET /v1/usage: kb_article_translate credits per article per target language, on success only. GET /v1/usage/credits lists the current price. There is no limit on how many locales a knowledge base enables.

ask and reindex draw on the add-on's own monthly AI credit pool instead. Article reads, writes and analytics are free.

Errors​

StatusCodeCause
403limit_exceededThe Knowledge Center add-on isn't active on this workspace, or not enough credits to translate
400validation_errorBad parentId: not top-level, wrong knowledge base, or the section has sub-sections
404not_foundNo knowledge base yet, or the section/article/redirect doesn't exist
409conflictSlug taken, section not empty, a linked video is awaiting approval, or the body still has a [TODO: ...] placeholder
423lockedThe article is locked