A change set is a release: it groups staged edits to live articles, articles that are not published yet and hidden categories, and publishes them in one action, now or at a time you choose. Use it to put your documentation live together with the feature it describes.
API keys only. The change-set endpoints accept an apikey header and nothing else. An OAuth access token is refused with 401 and {"status": "error", "message": "Unknown API key."}. Change sets are part of the Catalyst plan, in early preview, like staged changes.
Endpoints
Method and path | What it does | Key |
|---|---|---|
| List change sets | Any |
| Create a change set | Read & write |
| Get a change set with its items and readiness | Any |
| Rename a change set or change its description | Read & write |
| Delete a change set | Read & write |
| Add articles and categories | Read & write |
| Remove an item | Read & write |
| Publish now, or schedule | Read & write |
| Cancel a schedule | Read & write |
| Follow a publish run | Any |
A Read only key on a write answers 403 with insufficient_scope. Every change-set request, reads and status polls included, counts toward the 120 writes a minute of your key (see Rate limits).
How a release works
Prepare the work: stage edits to live articles (see Staged changes and versions), write new articles as drafts, and keep a new category hidden.
Create a change set and add those articles and categories. Each becomes one or more items.
Check its
readiness: blockers stop a publish, warnings never do.Publish it, or schedule it. The answer is
202 Accepted: the run happens after the response.Poll the status until
doneistrue, then check what applied.
A change set only points at work that exists on its own. Removing an item or deleting a change set never deletes or changes an article or category. Nothing in a change set is visible to readers until it publishes.
Items
Type | Made from | What publishing does |
|---|---|---|
| A published article with staged changes: one item for each staged language. | Publishes that language's staged changes, like |
| An article that is not published: a draft, or an article that was unpublished. | Publishes the article, and any staged changes it has. |
| A category. | Sets the category's visibility to the value you chose when you added it. |
Statuses
Status | Meaning |
|---|---|
| Not published yet. You can change it and publish it. A scheduled change set is |
| A run is in progress. Its items cannot change, and it cannot be deleted. |
| Every item applied. |
| Some items applied and some failed. Fix the failed items and publish again. |
| No item applied. Fix the items and publish again. |
There is no draft or scheduled status. open, partially_published and failed change sets can still be edited and published.
Limits
A change set holds up to 500 items.
A help center can have up to 25 change sets that are
open,partially_publishedorfailed. Published change sets do not count.A name can be up to 120 characters, a description up to 2,000 and a release note up to 500.
The change set object
Field | Type | Description |
|---|---|---|
| integer | The change set's id. |
| string | Its name. |
| string or null | Its description. |
| string |
|
| boolean |
|
| string or null | The scheduled time, ISO 8601. |
| string or null | When the first item was published. It stays the same when you publish again. |
| string or null | The note you sent with the publish. |
| integer | How many items it holds. |
| integer | Progress of the last run. Both are |
| string | ISO 8601, with an offset. |
| object | Whether it can publish. Not in the list endpoint. |
| array | The items, in the order they were added. Not in the list endpoint. |
Each item has id, type, article_id, category_id, locale (for staged_version items), variant (always default), payload (for a category item, from and to visibility), applied and error (the reason the item failed in the last run).
readiness has counts (items by type), locales (the languages the release touches), articles (how many articles), blockers, warnings and publishable, which is true when there is at least one item and no blocker. Here is a change set with five items, three of them shown:
{
"id": 3,
"name": "Release 3.4",
"description": "Stage gates for boards",
"status": "open",
"is_scheduled": false,
"scheduled_for": null,
"published_at": null,
"release_note": null,
"items_count": 5,
"processed_items": 0,
"total_items": 0,
"created_at": "2026-09-30T08:21:23+00:00",
"updated_at": "2026-09-30T08:21:23+00:00",
"readiness": {
"counts": {
"staged_version": 3,
"article_publish": 1,
"category_visibility": 1
},
"locales": [
"de",
"en"
],
"articles": 3,
"blockers": [],
"warnings": [
{
"article_id": 598,
"article_title": "Move cards between stages",
"reason": "locale_gap",
"missing_locales": [
"de"
],
"message": "This article has no de changes, but the release includes de."
}
],
"publishable": true
},
"items": [
{
"id": 2,
"type": "staged_version",
"article_id": 597,
"category_id": null,
"locale": "de",
"variant": "default",
"payload": null,
"applied": false,
"error": null
},
{
"id": 5,
"type": "article_publish",
"article_id": 600,
"category_id": null,
"locale": null,
"variant": "default",
"payload": null,
"applied": false,
"error": null
},
{
"id": 6,
"type": "category_visibility",
"article_id": null,
"category_id": 307,
"locale": null,
"variant": "default",
"payload": {
"to": "public",
"from": "unlisted"
},
"applied": false,
"error": null
}
]
}
Blockers and warnings
Each blocker and warning has a reason, a message and the ids that locate it (item_id, article_id, article_title, locale where they apply).
Reason | Kind | Message |
|---|---|---|
| Blocker |
|
| Blocker |
|
| Blocker |
|
| Warning |
|
| Warning |
|
| Warning | A category item whose category already has the target visibility. |
List change sets
List change sets, most recently updated first. Each has the fields above, without readiness and items.
/v1/change-setsList change sets, most recently updated first.
statusmust match exactly. An unknown value, such asdraft, returns an empty list rather than an error.Paging uses
pageandper_page(default 25, up to 100), andmetaiscurrent_page,last_pageandtotal, unlike most list endpoints (see Pagination).
Create a change set
A change set starts empty and open. Name it after what it ships with, so you can find it later.
/v1/change-setsCreate an empty change set.
Leaving out
nameanswers422with{"status": "error", "message": "A change set needs a name."}. An empty name answers422withvalidation_error.A help center without change sets answers
402 Payment RequiredwithreasonCHANGE_SETS_UNAVAILABLE.A 26th open change set answers
409 ConflictwithTOO_MANY_OPEN_CHANGE_SETS:You already have 25 open change sets. Publish or delete one before starting another.
Get a change set
/v1/change-sets/{changeSetId}Get a change set with its items and readiness.
A change set that does not exist, belongs to another help center or was deleted answers 404 with Change set not found.
Rename a change set
Change name, description or both. This works in every status, including after publishing. null clears the description. An empty name answers 422.
/v1/change-sets/{changeSetId}Rename a change set or change its description.
Delete a change set
Delete the change set itself. Staged changes stay staged, unpublished articles stay unpublished, and categories keep their visibility. A change set that is publishing answers 409 with CHANGE_SET_NOT_OPEN.
/v1/change-sets/{changeSetId}Delete a change set without changing its articles or categories.
Add items
Send article ids, category ids or both. Each id is handled on its own, so one article in an unexpected state does not stop the others.
/v1/change-sets/{changeSetId}/itemsAdd articles and categories to a change set.
Field | Type | Description |
|---|---|---|
| array of integers | Up to 500 article ids. |
| array of integers | Up to 500 category ids. |
| string | The visibility the categories get when the change set publishes: |
A published article adds one
staged_versionitem for each language it has staged. An article that is not published adds onearticle_publishitem.A published article with nothing staged is not added, and neither is an id that does not exist, belongs to another help center or is in Trash. When anything is not added, the answer is
207 Multi-Statuswith"status": "partial"and arejectedlist:
{
"status": "partial",
"added": 0,
"rejected": [
{
"article_id": 599,
"reason": "Article 599 is live and has no staged changes, so there is nothing to release."
},
{
"article_id": 999999,
"reason": "not_found"
},
{
"category_id": 999999,
"reason": "not_found"
}
],
"change_set": "…"
}
addedcounts items, so an article with two staged languages adds 2. The response always includes the wholechange_set, with its readiness.Adding an article that is already in the change set updates its items to the current staged copies and counts them in
addedagain. Do this after you restage an article, or when astaged_not_addedwarning names it.Past 500 items, the request stops with
409andCHANGE_SET_FULL. Items added before that point stay added, and the answer does not list them: read the change set to see where it stopped.A change set that is not open answers
409withCHANGE_SET_NOT_OPEN.
This is the only way to change a category's visibility over the API. A typical launch: your team builds the new section in a category shared with Team members only, you add that category here with category_visibility set to public, and the release shows it to everybody.
Remove an item
Take one item out of the change set. The staged changes, article or category it pointed at stay as they are.
/v1/change-sets/{changeSetId}/items/{itemId}Take one item out of a change set, leaving the work it points at as it is.
An item id that is not in this change set answers 404 with Change set not found. A change set that is not open answers 409 with CHANGE_SET_NOT_OPEN.
Publish or schedule
Send the request with no scheduled_for to publish now, or with scheduled_for to schedule. Both answer 202 Accepted, and status says which: queued comes with a status_url to poll; scheduled means nothing publishes until the time you set.
/v1/change-sets/{changeSetId}/publishPublish a change set now or schedule it, with a 202 answer because the run happens later.
Field | Type | Description |
|---|---|---|
| string | Optional. When to publish, in the future. ISO 8601 with an offset, such as |
| string | Optional. Up to 500 characters, kept on the change set. |
The plan is checked first. Without change sets, the answer is
402withCHANGE_SETS_UNAVAILABLE.Then readiness. With a blocker, or with no items, the answer is
422withSome items need attention before this change set can publish.orThere is nothing in this change set to publish., and thereadinessobject.A change set that is publishing or published answers
409withCHANGE_SET_NOT_OPEN.Scheduling. Scheduled change sets publish at the first minute at or after
scheduled_for. At that time, HelpCenter.io checks the plan again: if the help center no longer has change sets, the schedule is cleared and the change set stays open. Readiness is not checked again, so an item that conflicts by then fails on its own. Sending publish again replaces the schedule, or publishes at once when you leave outscheduled_for.Schedule only
openchange sets. Apartially_publishedorfailedchange set acceptsscheduled_forand answers202with"status": "scheduled", butis_scheduledstaysfalseand it does not publish at that time. Publish those now instead.scheduled_forin the past answers422withA change set cannot be scheduled for a time that has passed.
Cancel a schedule
Clear scheduled_for. The change set stays open, with its items.
/v1/change-sets/{changeSetId}/scheduleCancel a scheduled publish and keep the change set open.
Follow a publish run
Poll the status until done is true. Branch on done, not on the status string: it is true whenever nothing more will happen without you, including after a run stops early.
/v1/change-sets/{changeSetId}/statusFollow a publish run: poll it until done is true.
Field | Description |
|---|---|
| The change set's status. |
|
|
| Progress of the current or last run. |
| When the first item was published. |
| Each item's |
Poll every few seconds: status reads count toward the 120 writes a minute. In a shell, with jq:
until [ "$(curl -sS "https://api.helpcenter.io/v1/change-sets/$CHANGE_SET_ID/status" \
-H "apikey: $HELPCENTER_API_KEY" -H "Accept: application/json" | jq .done)" = true ]; do
sleep 5
done
When a run ends partially_published, the failed items carry an error:
{
"status": "success",
"change_set_status": "partially_published",
"done": true,
"processed_items": 2,
"total_items": 2,
"published_at": "2026-09-30T08:21:52+00:00",
"items": [
{
"id": 8,
"type": "staged_version",
"article_id": 599,
"locale": "en",
"variant": "default",
"applied": false,
"error": "The live article changed after version 27 (en) was staged."
},
{
"id": 9,
"type": "staged_version",
"article_id": 730,
"locale": "en",
"variant": "default",
"applied": true,
"error": null
}
]
}
What a run does
Items apply one at a time, each in its own transaction, in the order they were added. A run is not all or nothing: when an item fails, the items before it stay published and the run goes on. After 5 failures in a row, the run stops.
Publishing again resumes: items that already applied are skipped. To finish a
partially_publishedchange set, fix what failed (for a conflict, discard and restage the article, then add it again), and publish again.A staged item whose changes were already published or discarded on their own is skipped and counts as applied. So is a category that already has the target visibility.
Published items send the usual webhooks:
article.updatedfor staged changes,article.publishedfor articles that go live,category.updatedfor categories.
Errors
Change-set refusals put their code in reason, not code: {"status": "error", "reason": "CHANGE_SET_NOT_OPEN", "message": "…"}.
Status | Reason or message | When |
|---|---|---|
|
| An OAuth access token, or a key whose help center was deleted. |
|
| No credential, or an unknown key. |
|
| Creating, publishing or scheduling on a help center without change sets. |
|
| A Read only key on a write. |
|
| No such change set or item. |
|
| Adding or removing items, publishing or scheduling when the change set is publishing or published; deleting it while it is publishing. |
|
| More than 500 items. |
|
| A 26th open change set. |
| A message (with | Blockers, nothing to publish, no name, or a time in the past. |
For the complete release flow as a program, see Publish release notes with change sets. For the dashboard side, see Release many changes together with change sets.