Update an article (status needs article.publish; restricted visibility needs a paid plan)
:::openapi-operation PATCH /api/v1/workspaces/{workspace}/articles/{slug} AUTH bearerAuth :::
Changes only the fields in the body. Send edits for exact text replacements, or markdown for a full body, not both. base_revision makes the call fail with 409 stale_revision when the article changed. A markdown body that removes more than half of the text and more than 2,000 characters returns 422 large_removal unless allow_shrink is true. A status change to a published status places an unplaced article in the sidebar, and the response then has placed true. When the org reviews changes from agent keys, a change to a published article, or a status that publishes a draft, answers 202 with the proposal that holds it. A change that makes the pending proposal of the key unneeded withdraws it, and withdrawnProposal names that proposal.
:::codesamples open Code examples
curl -X PATCH "https://contextowl.co/api/v1/workspaces/string/articles/string" \
-H "Accept: application/json" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
--data '{
"allow_shrink": true,
"base_revision": "string",
"edits": [
{
"new": "string",
"old": "string"
}
],
"markdown": "string",
"note": "string",
"section": "string",
"status": "string",
"title": "string",
"visibility": "string"
}'
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://contextowl.co/api/v1/workspaces/string/articles/string"))
.header("Accept", "application/json")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.method("PATCH", HttpRequest.BodyPublishers.ofString("{\n \"allow_shrink\": true,\n \"base_revision\": \"string\",\n \"edits\": [\n {\n \"new\": \"string\",\n \"old\": \"string\"\n }\n ],\n \"markdown\": \"string\",\n \"note\": \"string\",\n \"section\": \"string\",\n \"status\": \"string\",\n \"title\": \"string\",\n \"visibility\": \"string\"\n}"))
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}
import urllib.request
url = "https://contextowl.co/api/v1/workspaces/string/articles/string"
headers = {
"Accept": "application/json",
"Authorization": "Bearer <token>",
"Content-Type": "application/json",
}
data = "{\n \"allow_shrink\": true,\n \"base_revision\": \"string\",\n \"edits\": [\n {\n \"new\": \"string\",\n \"old\": \"string\"\n }\n ],\n \"markdown\": \"string\",\n \"note\": \"string\",\n \"section\": \"string\",\n \"status\": \"string\",\n \"title\": \"string\",\n \"visibility\": \"string\"\n}".encode("utf-8")
req = urllib.request.Request(url, data=data, headers=headers, method="PATCH")
with urllib.request.urlopen(req) as res:
print(res.status)
print(res.read().decode())
const response = await fetch("https://contextowl.co/api/v1/workspaces/string/articles/string", {
method: "PATCH",
headers: {
"Accept": "application/json",
"Authorization": "Bearer <token>",
"Content-Type": "application/json",
},
body: JSON.stringify({
"allow_shrink": true,
"base_revision": "string",
"edits": [
{
"new": "string",
"old": "string"
}
],
"markdown": "string",
"note": "string",
"section": "string",
"status": "string",
"title": "string",
"visibility": "string"
})
});
console.log(response.status);
console.log(await response.json());
package main
import (
"fmt"
"io"
"net/http"
"strings"
"time"
)
func main() {
body := strings.NewReader(`{
"allow_shrink": true,
"base_revision": "string",
"edits": [
{
"new": "string",
"old": "string"
}
],
"markdown": "string",
"note": "string",
"section": "string",
"status": "string",
"title": "string",
"visibility": "string"
}`)
req, err := http.NewRequest("PATCH", "https://contextowl.co/api/v1/workspaces/string/articles/string", body)
if err != nil {
panic(err)
}
req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer <token>")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
res, err := client.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
bodyBytes, err := io.ReadAll(io.LimitReader(res.Body, 10<<20))
if err != nil {
panic(err)
}
fmt.Println(res.StatusCode)
fmt.Println(string(bodyBytes))
}
// cargo add reqwest --features blocking
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::blocking::Client::new();
let response = client.request(reqwest::Method::PATCH, "https://contextowl.co/api/v1/workspaces/string/articles/string")
.header("Accept", "application/json")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"allow_shrink\": true,\n \"base_revision\": \"string\",\n \"edits\": [\n {\n \"new\": \"string\",\n \"old\": \"string\"\n }\n ],\n \"markdown\": \"string\",\n \"note\": \"string\",\n \"section\": \"string\",\n \"status\": \"string\",\n \"title\": \"string\",\n \"visibility\": \"string\"\n}")
.send()?;
println!("{}", response.status());
println!("{}", response.text()?);
Ok(())
}
:::
:::details open Parameters (2)
| NAME | IN | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|---|
workspace |
path | string | yes | Workspace id, or - for the key's bound workspace |
slug |
path | string | yes | - |
:::
:::details open Request body (required)
application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
allow_shrink |
boolean | no | Accept a markdown body that removes more than half of the text and more than 2,000 characters. The default is false. |
base_revision |
string | no | Revision of the article that the change starts from, as getArticle returns it. A different current revision returns 409 stale_revision. |
edits |
array items: ArticleEdit object | no | 1 to 20 exact text replacements, applied in order. Do not send them together with markdown. |
markdown |
string | no | New body in Markdown. It replaces the whole body. Do not send it together with edits. |
note |
string | no | Note for the reviewer when the org reviews agent changes: what the change does and why. A direct write ignores it. |
section |
string | no | New display label of the section. It does not move the article in the sidebar. |
status |
string | no | DRAFT, IN REVIEW, BETA, STABLE or DEPRECATED. A status change needs the article.publish permission. |
title |
string | no | New title |
visibility |
string | no | public, internal or private. internal and private need a paid plan or an active trial. |
{
"allow_shrink": true,
"base_revision": "string",
"edits": [
{
"new": "string",
"old": "string"
}
],
"markdown": "string",
"note": "string",
"section": "string",
"status": "string",
"title": "string",
"visibility": "string"
}
:::
:::details open Success responses (200, 202)
200
Success
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
changed |
array items: string | yes | - |
nav |
string | yes | - |
placed |
boolean | no | - |
previousRevision |
string | yes | - |
revision |
string | yes | - |
slug |
string | yes | - |
status |
string | yes | - |
updatedAt |
string (date-time) | yes | - |
url |
string | yes | - |
withdrawnProposal |
WithdrawnProposal object | no | - |
{
"changed": [
"string"
],
"nav": "string",
"placed": true,
"previousRevision": "string",
"revision": "string",
"slug": "string",
"status": "string",
"updatedAt": "string",
"url": "string",
"withdrawnProposal": {
"id": 0,
"reason": "string"
}
}
202
The org reviews changes from agent keys, so the change waits in a proposal for an editor. Nothing changed for readers.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
message |
string | yes | What happened and what to tell the user |
pendingReview |
boolean | yes | True: the org reviews changes from agent keys, so the change waits in a proposal for an editor. Nothing changed for readers. |
proposal |
ReviewProposal object | yes | - |
{
"message": "string",
"pendingReview": true,
"proposal": {
"baseRevision": "string",
"entryId": 0,
"id": 0,
"objectType": "string",
"outcome": "string",
"reviewUrl": "string",
"slug": "string",
"status": "string",
"summary": "string",
"target": "string"
}
}
:::
:::details Error responses (400, 401, 402, 403, 404, 409, 422, 429, 500)
400
invalid_request: a parameter or the body is not valid.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
401
unauthorized: the access key is missing, invalid or expired.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
402
upgrade_required: internal and private visibility need a paid plan or an active trial.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
403
permission_denied: the key lacks the permission. two_factor_required: the org requires admins to turn on two-factor authentication.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
404
not_found: the object does not exist, or the key cannot reach it.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
409
stale_revision: base_revision does not match the current content. openapi_generated: detach the generated page before you edit it.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
422
encrypted, edit_not_found, edit_ambiguous or large_removal: the change does not apply to the current text. When the edits apply to the pending proposal of the key, details.proposalId names it.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
429
rate_limited: too many requests. Wait for the time in the Retry-After header.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
500
internal: the server failed.
Content-Type: application/json
| FIELD | TYPE | REQUIRED | DESCRIPTION |
|---|---|---|---|
error |
object | yes | - |
{
"error": {
"code": "string",
"details": null,
"message": "string",
"status": 0
}
}
:::