Use Cases Ask AI Pricing Embeds AI Skill MCP Server Sign in Get Started
Home

API Documentation

Use the REST API to programmatically manage your helpdesk content

Connecting Claude, ChatGPT, Cursor or another AI agent? Use the MCP server instead — it exposes the knowledge base as tools with OAuth or a personal token.

Contents
Quick Start

Create a category and article in 3 steps

const API_KEY = "hdh_your_api_key";
const BASE_URL = "https://helpdesky.io/api/v1";

const headers = {
  "X-API-Key": API_KEY,
  "Content-Type": "application/json",
};

// 1. Create a category
const catRes = await fetch(`${BASE_URL}/categories`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    name: "Getting Started",
    description: "Help users get set up",
  }),
});
const { data: category } = await catRes.json();

// 2. Create an article in that category
const articleRes = await fetch(`${BASE_URL}/articles`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    title: "How to create your first project",
    content: "## Welcome\n\nFollow these steps to get started:\n\n1. Click **New Project**\n2. Enter a name\n3. Choose a template\n\nThat's it!",
    categoryId: category.id,
    published: true,
  }),
});
const { data: article } = await articleRes.json();
console.log("Created:", article.title);
Authentication

All API requests require an API key sent via the X-API-Key header. Generate your API key from your dashboard under Settings > API Integration.

curl -X GET https://helpdesky.io/api/v1/categories \
  -H "X-API-Key: hdh_your_api_key_here"

Base URL: https://helpdesky.io/api/v1

Content Type: application/json

Categories

CRUD operations for organising articles into categories

GET /api/v1/categories

List all categories in your helpdesk

curl -X GET https://helpdesky.io/api/v1/categories \
  -H "X-API-Key: hdh_your_api_key"
{
  "data": [
    {
      "id": "abc123",
      "name": "Getting Started",
      "slug": "getting-started",
      "description": "Introductory guides",
      "icon": "folder",
      "order": 0
    }
  ]
}
POST /api/v1/categories

Create a new category

{
  "name": "Getting Started",      // required
  "description": "Intro guides",  // optional
  "icon": "book-open"              // optional, default: "folder"
}
curl -X POST https://helpdesky.io/api/v1/categories \
  -H "X-API-Key: hdh_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Getting Started", "description": "Introductory guides"}'
{
  "data": {
    "id": "abc123",
    "name": "Getting Started",
    "slug": "getting-started",
    "description": "Introductory guides",
    "icon": "folder",
    "order": 0
  }
}
PATCH /api/v1/categories/:id

Update a category. Only include the fields you want to change.

{
  "name": "New Name",             // optional
  "description": "Updated desc",  // optional
  "icon": "star",                  // optional
  "order": 1                       // optional
}
curl -X PATCH https://helpdesky.io/api/v1/categories/CATEGORY_ID \
  -H "X-API-Key: hdh_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Updated Name"}'
{
  "data": {
    "id": "abc123",
    "name": "Updated Name",
    "slug": "getting-started",
    "description": "Introductory guides",
    "icon": "folder",
    "order": 0
  }
}
DELETE /api/v1/categories/:id

Delete a category and all its articles

curl -X DELETE https://helpdesky.io/api/v1/categories/CATEGORY_ID \
  -H "X-API-Key: hdh_your_api_key"
{
  "data": {
    "success": true
  }
}
Articles

CRUD operations for managing helpdesk articles

GET /api/v1/articles

List all articles. Optionally filter by category using the categoryId query parameter.

curl -X GET "https://helpdesky.io/api/v1/articles?categoryId=CATEGORY_ID" \
  -H "X-API-Key: hdh_your_api_key"
{
  "data": [
    {
      "id": "def456",
      "title": "How to reset your password",
      "slug": "how-to-reset-your-password",
      "content": "## Steps\n\n1. Click **Forgot Password**...",
      "excerpt": "Learn how to reset your password",
      "published": true,
      "categoryId": "abc123",
      "order": 0
    }
  ]
}
POST /api/v1/articles

Create a new article. Content should be in Markdown format.

{
  "title": "How to reset your password",  // required
  "content": "## Steps\n\n1. Click...",    // required, Markdown
  "slug": "reset-password",                // optional, auto-generated from title if omitted
  "excerpt": "Short summary",               // optional
  "categoryId": "abc123",                   // optional
  "published": true,                         // optional, default: false
  "numberHeadings": true,                    // optional, default: false — numbers H2 headings
  "hiddenFromWidget": false                  // optional, default: false — hide article from the embedded widget while keeping it on the public help center
}
curl -X POST https://helpdesky.io/api/v1/articles \
  -H "X-API-Key: hdh_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "How to reset your password",
    "content": "## Steps\n\n1. Click **Forgot Password**\n2. Enter your email\n3. Check your inbox",
    "categoryId": "CATEGORY_ID",
    "published": true
  }'
{
  "data": {
    "id": "def456",
    "title": "How to reset your password",
    "slug": "reset-password",
    "content": "## Steps\n\n1. Click **Forgot Password**...",
    "excerpt": null,
    "published": true,
    "numberHeadings": false,
    "hiddenFromWidget": false,
    "categoryId": "abc123",
    "order": 0
  }
}
PATCH /api/v1/articles/:id

Update an article. Only include the fields you want to change. Changing the slug on a published article automatically creates a 301 redirect from the old URL.

{
  "title": "Updated Title",       // optional
  "content": "New content...",     // optional, Markdown
  "slug": "new-url-handle",       // optional — auto-redirect created for published articles
  "excerpt": "Updated summary",   // optional
  "categoryId": "abc123",         // optional
  "published": true,               // optional
  "numberHeadings": true,          // optional — numbers H2 headings
  "hiddenFromWidget": true,        // optional — hide from the embedded widget while keeping it on the public help center
  "order": 2                       // optional
}
curl -X PATCH https://helpdesky.io/api/v1/articles/ARTICLE_ID \
  -H "X-API-Key: hdh_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"published": true}'
{
  "data": {
    "id": "def456",
    "title": "How to reset your password",
    "slug": "new-url-handle",
    "content": "## Steps...",
    "published": true,
    "numberHeadings": true,
    "categoryId": "abc123",
    "order": 0
  }
}
DELETE /api/v1/articles/:id

Delete an article permanently

curl -X DELETE https://helpdesky.io/api/v1/articles/ARTICLE_ID \
  -H "X-API-Key: hdh_your_api_key"
{
  "data": {
    "success": true
  }
}
Images

Upload images to use in article content

POST /api/v1/images

Upload an image file. Returns a URL you can embed in article content using HTML or Markdown (see format options below). Maximum file size is 5MB. Allowed types: JPG, PNG, GIF, WebP, SVG.

Send a multipart/form-data request with the image in a field named image.

curl -X POST https://helpdesky.io/api/v1/images \
  -H "X-API-Key: hdh_your_api_key" \
  -F "image=@/path/to/screenshot.png"
{
  "url": "/api/images/uploads/USER_ID/abc123-screenshot.png",
  "filename": "screenshot.png",
  "size": 245760,
  "contentType": "image/png"
}
Using images in articles

Use the returned url in your article content. There are two formats you can use:

Use an HTML <img> tag to control the display size with width and/or height attributes. This is the same format used by the dashboard visual editor.

<img src="/api/images/uploads/USER_ID/abc123-screenshot.png" width="400" />

Standard Markdown image syntax. Simple, but the image always displays at its full original size — there is no way to set dimensions.

![Screenshot](/api/images/uploads/USER_ID/abc123-screenshot.png)

Tip: We recommend HTML for most use cases — it gives you full control over how large images appear in your articles. Both formats are supported anywhere in your article content and can be mixed freely.

Error Responses

All errors return a JSON object with an error field:

{
  "error": "Missing X-API-Key header"
}
{
  "error": "Title is required"
}
{
  "error": "Category not found"
}
{
  "error": "Rate limit exceeded. Please try again later."
}
{
  "error": "Internal server error"
}
Markdown Formatting

Supported syntax for article content

Article content uses Markdown. In addition to standard syntax (headings, bold, italic, lists, links, images, code blocks), we support:

Rendering articles on your own site? The Content API returns this syntax already rendered as HTML, together with a stylesheet for it — you never need to render the Markdown yourself.

Use pipe syntax to create tables. The first row becomes the header.

| Feature     | Status    | Notes             |
| ----------- | --------- | ----------------- |
| Markdown    | Supported | Full GFM syntax   |
| Tables      | Supported | Auto-styled       |
| Callouts    | Supported | info/warning/danger |

Highlight important information with callout blocks using blockquote syntax:

> [!info]
> This is an informational note.

> [!warning]
> Be careful with this setting.

> [!danger]
> This action cannot be undone.

Paste a YouTube URL on its own line to auto-embed the video:

https://www.youtube.com/watch?v=VIDEO_ID

Turn any link into a call-to-action button by adding {.btn} (filled) or {.btn-outline} (outline) after the link. By default the link opens in a new tab with rel="noopener". Add .same-tab to open in the same tab, and .nofollow to add rel="nofollow".

[Start free trial](https://helpdesky.io/register){.btn}

[Read the docs](https://helpdesky.io/api-docs){.btn-outline .same-tab}

[Sponsored partner](https://example.com){.btn .nofollow}

Action cards are tinted, icon-led tiles that link somewhere. Add {.card} after a link and optionally set icon and desc. Place two or more cards on adjacent lines and they automatically lay out as a responsive 2-column grid (single column on mobile). Add sameTab to open in the same tab and nofollow to add rel="nofollow"; both default to off.

[Install the widget](/help/getting-started){.card icon="rocket" desc="Add Helpdesky to your site in two minutes."}

[Invite your team](/dashboard/staff){.card icon="user-plus" desc="Give teammates access to the inbox."}

Supported icon values:

user-plus play book-open rocket settings mail download link info check sparkles code

desc is optional. Unknown icon names fall back to info.

Content API

Public, read-only endpoints for headless help centers: fetch published articles as finished HTML and render them inside your own site

Use the Content API when help articles should appear inside your own website templates (your header, footer and CSS) instead of on the hosted help center. Your site fetches an article and injects the returned HTML; a standalone stylesheet reproduces callouts, buttons, action cards, code blocks, numbered headings, embeds, images and tables exactly as the hosted page shows them. The same model Ghost and Contentful use.

Building it with an AI coding agent? Point it at the self-contained spec helpdesky.io/docs/headless.md: endpoints, the HTML vocabulary, the URL structure to serve, SEO and redirect rules, and acceptance criteria.

Authentication

None. The Content API is unauthenticated and only ever returns published content. Address a help center by its slug — the last segment of its Helpdesky address (https://helpdesky.io/help/{helpdesk-slug}, shown under Settings → Domain & Address).

Caching, CORS and rate limits

Every response carries Access-Control-Allow-Origin: * (preflight OPTIONS answers 204), Cache-Control: public, max-age=60 and an ETag — send If-None-Match to get 304. Requests are limited to 120 per minute per visitor IP (RateLimit-* headers, 429 when exceeded); when you fetch from your server, forward the visitor's address in X-Forwarded-For so the limit and the view analytics apply per visitor rather than to your server. Cache responses on your side for at least 60 seconds.

The helpdesk object

Every JSON response starts with the same helpdesk object: name and slug, url (the help center's live canonical base URL: your headless address once saved, otherwise its subfolder address, custom domain or Helpdesky address), and the help center's SEO title and SEO description from the dashboard's Settings page, metaTitle and metaDescription (null when unset). Use the SEO fields for your help index page: <title> = metaTitle (fall back to "name Help Center") plus your site name, <meta name="description"> = metaDescription, and the WebSite/CollectionPage JSON-LD — the same values the hosted home page uses. Title category pages "category name - site name" with the category description. The spec's SEO section also covers the BreadcrumbList structured data, the help-center section of your /llms.txt (index plus every published article as - [title](url): excerpt, grouped by category, built from the categories endpoint) and why the sitemap and llms file belong at the root of your origin (/sitemap.xml, /llms.txt).

GET /api/content/v1/helpdesk/{helpdesk-slug}/articles/{article-slug}

One published article as rendered HTML, with heading IDs, table of contents, category, author display info and its live canonical URL. Returns 404 for drafts and unknown slugs, 410 for deleted articles. Each fetch counts as an article view (bot user agents excluded).

curl "https://helpdesky.io/api/content/v1/helpdesk/acme/articles/getting-started"
{
  "helpdesk": {
    "name": "Acme Help", "slug": "acme",
    "url": "https://help.acme.com",             // live canonical base URL of the help center
    "metaTitle": "Acme Help Center: guides and FAQs",                     // SEO title from Settings (null when unset) — title your index page with it
    "metaDescription": "Answers about billing, workspaces and integrations."   // SEO description (null when unset) — your index page's meta description
  },
  "title": "Getting started",
  "slug": "getting-started",                 // current slug — differs from the request when the article was renamed (301 to it)
  "excerpt": "Set up your workspace in five minutes.",
  "url": "https://help.acme.com/getting-started",   // live canonical URL of the hosted article
  "html": "<p>Welcome…</p>\n<h2 id=\"create-a-workspace\">Create a workspace</h2>…",   // all URLs absolute: uploads on helpdesky.io, internal links under helpdesk.url
  "toc": [ { "id": "create-a-workspace", "text": "Create a workspace", "level": 2 } ],
  "numberHeadings": false,                   // true when the hosted page numbers the H2 headings
  "category": { "name": "Basics", "slug": "basics", "url": "https://help.acme.com/category/basics" },
  "showAuthors": true,                       // respects the help center's show-author setting and per-article override
  "organizationAsAuthor": false,             // true when the organisation is credited instead of people
  "authors": [ { "name": "Jane Doe", "avatarUrl": "https://…/jane.png", "url": "https://help.acme.com/author/jane-doe" } ],
  "createdAt": "2026-03-02T10:15:00.000Z",
  "updatedAt": "2026-09-12T08:40:12.000Z"
}
GET /api/content/v1/helpdesk/{helpdesk-slug}/categories

Categories in display order, each with its published articles (lean: title, slug, excerpt, timestamps, canonical URL — no bodies), plus the uncategorized articles. Each category carries its canonical URL.

{
  "helpdesk": { "name": "Acme Help", "slug": "acme", "url": "https://help.acme.com", "metaTitle": "Acme Help Center: guides and FAQs", "metaDescription": "Answers about billing, workspaces and integrations." },
  "categories": [
    {
      "name": "Basics",
      "slug": "basics",
      "description": "First steps",
      "icon": "rocket",
      "url": "https://help.acme.com/category/basics",
      "articles": [
        {
          "title": "Getting started",
          "slug": "getting-started",
          "excerpt": "Set up your workspace in five minutes.",
          "url": "https://help.acme.com/getting-started",
          "createdAt": "2026-03-02T10:15:00.000Z",
          "updatedAt": "2026-09-12T08:40:12.000Z"
        }
      ]
    }
  ],
  "uncategorized": []
}
GET /api/content/v1/helpdesk/{helpdesk-slug}/articles

Flat list of every published article in the same lean shape, each with its category (null when uncategorised). Use it to build routes, sitemaps and the help-center section of your llms.txt.

{
  "helpdesk": { "name": "Acme Help", "slug": "acme", "url": "https://help.acme.com", "metaTitle": "Acme Help Center: guides and FAQs", "metaDescription": "Answers about billing, workspaces and integrations." },
  "articles": [
    {
      "title": "Getting started",
      "slug": "getting-started",
      "excerpt": "Set up your workspace in five minutes.",
      "url": "https://help.acme.com/getting-started",
      "createdAt": "2026-03-02T10:15:00.000Z",
      "updatedAt": "2026-09-12T08:40:12.000Z",
      "category": { "name": "Basics", "slug": "basics", "url": "https://help.acme.com/category/basics" }
    }
  ]
}
GET /api/content/v1/helpdesk/{helpdesk-slug}/search?q={text}

Published articles whose title or body matches q (case-insensitive), in the same lean shape as the flat list plus the echoed query. q is required (400 when missing).

curl "https://helpdesky.io/api/content/v1/helpdesk/acme/search?q=workspace"
GET /api/content/v1/article.css

Standalone stylesheet for the article HTML (text/css, CORS, cached for a day with stale-while-revalidate and an ETag). Every rule is scoped under one wrapper class, .helpdesky-article, so it cannot leak into your page. Add numbered-headings to the wrapper when numberHeadings is true. Theme it with CSS custom properties set on :root or the wrapper: --helpdesky-accent (default #3b82f6), --helpdesky-text (#1f2937), --helpdesky-font (Inter stack) and --helpdesky-radius (14px). The headless spec lists the optional ones (accent gradient, code background).

<link rel="stylesheet" href="https://helpdesky.io/api/content/v1/article.css">
<style>:root { --helpdesky-accent: #e11d48; --helpdesky-font: "Source Sans 3", system-ui, sans-serif; }</style>

<h1 id="title"></h1>
<article id="body" class="helpdesky-article"></article>

<script>
  const res = await fetch("https://helpdesky.io/api/content/v1/helpdesk/acme/articles/getting-started");
  if (res.status === 404) showNotFound();
  const article = await res.json();
  document.title = article.title;
  document.getElementById("title").textContent = article.title;
  const body = document.getElementById("body");
  body.classList.toggle("numbered-headings", article.numberHeadings);
  body.innerHTML = article.html;   // finished HTML — do not run it through Markdown or a class-stripping sanitiser
</script>

The html is the output of the Markdown syntax documented above (callouts become <div class="callout callout-info">, buttons <a class="btn-block btn-block-primary"> (consecutive ones inside a <div class="btn-row">), action cards <div class="action-card-grid"> of a.action-card, YouTube links <div class="video-embed"><iframe>, headings carry ids matching toc). It is sanitised for your origin with an allowlist (no scripts, styles, event handlers, javascript: URLs, forms, or iframes other than YouTube embeds), so inject it as is. Every URL in it is absolute, so the HTML works on your origin: uploaded images point at https://helpdesky.io/api/images/… (allow that host in your img-src policy, never rewrite them to your host) and links to other articles are resolved beneath the help center's live address, so they start with helpdesk.url + "/" — replace that prefix with your own base path to keep visitors on your site. For server-side rendering, the URL structure a headless site should serve, SEO and redirect rules, read the headless spec.

Notes

Slugs

Slugs are auto-generated from the article title or category name if not provided, but you can set a custom slug when creating an article or category via POST. You can also change the slug later with PATCH. If you change the slug on a published article, a 301 redirect is automatically created from the old URL to the new one.

Public URLs

Article slugs determine the public URL of each article. The URL format depends on whether you use a custom domain:

Path-based (default): https://helpdesky.io/help/{helpdesk-slug}/{article-slug}

Custom domain: https://{your-domain}/{article-slug}

On custom domains, articles are served directly at the root. The /articles path (without a slug) is a separate page that lists all published articles.

Pagination

The API does not currently paginate results. All categories and articles are returned in a single response.

Filtering

Use the categoryId query parameter on GET /api/v1/articles to filter articles by category.

Rate Limiting

The API is limited to 60 requests per minute per API key. If you exceed this limit, you'll receive a 429 status code. Rate limit headers (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset) are included in every response.

Category Icons

The following icon values are available for categories:

folder file-text book-open lightbulb help-circle settings zap users

The default icon is folder if none is specified.