Articles

Help articles, each belonging to one category.

Service: help.articles

Help articles, each belonging to one category. The article body is Markdown source (body, pg.Text). is_public controls visibility: an article is a draft until explicitly published. priority drives the curated position on the help index and the sidebar — list sort is (-priority, -updated_at): larger priority sorts higher; priority=0 (the default) means “no manual priority” and the article sorts by -updated_at relative to other priority=0 rows.

Fields

Field Type Required Description
id string Primary key. Read-only, auto-generated
slug string URL slug; unique across all articles
category_id string Parent category id. Deleting the category cascades to its articles
title string Article title (default-locale / English)
description string One-sentence summary shown on the category card / article header. Default ""
body string Article body as Markdown source
is_public boolean Whether the article is publicly visible (draft until explicitly published). Default false
priority integer Curated priority on the help index and the sidebar (larger = higher). 0 (default) = no manual priority, sorts by -updated_at. List sort: -priority, -updated_at
created_at string ISO 8601 creation timestamp. Read-only
updated_at string ISO 8601 last update timestamp. Read-only
article_translation ArticleTranslation[] Per-locale overlays. Inline — edited on the parent form, not a top-level service
article_author ArticleAuthor[] Authors attributed for editorial history. Admin-only — not exposed publicly

Filterable: category_id · is_public

ArticleTranslation

Inline overlay edited on the Article form, not published as a top-level service. Translatable text fields are nullable so a partial translation keeps the base value where the overlay is NULL (NULL = inherit base); is_public is non-nullable (each locale is published independently). UniqueConstraint(["source_id", "language_id"]) — one translation per (article, language). language_id FK → common_language with on_delete=PROTECT: deleting a language still referenced by translations is blocked.

Field Type Required Description
id string Primary key. Read-only. Omit when creating
source_id string Parent article id. Read-only, auto-set from the parent
language_id string Language of this translation (FK → common_language)
title string? Translated article title. null = inherit base
description string? Translated article description. null = inherit base
body string? Translated article body as Markdown. null = inherit base
is_public boolean Whether this translation is publicly visible (draft until explicitly published). Default false
created_at string ISO 8601 creation timestamp. Read-only
updated_at string ISO 8601 last update timestamp. Read-only

ArticleAuthor

Junction table: article ↔ user (author), edited inline on the Article form as an “Authors” block. Kept for editorial history and admin attribution — not exposed publicly (the help-web frontend does not list authors on articles). Mirrors blog.PostAuthor in shape and on-delete semantics.

Field Type Required Description
id string Primary key. Read-only. Omit when creating
article_id string Parent article id. Read-only, auto-set from the parent
user_id string Author (FK → accounts_user)

UniqueConstraint(["article_id", "user_id"]) — an author can be attributed to an article at most once. Both sides cascade: deleting the article or the user removes the junction row.

find

List articles with filtering, sorting, and pagination. Default list sort is (-priority, -updated_at); pass sort to override.

HTTP

Request

GET /v1/help/articles?filter={"category_id":{"$eq":"1920438291700"},"is_public":{"$eq":true}}&fields=id,slug,category_id,title,description,is_public,priority,updated_at&sort=[{"field":"priority","dir":"desc"},{"field":"updated_at","dir":"desc"}]&limit=100
Authorization: Bearer <token>

Response

{
  "items": [
    {
      "id": "1920438292100",
      "slug": "install-enbox",
      "category_id": "1920438291700",
      "title": "Install Enbox on your devices",
      "description": "Desktop, mobile, and browser extensions.",
      "is_public": true,
      "priority": 10,
      "updated_at": "2026-05-13T18:30:00Z"
    },
    {
      "id": "1920438292110",
      "slug": "first-note",
      "category_id": "1920438291700",
      "title": "Create your first note",
      "description": "Capture, edit, and find notes.",
      "is_public": true,
      "priority": 0,
      "updated_at": "2026-05-10T12:00:00Z"
    }
  ],
  "total": 2
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.find",
  "params": {
    "filter": {
      "category_id": { "$eq": "1920438291700" },
      "is_public": { "$eq": true }
    },
    "fields": [
      "id", "slug", "category_id", "title", "description",
      "is_public", "priority", "updated_at"
    ],
    "sort": [
      { "field": "priority", "dir": "desc" },
      { "field": "updated_at", "dir": "desc" }
    ],
    "limit": 100
  },
  "id": 10
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "items": [
      {
        "id": "1920438292100",
        "slug": "install-enbox",
        "category_id": "1920438291700",
        "title": "Install Enbox on your devices",
        "description": "Desktop, mobile, and browser extensions.",
        "is_public": true,
        "priority": 10,
        "updated_at": "2026-05-13T18:30:00Z"
      },
      {
        "id": "1920438292110",
        "slug": "first-note",
        "category_id": "1920438291700",
        "title": "Create your first note",
        "description": "Capture, edit, and find notes.",
        "is_public": true,
        "priority": 0,
        "updated_at": "2026-05-10T12:00:00Z"
      }
    ],
    "total": 2
  },
  "id": 10
}

findOneById

Fetch a single article with full detail, including the Markdown body and translations.

HTTP

Request

GET /v1/help/articles/1920438292100?fields=id,slug,category_id,title,description,body,is_public,priority,created_at,updated_at,article_translation.id,article_translation.language_id,article_translation.title,article_translation.description,article_translation.body,article_translation.is_public
Authorization: Bearer <token>

Response

{
  "id": "1920438292100",
  "slug": "install-enbox",
  "category_id": "1920438291700",
  "title": "Install Enbox on your devices",
  "description": "Desktop, mobile, and browser extensions.",
  "body": "## Desktop\n\nDownload the installer for [macOS](...) or [Windows](...).\n\n## Mobile\n\nGet Enbox from the App Store or Google Play.",
  "is_public": true,
  "priority": 10,
  "created_at": "2026-05-01T10:30:00Z",
  "updated_at": "2026-05-13T18:30:00Z",
  "article_translation": [
    {
      "id": "1920438292120",
      "language_id": "1920438290002",
      "title": "Installer Enbox sur vos appareils",
      "description": null,
      "body": "## Bureau\n\nTéléchargez l'installateur pour [macOS](...) ou [Windows](...).",
      "is_public": false
    }
  ]
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.findOneById",
  "params": {
    "id": "1920438292100",
    "fields": [
      "id", "slug", "category_id", "title", "description", "body",
      "is_public", "priority", "created_at", "updated_at",
      "article_translation.id", "article_translation.language_id",
      "article_translation.title", "article_translation.description",
      "article_translation.body", "article_translation.is_public"
    ]
  },
  "id": 11
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438292100",
    "slug": "install-enbox",
    "category_id": "1920438291700",
    "title": "Install Enbox on your devices",
    "description": "Desktop, mobile, and browser extensions.",
    "body": "## Desktop\n\nDownload the installer for [macOS](...) or [Windows](...).\n\n## Mobile\n\nGet Enbox from the App Store or Google Play.",
    "is_public": true,
    "priority": 10,
    "created_at": "2026-05-01T10:30:00Z",
    "updated_at": "2026-05-13T18:30:00Z",
    "article_translation": [
      {
        "id": "1920438292120",
        "language_id": "1920438290002",
        "title": "Installer Enbox sur vos appareils",
        "description": null,
        "body": "## Bureau\n\nTéléchargez l'installateur pour [macOS](...) ou [Windows](...).",
        "is_public": false
      }
    ]
  },
  "id": 11
}

create

Create a new article. The server assigns id, created_at, and updated_at. New articles default to is_public: false (draft) and priority: 0.

HTTP

Request

POST /v1/help/articles
Authorization: Bearer <token>
Content-Type: application/json

{
  "document": {
    "slug": "reset-password",
    "category_id": "1920438291730",
    "title": "Reset your password",
    "description": "Recover access when you forget your password.",
    "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email and follow the link.",
    "is_public": false,
    "priority": 5,
    "article_translation": [
      {
        "language_id": "1920438290002",
        "title": "Réinitialiser votre mot de passe",
        "is_public": false
      }
    ]
  }
}

Response

{
  "id": "1920438292130",
  "slug": "reset-password",
  "title": "Reset your password",
  "is_public": false,
  "priority": 5,
  "created_at": "2026-05-13T18:30:00Z"
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.create",
  "params": {
    "document": {
      "slug": "reset-password",
      "category_id": "1920438291730",
      "title": "Reset your password",
      "description": "Recover access when you forget your password.",
      "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email and follow the link.",
      "is_public": false,
      "priority": 5,
      "article_translation": [
        {
          "language_id": "1920438290002",
          "title": "Réinitialiser votre mot de passe",
          "is_public": false
        }
      ]
    }
  },
  "id": 12
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438292130",
    "slug": "reset-password",
    "title": "Reset your password",
    "is_public": false,
    "priority": 5,
    "created_at": "2026-05-13T18:30:00Z"
  },
  "id": 12
}

updateOneById

Partial update — only specified fields change; omitted fields are untouched. Nested collections (article_translation, article_author) are updated automatically: items with id are updated, items without id are created, items missing from the array are deleted.

HTTP

Request

PATCH /v1/help/articles/1920438292130
Authorization: Bearer <token>
Content-Type: application/json

{
  "document": {
    "is_public": true,
    "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email.\n4. Check your inbox for the reset link.",
    "priority": 15
  }
}

Response

{
  "id": "1920438292130",
  "is_public": true,
  "priority": 15,
  "updated_at": "2026-05-13T18:45:00Z"
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.updateOneById",
  "params": {
    "id": "1920438292130",
    "document": {
      "is_public": true,
      "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email.\n4. Check your inbox for the reset link.",
      "priority": 15
    }
  },
  "id": 13
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438292130",
    "is_public": true,
    "priority": 15,
    "updated_at": "2026-05-13T18:45:00Z"
  },
  "id": 13
}

replaceOneById

Full replacement — all writable fields must be provided. Unlike updateOneById, omitted fields are reset to defaults. Nested collections (article_translation, article_author) are not accepted in this method — use updateOneById to modify them.

HTTP

Request

PUT /v1/help/articles/1920438292130
Authorization: Bearer <token>
Content-Type: application/json

{
  "document": {
    "slug": "reset-password",
    "category_id": "1920438291730",
    "title": "Reset your password",
    "description": "Recover access when you forget your password.",
    "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email and follow the link.",
    "is_public": true,
    "priority": 15
  }
}

Response

{
  "id": "1920438292130",
  "slug": "reset-password",
  "category_id": "1920438291730",
  "title": "Reset your password",
  "description": "Recover access when you forget your password.",
  "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email and follow the link.",
  "is_public": true,
  "priority": 15
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.replaceOneById",
  "params": {
    "id": "1920438292130",
    "document": {
      "slug": "reset-password",
      "category_id": "1920438291730",
      "title": "Reset your password",
      "description": "Recover access when you forget your password.",
      "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email and follow the link.",
      "is_public": true,
      "priority": 15
    }
  },
  "id": 14
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438292130",
    "slug": "reset-password",
    "category_id": "1920438291730",
    "title": "Reset your password",
    "description": "Recover access when you forget your password.",
    "body": "## How to reset\n\n1. Go to the sign-in page.\n2. Click **Forgot password?**.\n3. Enter your email and follow the link.",
    "is_public": true,
    "priority": 15
  },
  "id": 14
}

update

Batch update multiple articles in a single transaction (all-or-nothing). See Batch operations.

HTTP

Request

PATCH /v1/help/articles
Authorization: Bearer <token>
Content-Type: application/json

{
  "items": [
    { "id": "1920438292100", "document": { "priority": 20 } },
    { "id": "1920438292110", "document": { "is_public": true } }
  ]
}

Response

{
  "affected": 2,
  "items": [
    { "id": "1920438292100", "priority": 20 },
    { "id": "1920438292110", "is_public": true }
  ]
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.update",
  "params": {
    "items": [
      { "id": "1920438292100", "document": { "priority": 20 } },
      { "id": "1920438292110", "document": { "is_public": true } }
    ]
  },
  "id": 15
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "affected": 2,
    "items": [
      { "id": "1920438292100", "priority": 20 },
      { "id": "1920438292110", "is_public": true }
    ]
  },
  "id": 15
}

deleteOneById

Delete an article. All translations and author attributions belonging to it are deleted with it (cascade).

HTTP

Request

DELETE /v1/help/articles/1920438292130
Authorization: Bearer <token>

Response

{
  "id": "1920438292130",
  "slug": "reset-password",
  "title": "Reset your password"
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.deleteOneById",
  "params": {
    "id": "1920438292130"
  },
  "id": 16
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "id": "1920438292130",
    "slug": "reset-password",
    "title": "Reset your password"
  },
  "id": 16
}

delete

Batch delete articles in a single transaction (all-or-nothing). See Batch operations.

HTTP

Request

DELETE /v1/help/articles
Authorization: Bearer <token>
Content-Type: application/json

{
  "items": [
    { "id": "1920438292100" },
    { "id": "1920438292110" }
  ]
}

Response

{
  "affected": 2,
  "items": [
    { "id": "1920438292100" },
    { "id": "1920438292110" }
  ]
}

WebSocket

Request

{
  "jsonrpc": "2.0",
  "method": "help.articles.delete",
  "params": {
    "items": [
      { "id": "1920438292100" },
      { "id": "1920438292110" }
    ]
  },
  "id": 17
}

Response

{
  "jsonrpc": "2.0",
  "result": {
    "affected": 2,
    "items": [
      { "id": "1920438292100" },
      { "id": "1920438292110" }
    ]
  },
  "id": 17
}