content_taxonomy_apply
Create or configure free-board/article categories and their labels (article subcategories).
Read the shared content contract for authorization, app identity, result interpretation and recovery.
Inputs and actions
Require channelCode, exact free-board or article pAppCode, requestId and expectedRevision from content_taxonomy_get. dryRun defaults to false. For category actions below, use the list revision for create_category and the selected category's revision for the other actions. GENERAL lifecycle actions use the separate general_labels inventory revision described below.
| action.kind | Fields |
|---|---|
| create_category | Free-board name 1–60 UTF-16 units; article name 1–30; optional description 0–100. |
| update_category | categoryId; nonempty fields. Free-board name 1–20/description 0–50; article name 1–30/description 0–100. coverImageSrc accepts an existing HTTP(S) URL or empty string to clear. |
| delete_category | Free-board: DELETE_CATEGORY_AND_CONTENTS. Article: DELETE_CATEGORY, only with fresh totalPosts exactly zero. |
| create_label | categoryId and name 1–30. |
| update_label | categoryId, labelId and name 1–30. |
| delete_label | categoryId, labelId and confirmation DELETE_LABEL. |
| order_labels | categoryId and labelIds containing every current label ID exactly once. |
| create_general_label / rename_general_label / delete_general_label | Article GENERAL lifecycle; separate inventory revision and content-general-label task, as described below. |
A board supports at most 100 categories and 100 labels per category. Choose label IDs from this category. Cover URLs follow the shared HTTP(S) rule. For an original free-board or article category image, use content_file_upload with purpose board_cover, then save its returned location through update_category using a fresh category revision. Upload and category persistence are separate operations.
Effects and recovery
Free-board category deletion may remove its contents. Article category deletion requires a fresh zero post count. Neither has automatic undo. Label deletion also has no general restoration. The tool returns CONTENT_BOARD_SAVED after the action-specific readback; category deletion observes missing detail, while label actions compare the resulting list and preserved category fields. It does not prove recovery of deleted contents or every downstream association.
Use taskType content-taxonomy. The journal records sent before the one source request. Retry/resume only reads back. Creation needs its returned category/label ID; absent identity is never guessed from a matching name. Retain the requestId on BOARD_OUTCOME_UNVERIFIED or a readback mismatch. The parent navigation opens category selection, not an invented dialog deep link.
Input example
This is an input illustration, not a recorded call. Replace the channel, app, observed IDs and all-zero revision with values from the matching authorized read. A dry run does not save changes.
{
"channelCode": "selected-channel",
"pAppCode": "A00003",
"requestId": "example-request-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true,
"action": {
"kind": "update_category",
"categoryId": 1,
"fields": {
"name": "Announcements"
}
}
}
Article lifecycle constraints
Creation sends name, an empty default description if omitted, a blank cover and isPublished:false. The source publication control is disabled: this method has no publish switch, category-title override or post-category assignment. Creating a category does not enable the category tab.
Article metadata edits merge the partial fields with the stored name/description/cover for a complete source save. Cover accepts an existing HTTP(S) URL or blank removal; URL syntax does not prove storage, image decoding or rendering. Delete requires confirmation DELETE_CATEGORY and fresh totalPosts exactly zero; unknown/null/nonzero counts are rejected, and upstream locked-category restrictions remain authoritative. Categories cannot be reordered here. order_labels is the subcategory editor and requires all current label IDs exactly once.
The revision includes the complete category inventory. Before dispatch, context, target membership and prerequisites are rechecked. Readback checks requested state and unchanged metadata/siblings; category deletion also needs explicit detail absence and the exact surviving inventory. A returned label ID that already existed cannot prove creation. Unknown creation IDs and sent effects remain observation-only, without title adoption or automatic restoration.
Illustrative new unpublished category plan:
{
"channelCode": "selected-channel",
"pAppCode": "B00001",
"requestId": "article-category-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true,
"action": {
"kind": "create_category",
"name": "Articles",
"description": "Community articles"
}
}
Save an uploaded article category cover
After a successfully acknowledged upload, select the exact category and read content_taxonomy_get again. Copy its category revision into expectedRevision and the upload's location into action.fields.coverImageSrc. update_category carries the current name/description and verifies the cover, unrelated metadata, labels and sibling categories. The upload's contentSaved:false is not a saved category or a taxonomy revision.
To restore a previous cover, save its original URL with a newly read category revision; an empty string clears the reference. Neither action deletes an uploaded storage object or recreates a deleted category. Uncertain category writes retain the original content-taxonomy task and observe readback without resend. Stored URL verification does not prove actual image display.
Illustrative save plan only: replace categoryId, revision and the placeholder URL with the matching fresh read and acknowledged upload. This example does not perform an upload or a category save.
{
"channelCode": "selected-channel",
"pAppCode": "B00001",
"requestId": "article-cover-save-001",
"expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
"dryRun": true,
"action": {
"kind": "update_category",
"categoryId": 1,
"fields": {
"coverImageSrc": "https://storage.example.invalid/returned-category-cover.png"
}
}
}
Article GENERAL label creation, rename and deletion
Read view:general_labels and use that complete inventory's revision as top-level expectedRevision. This is distinct from category revisions and the post-detail revision used for post selection. Keep the exact channel/app, requestId and original action. Existing category schemas/defaults/fingerprints are unchanged.
| action.kind | Required fields and effect |
|---|---|
| create_general_label | name, 1–30 UTF-16 units, not trimmed. POST one GENERAL record. |
| rename_general_label | Existing positive safe integer labelId and name under the same bounds. PATCH only name; retain the label ID. |
| delete_general_label | Existing labelId and confirmation:DELETE_GENERAL_LABEL. DELETE that record with no body. |
The exact installed consumer scopes /seller/api/v1/labels and the selected label-ID path. Creation sends name/type:GENERAL; rename sends only name, with the ID in the path. No callback/action bookkeeping, category ID or caller-created identity is accepted. GENERAL ordering is not exposed: the original GENERAL list has no reorder control; category-sublabel ordering remains separate. Duplicate-name or locked-resource rules remain upstream decisions.
The full inventory must remain complete, unique and within the defensive1,000-label read bound. Creation at that bound is refused because its result could not be verified; this is not a source100-label creation limit. dryRun returns PLAN_READY without a write or new job, including the selected label's current post/series counts for rename/delete. Used labels are not restricted to zero usage before deletion. Inspect the counts and explicit confirmation: deletion cannot restore the old label identity or associations. It does not request post/series deletion, relinking or recreation, and the inventory cannot verify cascade or visitor behavior.
Before dispatch, the inventory revision, exact target and current context must match. The journal is saved as sent before the one request. Creation needs a new returned GENERAL identity with the requested name and matching consumer when present; a prior, missing or foreign ID cannot be inferred from names or the later inventory. Readback requires the created label with zero post/series counts, the exact renamed record with all other metadata unchanged, or explicit absence after deletion. All other labels must remain unchanged. Concurrent count/order changes conservatively leave the result uncertain; this is not an atomic transaction.
Fresh verified success returns CONTENT_GENERAL_LABEL_SAVED, labelId, label or deleted:true, the new inventory revision, and verification flags including completeInventory:true, otherLabelsPreserved:true and linkedContentVerified:false. removalRecoverable:false does not promise restoration. Unknown receipt/readback yields action_required with CONTENT_GENERAL_LABEL_OUTCOME_UNVERIFIED, the original taskId and writesPerformed:unknown. Preserve that task; no automatic resend or undo is attempted.
Use taskType:content-general-label, not content-taxonomy or content-post. Status is historical; task_resume or an identical sent apply only reads back, even if the write hook later disappears. A verified replay is historical and supplies no fresh confirmation. Reusing dryRun on an existing job returns its saved phase without refreshing the inventory; it is not a new executable plan. Context/credential/instance changes block recovery. No global lock across request IDs is added: reconcile uncertainty before another request with a fresh revision. Eligible current navigation opens the exact article labels screen, never a guessed label-detail route.
It preserves the POST creation receipt and allows empty successful PATCH/DELETE responses followed by complete inventory readback. On older hosts without the writer, a new lifecycle request returns CONTENT_GENERAL_LABEL_TRANSPORT_REQUIRED before a write, including dryRun. No new public method is added.
Illustrative dry runs only; replace the channel, exact app, label IDs and GENERAL inventory revision:
{"channelCode":"selected-channel","pAppCode":"B00001","requestId":"general-label-create-001","expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000","dryRun":true,"action":{"kind":"create_general_label","name":"News"}}
{"channelCode":"selected-channel","pAppCode":"B00001","requestId":"general-label-rename-001","expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000","dryRun":true,"action":{"kind":"rename_general_label","labelId":7,"name":"Updates"}}
{"channelCode":"selected-channel","pAppCode":"B00001","requestId":"general-label-delete-001","expectedRevision":"0000000000000000000000000000000000000000000000000000000000000000","dryRun":true,"action":{"kind":"delete_general_label","labelId":7,"confirmation":"DELETE_GENERAL_LABEL"}}