> ## Documentation Index > Fetch the complete documentation index at: https://apidocs.document360.com/llms.txt > Use this file to discover all available pages before exploring further. # Update an article > Partial update — only fields included in the request body are modified; omitted fields remain unchanged. **Reorder within the same category:** send `order` (0-based position among sibling articles; lower numbers appear higher in the list). **Move to a different category:** send `category_id` (the destination category's id), optionally with `order` to position it there. Combine both to move and position in one request. **Content updates:** when `auto_fork` is true and the target version is published, a new draft version is automatically created and the update is applied to the draft. If `auto_fork` is false (default), attempting to update content on a published version returns a 422 error. Reorder/move-only changes (`order` / `category_id` without content) do not require a fork. ## OpenAPI ````json PATCH /v3/projects/{project_id}/articles/{article_id} { "openapi": "3.0.1", "info": { "title": "Document360 Customer API", "description": "> **⚠️ Beta:** Version 3 of the Document360 Customer API is currently in **Beta**. The contract (routes, request/response field names, status codes) may change without notice prior to GA. Pin to a specific revision and review release notes before upgrading.\n\nDocument360 RESTful APIs let you integrate your documentation with your software — onboard readers, manage articles, automate publishing, and more.\n\nFull reference: [API Documentation](https://apidocs.document360.io/docs).\n\n## Authentication\nV3 authenticates with an **API key** — not the `api_token` used by V1/V2. Create one in the Document360 portal under **Settings → Knowledge base portal → API keys**, then click **Create API key**. (Projects that still have the older screen show two tabs; choose **Enhanced keys (v3)**, or pick **API v3** from the Create menu.)\n\nSend it in the `X-API-Key` header:\n\n```\nX-API-Key: d360_sk_...\n```\n\nThe plaintext key is shown **once**, at creation — only its hash is stored, so a lost key must be replaced rather than recovered. Each key carries a fixed portal role, content role and content-access scope, supports an optional expiry, and can be disabled or deleted at any time from the same screen. Treat it as an opaque string: it is a pre-shared secret, not an OAuth 2.0 access token, and must not be parsed.\n\nFor flows that act on behalf of a specific person, V3 also accepts an OAuth 2.0 bearer token carrying the `customerApi` scope.\n\nAPI access is part of your subscription plan. If your plan does not include it, every endpoint returns `403` with the error code `FEATURE_NOT_IN_LICENSE` — including for keys created while an earlier plan was active. Contact your Customer Success Manager or `support@document360.com` to enable it.\n\n> **Note on terminology:** V1/V2 call these *API tokens* and manage them under **Settings → Knowledge base portal → API tokens**. V3 keys are a separate credential with their own screen, roles and scope — a V1/V2 token will not authenticate a V3 request.\n\n## Rate Limits\nAll endpoints are rate-limited per API key per project. Default limits vary by plan. When rate-limited, responses include `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. Use exponential backoff with jitter for optimal retry behavior.", "termsOfService": "https://document360.com/terms", "contact": { "name": "Document360 Support", "url": "https://document360.io/contact-us/", "email": "support@document360.com" }, "license": { "name": "Document360 API Terms of Use", "url": "https://document360.com/terms" }, "version": "3.0.0" }, "servers": [ { "url": "https://apihub.document360.io", "description": "Document 360 API Hub" }, { "url": "https://apihub.us.document360.io", "description": "Document360 API Hub - US data center" }, { "url": "https://apihub.{private_hosting}.document360.io", "description": "Private hosting - Please provide the subdomain name.", "variables": { "private_hosting": { "default": "domain", "description": "Sub domain for private hosting" } } } ], "security": [ { "ApiKey": [] }, { "Bearer": [ "customerApi" ] } ], "tags": [ { "name": "Projects", "description": "List and manage knowledge base projects. Export and import project documentation, and manage project-level site customization such as custom CSS and JavaScript." }, { "name": "Projects > Import & export", "description": "Start a project export or import and poll the resulting background operation for completion." }, { "name": "Workspaces", "description": "Manage workspaces and access workspace-scoped categories and articles." }, { "name": "Workspaces > AI", "description": "Query the workspace with Eddy AI and submit feedback on AI answers." }, { "name": "Articles", "description": "Create, read, update, and delete knowledge base articles and their settings, including bulk operations." }, { "name": "Articles > Publishing & workflow", "description": "Publish and unpublish articles (single and bulk) and update the article workflow status." }, { "name": "Articles > Versions", "description": "List, fork, and delete article versions." }, { "name": "Articles > Attachments", "description": "Manage the files attached to an article — list, upload, and delete article attachments." }, { "name": "Articles > Content drafts", "description": "(Internal) Stage AI-generated article content with an Accept/Reject review lifecycle — used by Eddy Copilot to preview content before committing to the live article body." }, { "name": "Articles > Labels", "description": "Read and set the labels associated with an article." }, { "name": "Categories", "description": "Manage the category hierarchy that organizes articles. Create, update, and delete categories and their settings, including bulk operations." }, { "name": "Categories > Page category", "description": "Read and update the content body of page-type categories, including bulk content updates." }, { "name": "Categories > Publishing & workflow", "description": "Publish and unpublish categories (single and bulk) and update the category workflow status." }, { "name": "Categories > Versions", "description": "List, fork, and delete category versions." }, { "name": "Categories > Labels", "description": "Read and set the labels associated with a category." }, { "name": "ArticleTemplates", "description": "Manage reusable article templates — per-language article skeletons that authors and AI tools (such as Eddy Copilot) can apply when starting a new article.", "x-displayName": "Article templates" }, { "name": "Content reuse > Glossaries", "description": "Manage glossary entries — terminology definitions referenced inside articles via merge codes (`{{glossary.name}}`) and rendered as tooltips on the public KB." }, { "name": "Content reuse > Snippets", "description": "Manage reusable snippets — markdown or WYSIWYG content fragments referenced across articles via merge codes (`{{snippet.name}}`)." }, { "name": "Content reuse > Variables", "description": "Manage reusable variables — short text values referenced across articles via merge codes (`{{variable.name}}`)." }, { "name": "AiWriterStyleGuides", "description": "Retrieve AI writer style guides configured for the project — voice / tone instructions Eddy Copilot follows when generating article content. Used to let users pick a style before kicking off generation.", "x-displayName": "AI writer style guides" }, { "name": "RedirectRules", "description": "Manage article-redirection rules — 301/302 redirects from old article URLs to new locations after renames or restructures.", "x-displayName": "Redirect rules" }, { "name": "Tags", "description": "Manage tags — labels used to classify articles, categories, and files for filtering and discovery." }, { "name": "Labels", "description": "Manage labels shown on the article list and category tree. (Associating labels with a specific article or category lives under Articles and Categories.)" }, { "name": "CustomFields", "description": "Retrieve the custom field definitions configured for the project. Custom field values are read and written through the article and category settings endpoints.", "x-displayName": "Custom fields" }, { "name": "ApiReferences", "description": "Import, resync, and publish OpenAPI/Swagger specifications for API reference documentation.", "x-displayName": "API references" }, { "name": "ApiReferences > Logs", "description": "Monitor API reference import logs and inspect individual log entries." }, { "name": "Drive", "description": "Search Drive and retrieve files associated with an article, and track Drive background tasks." }, { "name": "Drive > Folders", "description": "Create, read, update, and delete Drive folders." }, { "name": "Drive > Files", "description": "Upload, copy, move, delete, and tag Drive files." }, { "name": "Drive > Image drafts", "description": "(Internal) Stage uploaded or AI-generated images (committed to Drive and associated with a staged article draft) — used by Eddy Copilot." }, { "name": "Readers", "description": "Manage reader accounts, invitations, and access scopes for private documentation." }, { "name": "Readers > Groups", "description": "Create, read, update, and delete reader groups." }, { "name": "Users", "description": "Manage users, their portal and content roles, and consolidated (effective) permissions within a project." }, { "name": "Users > Groups", "description": "Manage user groups, their portal role, and consolidated (effective) group permissions." }, { "name": "Roles", "description": "Create, read, update, and delete portal and content roles, list the users assigned to a role, and retrieve the permission dependency matrix." }, { "name": "ContentAccess", "description": "Manage per-node content-access permissions — grant, allow, deny, and inheritance overrides for users and groups against specific versions, languages, categories, or articles, plus version/language site protection.", "x-displayName": "Content access" }, { "name": "ContentAccess > Eligibility", "description": "List the readers and groups eligible to be granted content access." }, { "name": "Languages", "description": "Retrieve configured languages for a workspace." }, { "name": "Translations", "description": "Query translation status of articles across languages." }, { "name": "Operations", "description": "Track asynchronous operations — long-running tasks (bulk updates, merges, exports) accepted with `202 Accepted` and an `Operation-Location` header. Poll until the operation reaches a terminal status, honouring the `Retry-After` header." }, { "name": "Analytics > Articles", "description": "View article performance metrics including views, top articles, reader locations, and category breakdowns." }, { "name": "Analytics > Search", "description": "View search usage metrics including top keywords, no-result queries, and bounced searches." }, { "name": "Analytics > AI search", "description": "View AI-powered search metrics including usage trends, most-referenced articles, and trending topics." }, { "name": "Analytics > Feedback", "description": "View reader feedback trends across articles, including top-performing articles and feedback keywords." } ], "externalDocs": { "description": "Document360 API Documentation", "url": "https://apidocs.document360.io/docs" }, "x-tagGroups": [ { "name": "Workspace", "tags": [ "Projects", "Projects > Import & export", "Workspaces", "Workspaces > AI" ] }, { "name": "Content", "tags": [ "Articles", "Articles > Publishing & workflow", "Articles > Versions", "Articles > Attachments", "Articles > Content drafts", "Articles > Labels", "Categories", "Categories > Page category", "Categories > Publishing & workflow", "Categories > Versions", "Categories > Labels", "ArticleTemplates", "AiWriterStyleGuides", "RedirectRules", "Tags", "Labels", "ApiReferences", "ApiReferences > Logs" ] }, { "name": "Content reuse", "tags": [ "Content reuse > Glossaries", "Content reuse > Snippets", "Content reuse > Variables" ] }, { "name": "Media", "tags": [ "Drive", "Drive > Folders", "Drive > Files", "Drive > Image drafts" ] }, { "name": "Access", "tags": [ "Readers", "Readers > Groups", "Users", "Users > Groups", "Roles", "ContentAccess", "ContentAccess > Eligibility" ] }, { "name": "Localization", "tags": [ "Languages", "Translations" ] }, { "name": "Operations", "tags": [ "Operations" ] }, { "name": "Analytics", "tags": [ "Analytics > Articles", "Analytics > Search", "Analytics > AI search", "Analytics > Feedback" ] } ], "paths": { "/v3/projects/{project_id}/articles/{article_id}": { "patch": { "tags": [ "Articles" ], "summary": "Update an article.", "description": "Partial update — only fields included in the request body are modified; omitted fields remain unchanged.\r\n \r\n**Reorder within the same category:** send `order` (0-based position among sibling articles;\r\nlower numbers appear higher in the list).\r\n \r\n**Move to a different category:** send `category_id` (the destination category's id),\r\noptionally with `order` to position it there. Combine both to move and position in one request.\r\n \r\n**Content updates:** when `auto_fork` is true and the target version is published, a new draft\r\nversion is automatically created and the update is applied to the draft. If `auto_fork` is false\r\n(default), attempting to update content on a published version returns a 422 error. Reorder/move-only\r\nchanges (`order` / `category_id` without content) do not require a fork.", "operationId": "updateArticle", "parameters": [ { "$ref": "#/components/parameters/ProjectId" }, { "$ref": "#/components/parameters/ArticleId" }, { "$ref": "#/components/parameters/LangCode" } ], "requestBody": { "description": "Updated article data.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateArticleRequest" }, "examples": { "Update title and content with auto-fork": { "summary": "Updates the article title and content. When auto_fork is true and the target version is published, a new draft version is created automatically.", "value": { "title": "Getting Started with Single Sign-On (Updated)", "content": "# Introduction\nThis updated guide walks you through configuring SSO for your organization.", "category_id": null, "hidden": null, "version_number": null, "translation_option": null, "source": null, "order": null, "auto_fork": true } }, "Reorder an article within its category": { "summary": "Moves the article to position 0 (top) among the other articles in its category, without changing content or category.", "value": { "title": null, "content": null, "category_id": null, "hidden": null, "version_number": null, "translation_option": null, "source": null, "order": 0, "auto_fork": null } }, "Move article to a different category": { "summary": "Moves the article to a new category and positions it there. Combine category_id with order to move and reorder in one request.", "value": { "title": null, "content": null, "category_id": "f4a5b6c7-d8e9-0a1b-2c3d-4e5f6a7b8c9d", "hidden": null, "version_number": null, "translation_option": null, "source": null, "order": 3, "auto_fork": null } }, "Update a specific version": { "summary": "Targets a specific draft version for the update. Use version_number to specify which version to update.", "value": { "title": "Getting Started with SSO - v3 Draft", "content": "# Introduction\nDraft version with additional sections.", "category_id": null, "hidden": null, "version_number": 3, "translation_option": null, "source": null, "order": null, "auto_fork": null } } } } } }, "responses": { "200": { "description": "Article updated successfully.", "headers": { "X-RateLimit-Limit": { "description": "The maximum number of requests allowed in the current time window for this request's bucket. Read requests (GET/HEAD) and write requests (POST/PUT/PATCH/DELETE) have independent limits, applied per caller (API key or user) per project.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Remaining": { "description": "The number of requests remaining in the current time window. When this reaches 0, subsequent requests receive a 429 response.", "schema": { "type": "integer", "format": "int32" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ArticleDetailResponseApiResponse" }, "examples": { "Article updated successfully": { "summary": "The article has been updated. If auto_fork was true and the version was published, the response shows the newly created draft version.", "value": { "data": { "id": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d", "title": "Getting Started with Single Sign-On (Updated)", "content": "# Introduction\nThis updated guide walks you through configuring SSO for your organization.", "html_content": null, "category_id": "f4a5b6c7-d8e9-0a1b-2c3d-4e5f6a7b8c9d", "workspace_id": "1c2d3e4f-5a6b-7c8d-9e0f-a1b2c3d4e5f6", "version_number": 3, "public_version": 2, "latest_version": 3, "enable_rtl": false, "hidden": false, "status": "draft", "version_type": null, "order": 0, "created_by": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "authors": [], "created_at": "2025-06-01T09:00:00Z", "modified_at": "2025-08-16T10:00:00Z", "modified_by": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "slug": "getting-started-with-single-sign-on", "is_fallback_content": false, "description": null, "category_type": null, "content_type": "markdown", "is_shared_article": false, "translation_option": "none", "url": null, "preview_url": null, "current_workflow_status_id": null, "lang_code": "en", "available_languages": null, "settings": null, "stale_status": null, "content_hash": "sha256:9f2c5a1d8e3b4c6f7a0918273645dacb5e1f2a3b4c5d6e7f8091a2b3c4d5e6f7" }, "success": true, "request_id": "req_abc123def456", "errors": null, "warnings": null } } } } } }, "401": { "description": "Authentication token is missing or invalid.", "headers": { "WWW-Authenticate": { "description": "Indicates the authentication scheme required. Returns `Bearer` with optional `error` and `error_description` parameters per RFC 6750.", "schema": { "type": "string" } } }, "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/unauthorized" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/unauthorized` for 401 responses." }, "status": { "enum": [ 401 ], "type": "integer", "description": "HTTP status code. Always `401` for this response.", "format": "int32" } } } ] }, "examples": { "Missing or invalid token": { "summary": "Authentication token is missing or invalid.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/unauthorized", "title": "Unauthorized.", "status": 401, "detail": "The authentication token is missing or has expired.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "UNAUTHORIZED", "message": "Bearer token is missing or invalid.", "field": null, "details": null } ], "warnings": null } } } } } }, "403": { "description": "Token lacks the required scope or content permission.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/forbidden" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/forbidden` for 403 responses." }, "status": { "enum": [ 403 ], "type": "integer", "description": "HTTP status code. Always `403` for this response.", "format": "int32" } } } ] }, "examples": { "Insufficient permissions": { "summary": "Insufficient permissions for this resource.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/forbidden", "title": "Forbidden.", "status": 403, "detail": "You do not have permission to perform this action.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "FORBIDDEN", "message": "Insufficient permissions for this project.", "field": null, "details": null } ], "warnings": null } } } } } }, "404": { "description": "Article not found.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/resource-not-found" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/resource-not-found` for 404 responses." }, "status": { "enum": [ 404 ], "type": "integer", "description": "HTTP status code. Always `404` for this response.", "format": "int32" } } } ] }, "examples": { "Resource not found": { "summary": "The requested resource was not found.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/resource-not-found", "title": "Not Found.", "status": 404, "detail": "The requested resource does not exist or has been deleted.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "RESOURCE_NOT_FOUND", "message": "The requested resource was not found.", "field": null, "details": null } ], "warnings": null } } } } } }, "422": { "description": "Validation failed.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/validation-error" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/validation-error` for 422 responses." }, "status": { "enum": [ 422 ], "type": "integer", "description": "HTTP status code. Always `422` for this response.", "format": "int32" } } } ] }, "examples": { "Validation failed": { "summary": "The request body contains invalid data.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/validation-error", "title": "Unprocessable Entity.", "status": 422, "detail": "One or more fields failed validation.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "VALIDATION_ERROR", "message": "This field is required.", "field": "title", "details": null } ], "warnings": null } } } } } }, "400": { "description": "The request body is malformed or contains invalid JSON.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/bad-request" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/bad-request` for 400 responses." }, "status": { "enum": [ 400 ], "type": "integer", "description": "HTTP status code. Always `400` for this response.", "format": "int32" } } } ] }, "examples": { "Malformed request body": { "summary": "The request body could not be parsed.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/bad-request", "title": "Bad Request.", "status": 400, "detail": "The request body is malformed or contains invalid JSON.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "BAD_REQUEST", "message": "Could not parse the request body. Ensure it is valid JSON.", "field": null, "details": null } ], "warnings": null } } } } } }, "429": { "description": "Rate limit exceeded. Retry after the duration specified in the Retry-After header.", "headers": { "Retry-After": { "description": "Number of seconds to wait before retrying the request. Use exponential backoff with jitter for optimal retry behavior.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Limit": { "description": "The maximum number of requests allowed in the current time window for this request's bucket. Read requests (GET/HEAD) and write requests (POST/PUT/PATCH/DELETE) have independent limits, applied per caller (API key or user) per project. Read limits are typically higher than write limits.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Remaining": { "description": "The number of requests remaining in the current time window. When this reaches 0, subsequent requests will receive a 429 response.", "schema": { "type": "integer", "format": "int32" } }, "X-RateLimit-Reset": { "description": "The UTC epoch timestamp (in seconds) when the current rate limit window resets.", "schema": { "type": "integer", "format": "int64" } } }, "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/too-many-requests" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/too-many-requests` for 429 responses." }, "status": { "enum": [ 429 ], "type": "integer", "description": "HTTP status code. Always `429` for this response.", "format": "int32" } } } ] }, "examples": { "Rate limit exceeded": { "summary": "Rate limit exceeded.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/too-many-requests", "title": "Too Many Requests.", "status": 429, "detail": "Rate limit exceeded. Retry after the duration specified in the Retry-After header.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "TOO_MANY_REQUESTS", "message": "Rate limit exceeded. Retry after the duration specified in the Retry-After header.", "field": null, "details": null } ], "warnings": null } } } } } }, "500": { "description": "An unexpected server error occurred.", "content": { "application/problem+json": { "schema": { "allOf": [ { "$ref": "#/components/schemas/V3ProblemDetails" }, { "type": "object", "properties": { "type": { "enum": [ "https://apidocs.document360.io/apidocs/errors/internal-server-error" ], "type": "string", "description": "RFC 7807 error type URI. Always `https://apidocs.document360.io/apidocs/errors/internal-server-error` for 500 responses." }, "status": { "enum": [ 500 ], "type": "integer", "description": "HTTP status code. Always `500` for this response.", "format": "int32" } } } ] }, "examples": { "Unexpected server error": { "summary": "Unexpected server error.", "value": { "type": "https://apidocs.document360.io/apidocs/errors/internal-server-error", "title": "Internal Server Error.", "status": 500, "detail": "An unexpected error occurred. Please try again or contact support.", "instance": null, "trace_id": "req_abc123def456", "errors": [ { "code": "INTERNAL_SERVER_ERROR", "message": "An unexpected error occurred.", "field": null, "details": null } ], "warnings": null } } } } } } }, "security": [ { "ApiKey": [] }, { "Bearer": [ "customerApi" ] } ] } } }, "components": { "parameters": { "ProjectId": { "name": "project_id", "in": "path", "description": "The unique identifier of the project. Retrieve project IDs from `GET /v3/projects`.", "required": true, "schema": { "type": "string", "format": "uuid", "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" } }, "ArticleId": { "name": "article_id", "in": "path", "description": "The unique identifier of the article. Retrieve article IDs from `GET /v3/projects/{projectId}/articles`.", "required": true, "schema": { "type": "string", "format": "uuid", "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" } }, "LangCode": { "name": "lang_code", "in": "query", "description": "ISO 639-1 language code (e.g., `en`, `fr`). Defaults to the project's primary language if omitted.", "schema": { "pattern": "^[a-z]{2}(-[A-Z]{2})?$", "type": "string", "format": "language-tag", "example": "en" } } }, "schemas": { "UpdateArticleRequest": { "type": "object", "properties": { "title": { "type": "string", "description": "The updated title of the article.", "nullable": true, "example": "Getting Started with Single Sign-On (Updated)" }, "content": { "type": "string", "description": "The updated body content of the article. Markdown for markdown articles; HTML for block or wysiwyg articles. For HTML content the server also derives and stores a markdown representation automatically.", "nullable": true, "example": "# Introduction\\nThis updated guide walks you through configuring SSO." }, "category_id": { "type": "string", "description": "The id of the category to move the article to. Combine with `order` to position it in the destination. Retrieve category IDs from `GET /v3/projects/{projectId}/categories`.", "nullable": true, "example": "f4a5b6c7-d8e9-0a1b-2c3d-4e5f6a7b8c9d" }, "hidden": { "type": "boolean", "description": "Whether the article should be hidden from readers.", "nullable": true, "example": false }, "version_number": { "type": "integer", "description": "The version number to apply the update to. Defaults to the latest version if omitted. When `auto_fork` is true and this version is published, the update is applied to the newly created draft version, not the original published version.", "format": "int32", "nullable": true, "example": 3 }, "translation_option": { "allOf": [ { "$ref": "#/components/schemas/TranslationOption" } ], "description": "The updated translation status of the article.", "nullable": true }, "source": { "type": "string", "description": "The source of the update. Common values: `API`, `import`, `portal`. When omitted, defaults to `API` for Customer API requests.", "nullable": true, "example": "API" }, "order": { "type": "integer", "description": "The updated 0-based display position within the category (lower = higher in the list).", "format": "int32", "nullable": true, "example": 5 }, "auto_fork": { "type": "boolean", "description": "When true and the target version is published, a new draft version is automatically forked and the update (including content and title) is applied to the new draft. When the target version is already a draft, no fork occurs and the update is applied directly. When false (default), updating content on a published version returns a 422 error.", "nullable": true, "example": true } }, "additionalProperties": false, "description": "Request to update an existing article." }, "ArticleDetailResponseApiResponse": { "required": [ "data", "request_id", "success" ], "type": "object", "properties": { "data": { "allOf": [ { "$ref": "#/components/schemas/ArticleDetailResponse" } ], "description": "Response data payload." }, "success": { "type": "boolean", "description": "Whether the API request was successful.", "readOnly": true }, "request_id": { "minLength": 1, "type": "string", "description": "Unique identifier for request tracing and correlation.", "readOnly": true }, "errors": { "type": "array", "items": { "$ref": "#/components/schemas/ApiError" }, "description": "List of errors if the request failed.", "nullable": true }, "warnings": { "type": "array", "items": { "$ref": "#/components/schemas/ApiWarning" }, "description": "List of non-fatal warnings from the request.", "nullable": true } }, "additionalProperties": false, "description": "Generic API response wrapper containing typed data." }, "V3ProblemDetails": { "required": [ "status", "title", "type" ], "type": "object", "properties": { "type": { "minLength": 1, "enum": [ "https://apidocs.document360.io/apidocs/errors/bad-request", "https://apidocs.document360.io/apidocs/errors/unauthorized", "https://apidocs.document360.io/apidocs/errors/forbidden", "https://apidocs.document360.io/apidocs/errors/resource-not-found", "https://apidocs.document360.io/apidocs/errors/conflict", "https://apidocs.document360.io/apidocs/errors/validation-error", "https://apidocs.document360.io/apidocs/errors/too-many-requests", "https://apidocs.document360.io/apidocs/errors/internal-server-error" ], "type": "string", "description": "URI reference identifying the error type (links to documentation)." }, "title": { "minLength": 1, "type": "string", "description": "Short human-readable summary of the error type." }, "status": { "enum": [ 400, 401, 403, 404, 409, 422, 429, 500 ], "type": "integer", "description": "HTTP status code.", "format": "int32" }, "detail": { "type": "string", "description": "Human-readable explanation specific to this occurrence.", "nullable": true }, "instance": { "type": "string", "description": "URI of the request that generated the error.", "nullable": true }, "trace_id": { "type": "string", "description": "Request trace identifier for correlation.", "nullable": true }, "errors": { "type": "array", "items": { "$ref": "#/components/schemas/ApiError" }, "description": "Structured list of specific errors (extension field).", "nullable": true }, "warnings": { "type": "array", "items": { "$ref": "#/components/schemas/ApiWarning" }, "description": "Non-fatal warnings (extension field).", "nullable": true } }, "additionalProperties": false, "description": "RFC 7807 Problem Details response for V3 API errors.\r\nContent-Type: application/problem+json" }, "TranslationOption": { "enum": [ "none", "needTranslation", "translated", "inProgress" ], "type": "string", "description": "The translation status of an article.", "x-enumNames": [ "None", "NeedTranslation", "Translated", "InProgress" ], "x-enum-varnames": [ "None", "NeedTranslation", "Translated", "InProgress" ], "x-ms-enum": { "name": "TranslationOption", "modelAsString": true } }, "ArticleDetailResponse": { "required": [ "created_at", "enable_rtl", "hidden", "id", "is_fallback_content", "is_shared_article", "latest_version", "modified_at", "order", "status", "title", "version_number" ], "type": "object", "properties": { "id": { "type": "string", "description": "The unique identifier of the article.", "format": "uuid", "nullable": true, "readOnly": true, "example": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d" }, "title": { "type": "string", "description": "The title of the article.", "nullable": true, "example": "Getting Started with Single Sign-On" }, "content": { "type": "string", "description": "The raw content of the article in its source format.", "nullable": true, "example": "# Introduction\\nThis guide walks you through configuring SSO for your organization." }, "html_content": { "type": "string", "description": "The rendered HTML content of the article.", "nullable": true, "example": "
This guide walks you through configuring SSO.
" }, "category_id": { "type": "string", "description": "The identifier of the category this article belongs to. Use this value with `GET /v3/projects/{projectId}/categories/{categoryId}` to retrieve category details.", "nullable": true, "example": "f4a5b6c7-d8e9-0a1b-2c3d-4e5f6a7b8c9d" }, "workspace_id": { "type": "string", "description": "The workspace this article belongs to. Corresponds to a workspace from `GET /v3/projects/{projectId}/workspaces`.", "nullable": true, "example": "1c2d3e4f-5a6b-7c8d-9e0f-a1b2c3d4e5f6" }, "version_number": { "type": "integer", "description": "The current version number of the article.", "format": "int32", "example": 3 }, "public_version": { "type": "integer", "description": "The latest published version number, or null if the article has never been published.", "format": "int32", "nullable": true, "readOnly": true, "example": 2 }, "latest_version": { "type": "integer", "description": "The latest version number including drafts.", "format": "int32", "readOnly": true, "example": 3 }, "enable_rtl": { "type": "boolean", "description": "Whether right-to-left text direction is enabled.", "example": false }, "hidden": { "type": "boolean", "description": "Whether the article is hidden from readers.", "example": false }, "status": { "enum": [ "draft", "published", "unpublished" ], "type": "string", "allOf": [ { "$ref": "#/components/schemas/ArticleStatus" } ], "description": "The publication status of the article.", "x-enumNames": [ "Draft", "Published", "Unpublished" ], "x-enum-varnames": [ "Draft", "Published", "Unpublished" ], "x-ms-enum": { "name": "ArticleStatus", "modelAsString": true } }, "version_type": { "type": "string", "description": "Derived version discriminator (version-less collab): `draft`, `published`, or `system_backup`. Null when collaborative editing is not enabled for the project. Display/system-backup ordinals are exposed only by the versions-list endpoint (they are relative to the full version set).", "nullable": true, "readOnly": true, "example": "draft" }, "order": { "type": "integer", "description": "The display order of the article within its category.", "format": "int32", "example": 5 }, "created_by": { "type": "string", "description": "The user ID of the original article creator. For full author details including name and avatar, see the `authors` array. Corresponds to a user from `GET /v3/projects/{projectId}/users`.", "nullable": true, "example": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d" }, "authors": { "type": "array", "items": { "$ref": "#/components/schemas/AuthorResponse" }, "description": "The list of contributors to this article, including the original creator and any subsequent editors.", "nullable": true }, "created_at": { "type": "string", "description": "The date and time the article was created.", "format": "date-time", "readOnly": true, "example": "2025-06-01T09:00:00Z" }, "modified_at": { "type": "string", "description": "The date and time the article was last modified.", "format": "date-time", "readOnly": true, "example": "2025-08-15T14:30:00Z" }, "modified_by": { "type": "string", "description": "The user ID of who last modified this article. Corresponds to a user from `GET /v3/projects/{projectId}/users`.", "nullable": true, "readOnly": true, "example": "b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e" }, "slug": { "type": "string", "description": "The URL slug for the article.", "nullable": true, "readOnly": true, "example": "getting-started-with-single-sign-on" }, "is_fallback_content": { "type": "boolean", "description": "Whether the content is a fallback from the default language.", "example": false }, "description": { "type": "string", "description": "A brief description of the article.", "nullable": true, "example": "Learn how to configure single sign-on authentication for your knowledge base." }, "category_type": { "allOf": [ { "$ref": "#/components/schemas/CategoryType" } ], "description": "The type of category this article belongs to.", "nullable": true }, "content_type": { "allOf": [ { "$ref": "#/components/schemas/ContentType" } ], "description": "The editor content type of the article.", "nullable": true }, "is_shared_article": { "type": "boolean", "description": "Whether the article is shared across multiple projects.", "example": false }, "translation_option": { "enum": [ "none", "needTranslation", "translated", "inProgress" ], "type": "string", "allOf": [ { "$ref": "#/components/schemas/TranslationOption" } ], "description": "The translation status of the article.", "x-enumNames": [ "None", "NeedTranslation", "Translated", "InProgress" ], "x-enum-varnames": [ "None", "NeedTranslation", "Translated", "InProgress" ], "x-ms-enum": { "name": "TranslationOption", "modelAsString": true } }, "url": { "type": "string", "description": "Canonical public URL of the article on the knowledge base site. Deterministic from the slug\r\nand always returned, but only resolves to live content once the article is published; a\r\ndraft-only article returns 404 (or redirects to login) at this URL until first publish.\r\nUse `preview_url` to view unpublished content.", "format": "uri", "nullable": true, "example": "https://docs.example.com/v1/docs/en/getting-started-with-single-sign-on" }, "preview_url": { "type": "string", "description": "Preview URL for the current version of the article on the knowledge base site. Works before\r\npublishing; anonymous readers are redirected to login. For an already-published article it\r\nrenders the requested version's content. Empty for legacy KB v1 projects.", "nullable": true, "example": "https://docs.example.com/v1/docs/en/9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d?isPreview=true&versionNumber=3" }, "current_workflow_status_id": { "type": "string", "description": "The current workflow status identifier. Retrieve available statuses from `GET /v3/projects/{projectId}/workflow-statuses`.", "nullable": true, "example": "b7e2a1d4-3f56-4c89-9d0e-1a2b3c4d5e6f" }, "lang_code": { "type": "string", "description": "The language code of this article version.", "nullable": true, "example": "en" }, "available_languages": { "type": "array", "items": { "$ref": "#/components/schemas/AvailableLanguage" }, "description": "The list of languages this article is available in.", "nullable": true }, "settings": { "allOf": [ { "$ref": "#/components/schemas/ArticleDocumentSettings" } ], "description": "A subset of article settings (SEO title, description, external search exclusion). For full settings including tags, related articles, and display options, use `GET /v3/projects/{projectId}/articles/{articleId}/settings`.", "nullable": true }, "stale_status": { "allOf": [ { "$ref": "#/components/schemas/StaleStatusResponse" } ], "description": "The content freshness status of the article: whether it is up to date or due for review, along with the review schedule. Mirrors the `stale_status` exposed by the category endpoints.", "nullable": true }, "content_hash": { "type": "string", "description": "SHA-256 hash of this version's stored content, prefixed with the algorithm\r\n(`sha256:`). Changes only when the content itself changes — record it at write\r\ntime and compare against listing or detail reads to detect drift without downloading\r\ncontent. `null` for versions saved before hash tracking was introduced.", "nullable": true, "readOnly": true, "example": "sha256:9f2c5a1d8e3b4c6f7a0918273645dacb5e1f2a3b4c5d6e7f8091a2b3c4d5e6f7" }, "custom_fields_data": { "type": "object", "additionalProperties": { "nullable": true }, "description": "Read-only map of custom field name → display value for this article (dropdown /\r\nmulti-select values are resolved to option labels; unset fields are `null`).\r\nPopulated on the single-article and article-version GET endpoints only. Named to match the\r\nV1/V2 `customFieldsData` map, and kept distinct from the `custom_fields` array on\r\nthe settings endpoints, which is where raw values are read and written.\r\nAbsent from the response entirely for article types that have no custom-field surface (for\r\nexample custom pages); an empty map means custom fields apply but none are configured.", "nullable": true, "readOnly": true } }, "additionalProperties": false, "description": "Full article detail with content." }, "ApiError": { "required": [ "code", "message" ], "type": "object", "properties": { "code": { "minLength": 1, "enum": [ "BAD_REQUEST", "CONFLICT", "FEATURE_NOT_IN_LICENSE", "FORBIDDEN", "INTERNAL_SERVER_ERROR", "LICENSE_LIMIT_EXCEEDED", "OPERATION_FAILED", "PREMIUM_FEATURE_NOT_IN_LICENSE", "RESOURCE_NOT_FOUND", "TOO_MANY_REQUESTS", "UNAUTHORIZED", "UNRECOGNIZED_FIELD", "UNRECOGNIZED_FIELDS", "VALIDATION_ERROR" ], "type": "string", "description": "Machine-readable error code (e.g. VALIDATION_ERROR, RESOURCE_NOT_FOUND)." }, "message": { "minLength": 1, "type": "string", "description": "Human-readable error message." }, "field": { "type": "string", "description": "The request field that caused the error, if applicable.", "nullable": true }, "details": { "type": "string", "description": "Additional context about the error.", "nullable": true } }, "additionalProperties": false, "description": "Represents an error returned by the API." }, "ApiWarning": { "required": [ "code", "message" ], "type": "object", "properties": { "code": { "minLength": 1, "type": "string", "description": "Machine-readable warning code." }, "message": { "minLength": 1, "type": "string", "description": "Human-readable warning message." } }, "additionalProperties": false, "description": "Represents a non-fatal warning from the API." }, "ArticleStatus": { "enum": [ "draft", "published", "unpublished" ], "type": "string", "description": "The publication status of an article.", "x-enumNames": [ "Draft", "Published", "Unpublished" ], "x-enum-varnames": [ "Draft", "Published", "Unpublished" ], "x-ms-enum": { "name": "ArticleStatus", "modelAsString": true } }, "AuthorResponse": { "required": [ "email", "id" ], "type": "object", "properties": { "id": { "type": "string", "description": "The unique identifier of the author.", "format": "uuid", "nullable": true, "readOnly": true, "example": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d" }, "first_name": { "type": "string", "description": "The first name of the author.", "nullable": true, "example": "Jane" }, "last_name": { "type": "string", "description": "The last name of the author.", "nullable": true, "example": "Doe" }, "email": { "type": "string", "description": "The email address of the author.", "format": "email", "nullable": true, "example": "jane.doe@example.com" }, "profile_logo_url": { "type": "string", "description": "The URL of the author's profile image.", "nullable": true, "example": "https://cdn.example.com/avatars/jane-doe.png" } }, "additionalProperties": false, "description": "Author information for an article." }, "CategoryType": { "enum": [ "folder", "page", "index" ], "type": "string", "description": "The type of category an article belongs to.", "x-enumNames": [ "Folder", "Page", "Index" ], "x-enum-varnames": [ "Folder", "Page", "Index" ], "x-ms-enum": { "name": "CategoryType", "modelAsString": true } }, "ContentType": { "enum": [ "markdown", "wysiwyg", "block" ], "type": "string", "description": "The editor content type used for an article.", "x-enumNames": [ "Markdown", "Wysiwyg", "Block" ], "x-enum-varnames": [ "Markdown", "Wysiwyg", "Block" ], "x-ms-enum": { "name": "ContentType", "modelAsString": true } }, "AvailableLanguage": { "type": "object", "properties": { "lang_code": { "type": "string", "description": "The language code (e.g., \"en\", \"fr\").", "nullable": true, "example": "fr" }, "url": { "type": "string", "description": "The URL of the article in this language.", "format": "uri", "nullable": true, "example": "https://docs.example.com/fr/articles/getting-started-with-single-sign-on" }, "translation_status": { "enum": [ "none", "needTranslation", "translated", "inProgress" ], "type": "string", "allOf": [ { "$ref": "#/components/schemas/TranslationOption" } ], "description": "The translation status for this language.", "x-enumNames": [ "None", "NeedTranslation", "Translated", "InProgress" ], "x-enum-varnames": [ "None", "NeedTranslation", "Translated", "InProgress" ], "x-ms-enum": { "name": "TranslationOption", "modelAsString": true } } }, "additionalProperties": false, "description": "Represents a language in which an article is available." }, "ArticleDocumentSettings": { "type": "object", "properties": { "seo_title": { "type": "string", "description": "The custom SEO title for search engines.", "nullable": true, "example": "SSO Setup Guide - Product Documentation" }, "description": { "type": "string", "description": "The meta description for search engines.", "nullable": true, "example": "Step-by-step instructions for configuring single sign-on with SAML or OIDC providers." }, "exclude_from_external_search": { "type": "boolean", "description": "Whether the article is excluded from external search engine indexing.", "example": false } }, "additionalProperties": false, "description": "SEO and display settings embedded within an article detail." }, "StaleStatusResponse": { "required": [ "expire_days", "is_from_document_settings" ], "type": "object", "properties": { "article_stale_status": { "enum": [ "fresh", "stale" ], "type": "string", "allOf": [ { "$ref": "#/components/schemas/ArticleStaleStatus" } ], "description": "Whether the article is up to date (fresh) or requires review (stale).", "x-enumNames": [ "Fresh", "Stale" ], "x-enum-varnames": [ "Fresh", "Stale" ], "x-ms-enum": { "name": "ArticleStaleStatus", "modelAsString": true } }, "stale_reason": { "type": "string", "description": "The reason the article was marked stale, if any.", "nullable": true }, "expired_at": { "type": "string", "description": "The date the article is next due for review (UTC). Once this date passes, the article is considered stale.", "format": "date-time", "nullable": true, "example": "2026-01-31T05:10:26Z" }, "is_from_document_settings": { "type": "boolean", "description": "Whether the review schedule was inherited from project-level documentation settings.", "example": false }, "expire_days": { "type": "integer", "description": "The number of days from publication after which the article should next be reviewed.", "format": "int32", "example": 30 } }, "additionalProperties": false, "description": "Content freshness information for an article, derived from its review reminder schedule." }, "ArticleStaleStatus": { "enum": [ "fresh", "stale" ], "type": "string", "description": "The content freshness classification of an article, derived from its review reminder schedule.", "x-enumNames": [ "Fresh", "Stale" ], "x-enum-varnames": [ "Fresh", "Stale" ], "x-ms-enum": { "name": "ArticleStaleStatus", "modelAsString": true } } }, "securitySchemes": { "ApiKey": { "type": "apiKey", "description": "API key for machine-to-machine integrations, webhooks, and CI/CD (recommended). This is a static, pre-shared secret - treat it as an opaque string and do not parse it; it is an API key, not an OAuth 2.0 access token. Create one in the portal under **Settings > Knowledge base portal > API keys** using the **Create API key** button - this is a different credential from the V1/V2 *API token*, which lives on its own screen and will not authenticate a V3 request. The plaintext key (format `d360_sk_...`) is shown only once, at creation. Send it in the `X-API-Key` header: `X-API-Key: d360_sk_...`. Each key carries a fixed portal role, content role, and content-access scope, supports an optional expiry, and can be disabled or deleted at any time from the portal.", "name": "X-API-Key", "in": "header" }, "Bearer": { "type": "oauth2", "description": "All V3 endpoints require a Bearer token. Generate tokens in the Document360 portal under **Settings > API Tokens**. Tokens are project-scoped, require the `customerApi` scope, and do not expire by default. Tokens can be revoked at any time from the portal. Include the token in every request: `Authorization: Bearer