--- title: "Create and publish an article" slug: "create-and-publish-an-article" description: "Automate article creation and publishing in Document360 using the v3 API. Learn to manage drafts and confirm live status efficiently." updated: 2026-09-05T17:54:02Z published: 2026-09-05T17:54:02Z canonical: "apidocs.document360.com/create-and-publish-an-article" --- > ## 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. # Create and publish an article Creating and publishing articles is a common task in Document360. Using the v3 API you can automate this end to end: create a draft article, then publish a specific version. ## Before you begin You need an API key and four IDs before you start. | Value | How to retrieve it | | --- | --- | | API key | Create one in **Settings › Knowledge base portal › API keys**, then send it as the `X-API-Key` header on every request. See *Generating an API key*. | | `project_id` | `GET /v3/projects` — copy the `id` of the project you want to use. | | `workspace_id` | `GET /v3/projects/{project_id}/workspaces` — copy the `id` of the workspace. | | `category_id` | `GET /v3/projects/{project_id}/workspaces/{workspace_id}/categories` — copy the `id` of the target category. | | `user_id` | `GET /v3/projects/{project_id}/users` — copy the `id` of the user the content is created as. Required when authenticating with an API key. | Note These examples use `https://apihub.document360.io`, the default API host. If your project is on a regional or privately hosted deployment, replace it with your own host. ## Step 1 - Create the article Send a `POST` request to the articles endpoint. The article is created in draft status. This requires the **Update articles** permission. ``` POST /v3/projects/{project_id}/articles ``` ``` curl --request POST "https://apihub.document360.io/v3/projects/{project_id}/articles" \ --header "X-API-Key: YOUR_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "title": "Getting Started with Single Sign-On", "content": "# Introduction\nThis guide walks you through configuring SSO.", "category_id": "f4a5b6c7-d8e9-0a1b-2c3d-4e5f6a7b8c9d", "workspace_id": "1c2d3e4f-5a6b-7c8d-9e0f-a1b2c3d4e5f6", "user_id": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "content_type": "markdown", "slug": "getting-started-with-single-sign-on" }' ``` | Field | Required | Description | | --- | --- | --- | | `title` | Yes | The article title. | | `category_id` | Yes | The category the article belongs to. | | `workspace_id` | Yes | The workspace the article belongs to. | | `user_id` | Yes, with an API key | The user the article is created as. Required when authenticating with `X-API-Key`, which identifies a token rather than a person. Ignored when using a user access token (OAuth) — the identity comes from the token. | | `content` | No | The article body. **Markdown** when `content_type` is `markdown` or omitted; **HTML** when `content_type` is `block` or `wysiwyg`. | | `content_type` | No | The editor format for `content`: `markdown` (default), `block`, or `wysiwyg`. | | `order` | No | Position of the article within the category. Omit, or send `0`, to add it at the **end** of the category. Send `1` or higher to place it at a specific position. The response returns the position actually assigned. | | `slug` | No | URL slug. Generated from the title if omitted. | A successful request returns `201 Created`, with a `Location` header pointing at the new article. Copy `data.id` and `data.version_number` — you need them to publish. ``` { "success": true, "request_id": "req_abc123def456", "data": { "id": "9a3b4c5d-6e7f-8a9b-0c1d-2e3f4a5b6c7d", "title": "Getting Started with Single Sign-On", "category_id": "f4a5b6c7-d8e9-0a1b-2c3d-4e5f6a7b8c9d", "workspace_id": "1c2d3e4f-5a6b-7c8d-9e0f-a1b2c3d4e5f6", "version_number": 1, "public_version": null, "latest_version": 1, "status": "draft", "order": 7, "slug": "getting-started-with-single-sign-on", "content_type": "markdown", "created_at": "2025-08-15T14:30:00Z" } } ``` Note When `content_type` is `block`, `content` must not be empty — an empty body returns `422`. Important You must have content access to the target location. If your content role does not grant access to the specified workspace and parent category, the request returns `403` with the error code `FORBIDDEN`. **If the request fails** | Status | Likely cause | | --- | --- | | `403` | Your content role does not grant access to the target workspace and category, or your role lacks the **Update articles** permission. | | `422` | A required field is missing or invalid. Most common: `user_id` omitted while using an API key, or `content` empty while `content_type` is `block`. | | `429` | Rate limit exceeded. Retry after the interval given in the `Retry-After` response header. See *Understanding rate limiting*. | ## Step 2 - Publish the article Publish a specific version of the article, making it visible to readers. This requires the **Publish articles** permission. ``` POST /v3/projects/{project_id}/articles/{article_id}/publish ``` ``` curl --request POST "https://apihub.document360.io/v3/projects/{project_id}/articles/{article_id}/publish" \ --header "X-API-Key: YOUR_API_KEY" \ --header "Content-Type: application/json" \ --data '{ "workspace_id": "1c2d3e4f-5a6b-7c8d-9e0f-a1b2c3d4e5f6", "user_id": "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d", "version_number": 1, "message": "Publishing SSO guide after technical review" }' ``` | Field | Required | Description | | --- | --- | --- | | `workspace_id` | Yes | The workspace containing the article. | | `version_number` | Yes | The version to publish. Must reference an existing **draft** version of the article. Publishing it supersedes whichever version is currently published. | | `user_id` | Yes, with an API key | The user performing the publish. Required when authenticating with `X-API-Key`. Ignored when using a user access token (OAuth). | | `message` | Conditional | A short note describing the publish action. Required when the project's published workflow status is configured to require a comment; optional otherwise. | A successful publish returns `200 OK`: ``` { "success": true, "request_id": "req_abc123def456" } ``` **If the request fails** | Status | Likely cause | | --- | --- | | `403` | Your role lacks the **Publish articles** permission, or your content role does not grant access to the article. | | `404` | No article with that `article_id` exists in the project. | | `422` | `version_number` does not reference an existing draft version, `user_id` is missing while using an API key, or `message` is empty in a project that requires a publish comment. | | `429` | Rate limit exceeded. Retry after the interval given in the `Retry-After` response header. | Tip To publish many articles at once, see *Create and publish multiple articles*. ## Step 3 - Confirm the article is live The publish response confirms only that the call was accepted — it returns no article state. Fetch the article to confirm the result. ``` GET /v3/projects/{project_id}/articles/{article_id} ``` In the response, check that: - `public_version` is no longer `null` and matches the `version_number` you published. - `status` is no longer `draft`. The article is now visible to readers at its slug in the published workspace. ## Related - [Create and publish multiple articles](/create-and-publish-multiple-articles.md) - [Create an article](/create-an-article.md) - [Making your first request](/making-your-first-request.md)