You are sending a POST or PUT request to /wp/v2/posts with categories or tags, and WordPress returns a 403 with the error code rest_cannot_assign_term. The REST API expects integer term IDs in the categories and tags fields, not names or slugs. When you pass valid IDs and still see this error, the authenticated user lacks the assign_term capability for those specific terms, or a custom taxonomy is misconfigured.
This error fires inside WP_REST_Posts_Controller::check_assign_terms_permission(), which runs during the permission check for both create_item and update_item. The method iterates over every term ID you send, looks up each one with get_term(), and calls current_user_can('assign_term', $term_id). If any single term fails that check, the entire request is rejected with a 403. Let’s break down the four causes and the correct workflow.
The Short Answer: REST Wants Term IDs, Not Names
The WordPress REST API treats the categories and tags fields on a post as arrays of integers. Every value you send gets cast to an integer through absint() during request sanitization. If you pass a string like "News" or "marketing", it becomes 0. The term with ID 0 does not exist, so get_term(0, $taxonomy) returns false, and the value is silently skipped in the permission check. Your post gets created without the intended terms, and no error is thrown.
This silent failure is the single most common issue developers hit when assigning terms via the REST API. You send what looks like a reasonable request, the post appears in WordPress, but the categories and tags are missing. The 403 rest_cannot_assign_term error is a separate but related problem: it fires only when you pass valid term IDs and the authenticated user lacks the capability to assign those specific terms. Both issues stem from the same root cause: a mismatch between what you are sending and what the REST API expects.
The fix in both cases is the same two-step workflow. First, resolve or create your terms by hitting the taxonomy endpoints (/wp/v2/categories and /wp/v2/tags). Second, pass the integer term IDs returned by those endpoints in your post creation request. We cover the full workflow with working examples below.
Cause 1: Sending Term Names Instead of IDs

This is the mistake that catches almost everyone the first time they work with the REST API. You know your category is called “News,” so you send "categories": ["News"] or "tags": ["marketing"]. The REST API does not resolve names or slugs to term IDs. It casts each value to an integer using absint(), which converts any non-numeric string to 0. Since no term has ID 0, the value is skipped during the permission check and during term assignment.
The result: your post is created successfully (HTTP 200 or 201), but it has no categories or no tags. If you are also experiencing a separate capability issue (Cause 2 below), the 403 error may fire on top of this problem, making diagnosis harder.
How to Look Up Existing Term IDs
Use GET requests to the taxonomy endpoints to find term IDs by name or slug:
To find a category by slug, send a request to GET /wp/v2/categories?slug=news. The response includes the id field, which is the integer you need. The same approach works for tags: GET /wp/v2/tags?slug=marketing. You can also search by name using the search parameter: GET /wp/v2/categories?search=News returns all categories matching that string.
How to Create New Terms and Get Their IDs
If the term does not exist yet, create it first by POSTing to the taxonomy endpoint:
Send POST /wp/v2/categories with a JSON body of {"name": "News"}. The response includes the new term’s id. Use that integer ID when creating or updating your post. The same pattern applies to tags: POST /wp/v2/tags with {"name": "marketing"}.
Why the REST API Differs from wp_insert_post
This is where the confusion often starts. The PHP function wp_set_post_terms() accepts different input formats depending on whether the taxonomy is hierarchical or non-hierarchical. For non-hierarchical taxonomies like tags, you can pass an array of names or slugs, and WordPress will create terms that do not exist. For hierarchical taxonomies like categories, you must pass integer IDs. The REST API, however, always expects integer IDs regardless of taxonomy type, because the check_assign_terms_permission() method casts every value to (int) before looking it up. The classic editor and the REST API follow different rules for the same underlying functions.
Cause 2: Missing the assign_terms Capability

When you are sending valid integer term IDs and still getting the 403, the authenticated user does not have the assign_term capability for one or more of those terms. The capability chain works as follows in WordPress core.
The REST API calls current_user_can('assign_term', $term_id). WordPress maps this meta capability through map_meta_cap(), which looks up the term, finds its taxonomy, and retrieves the taxonomy’s assign_terms capability string. For the built-in category taxonomy, that string is assign_categories. For the tag taxonomy, it is assign_post_tags. Both of these are then mapped again: assign_categories and assign_post_tags both resolve to edit_posts in the default map_meta_cap switch statement.
This means any role with edit_posts can assign existing categories and tags by default. Authors, Editors, and Administrators all have edit_posts, so they can assign terms via the REST API without issue. Contributors also have edit_posts, so they can assign terms to their own drafts. Subscribers do not, so they would fail earlier with rest_cannot_create before the term check even runs.
When Authors Lack assign_terms
Out of the box, Authors have edit_posts and can assign terms. The capability problem arises when a security plugin, a custom role manager, or custom code modifies the map_meta_cap filter. For example, a site owner might restrict Authors to specific categories using a plugin that adds do_not_allow to the assign_term capability check for certain term IDs. In that scenario, the Author can create posts but cannot assign the restricted category, triggering rest_cannot_assign_term.
Another scenario: a custom taxonomy registered with a non-standard assign_terms capability that maps to something other than edit_posts. If the custom capability string does not match any capability the user’s role has, the check fails. We cover this in detail in Cause 3 below.
How to Diagnose
Check the authenticated user’s role and capabilities. If you are using application passwords or basic auth, verify which user the credentials belong to. Then test whether that user can assign the specific term:
- Confirm the user’s role includes
edit_posts(Authors and above have this by default). - Check whether any plugin filters
map_meta_capto restrictassign_termfor specific terms or taxonomies. - For custom taxonomies, verify the
assign_termscapability string in theregister_taxonomycall and confirm the user’s role has the mapped capability. - Temporarily test with an Editor or Administrator account. If the request succeeds, the issue is the user’s capabilities, not your code.
Cause 3: Custom Taxonomies Without show_in_rest
If you are working with a custom taxonomy and the terms are being silently ignored, the taxonomy may not have REST API support enabled. WordPress only exposes taxonomies through the REST API when show_in_rest is set to true in the register_taxonomy() arguments. Without this flag, the taxonomy does not get a REST route, and the posts controller skips it entirely when processing term assignments.
Here is a correctly registered custom taxonomy with full REST API support:
The critical arguments are show_in_rest => true, which enables REST API routes for the taxonomy, and rest_base => 'genre', which sets the field name used in post requests. If rest_base is not set, it defaults to the taxonomy name (e.g., genre). You must use this rest_base value as the key in your post creation JSON, not the taxonomy name, if they differ.
For example, if your taxonomy is registered as book_genre with rest_base => 'genres', you would send "genres": [5, 12] in your post creation request, not "book_genre": [5, 12]. The posts controller uses the rest_base to look up the field in the request body.
Capability Mapping in Custom Taxonomies
Custom taxonomies can specify their own capability strings using the capabilities argument in register_taxonomy(). By default, if you do not set capabilities, WordPress uses the default mapping where assign_terms resolves to edit_posts. But if you set custom capabilities, the assign_terms string must map to a capability the user’s role actually has.
A common mistake is registering a custom taxonomy with 'assign_terms' => 'manage_genres' without adding manage_genres to any role. When the REST API checks current_user_can('assign_term', $term_id), it resolves through map_meta_cap to manage_genres, which no role has, and the request fails with 403. Either map the custom capability to an existing one via the map_meta_cap filter, or assign the capability to the user’s role using add_cap().
Adding REST Support to an Existing Taxonomy
If you do not control the taxonomy registration (it is in a theme or plugin you cannot modify), use the register_taxonomy_args filter to add REST support:
This filter runs during taxonomy registration. Check the taxonomy name, then set show_in_rest => true and optionally rest_base. After this, the taxonomy will be available at /wp/v2/genres and assignable in post creation requests.
Cause 4: Implicitly Creating Terms via the Post Endpoint
The REST API does not create terms implicitly when you create a post. If you send a term ID that does not exist in the database, it is skipped. If you send a string name hoping the API will create the term and assign it, the name is cast to 0 by absint() and skipped. There is no “create if not found” behavior on the post creation endpoint.
This differs from the classic editor experience. In the classic editor, typing a new tag name in the tags metabox and saving the post creates the tag automatically. The REST API does not replicate this behavior. To assign a term that does not exist yet, you must create it first via the taxonomy endpoint, then reference the returned ID.
Hierarchical vs Non-Hierarchical: Different Capability Requirements
The capability required to create a new term depends on whether the taxonomy is hierarchical. This distinction matters when you are building the two-step workflow:
- Categories (hierarchical): Creating a new term requires
edit_terms, which maps toedit_categories, which maps tomanage_categories. Only Editors and Administrators havemanage_categoriesby default. Authors cannot create new categories. - Tags (non-hierarchical): Creating a new term requires
assign_terms, which maps toassign_post_tags, which maps toedit_posts. Authors and above can create new tags.
This asymmetry comes from WordPress core’s WP_REST_Terms_Controller::create_item_permissions_check(). The controller checks: if the taxonomy is hierarchical and the user lacks edit_terms, or if the taxonomy is non-hierarchical and the user lacks assign_terms, it returns rest_cannot_create with a 403 status. So an Author can create tags but not categories. An Editor can create both.
If an Author tries to create a new category via POST /wp/v2/categories, they get a 403 with rest_cannot_create. If an Editor does the same, it succeeds. Plan your workflow around the authenticated user’s role.
The Correct Two-Step Workflow
Stop trying to assign terms by name in a single post creation request. The correct workflow is always two steps:
Step 1: Resolve or create terms via the taxonomy endpoints. Send a GET request to /wp/v2/categories?slug=news or /wp/v2/tags?search=marketing to find existing term IDs. If a term does not exist, POST to /wp/v2/categories or /wp/v2/tags to create it. Save the id from each response.
Step 2: Reference those IDs when creating or updating the post. Send your POST or PUT to /wp/v2/posts with the integer term IDs in the categories and/or tags arrays. The REST API validates each ID against the database, checks the user’s assign_term capability, and assigns the terms.
This workflow works regardless of whether you are using categories, tags, or custom taxonomies. For custom taxonomies, use the rest_base value as the JSON key and the corresponding taxonomy endpoint (e.g., /wp/v2/genres for a taxonomy with rest_base => 'genres').
Quick Reference: Causes and Fixes
| Cause | HTTP Response | Root Issue | Fix |
|---|---|---|---|
| Names or slugs instead of IDs | 200/201, terms missing | absint() converts strings to 0, which is skipped | Look up term IDs via GET /wp/v2/categories or /wp/v2/tags first |
| Missing assign_terms capability | 403 rest_cannot_assign_term | User role lacks edit_posts (or custom mapped capability) | Use a role with edit_posts, or add the capability via add_cap() |
| Custom taxonomy without show_in_rest | 200/201, custom terms missing | Taxonomy not exposed via REST API | Set show_in_rest => true and rest_base in register_taxonomy() |
| Trying to create terms implicitly | 200/201, new terms missing | REST API does not create terms from the post endpoint | Create terms via POST /wp/v2/categories or /wp/v2/tags first |
| Author creating new category | 403 rest_cannot_create | Hierarchical taxonomy needs edit_terms (manage_categories) | Use an Editor or Admin account, or create the category separately |
Working Examples: curl and Python

Step 1: Find or Create Term IDs (curl)
Find an existing category by slug:
curl -u "user:app_password" "https://example.com/wp-json/wp/v2/categories?slug=news"
The response is a JSON array. If it is not empty, extract the id field from the first element. If the array is empty, the category does not exist yet, so create it:
curl -u "user:app_password" -X POST "https://example.com/wp-json/wp/v2/categories" -H "Content-Type: application/json" -d '{"name": "News"}'
Save the id from the response. Do the same for tags using /wp/v2/tags instead of /wp/v2/categories.
Step 2: Create the Post with Term IDs (curl)
Now create the post using the integer IDs you collected. In this example, category ID 5 and tag IDs 12 and 18:
curl -u "user:app_password" -X POST "https://example.com/wp-json/wp/v2/posts" -H "Content-Type: application/json" -d '{"title": "Hello World", "content": "Post body here", "status": "draft", "categories": [5], "tags": [12, 18]}'
Full Python Example with requests
The same two-step workflow in Python, using the requests library. This example finds existing category and tag IDs, creates them if they do not exist, then creates the post:
import requests
BASE_URL = "https://example.com/wp-json/wp/v2"
AUTH = ("user", "your_app_password")
def get_or_create_term(taxonomy, name, slug):
# Try to find existing term by slug
r = requests.get(f"{BASE_URL}/{taxonomy}", params={"slug": slug}, auth=AUTH)
if r.status_code == 200 and r.json():
return r.json()[0]["id"]
# Term does not exist, create it
r = requests.post(f"{BASE_URL}/{taxonomy}", json={"name": name}, auth=AUTH)
r.raise_for_status()
return r.json()["id"]
# Step 1: Resolve or create terms
category_id = get_or_create_term("categories", "News", "news")
tag_id = get_or_create_term("tags", "marketing", "marketing")
# Step 2: Create the post with integer term IDs
post_data = {
"title": "Hello World",
"content": "Post body here",
"status": "draft",
"categories": [category_id],
"tags": [tag_id],
}
r = requests.post(f"{BASE_URL}/posts", json=post_data, auth=AUTH)
r.raise_for_status()
print(f"Post created with ID {r.json()['id']}")
Note that if the authenticated user is an Author, the get_or_create_term function will succeed for tags but fail with a 403 for categories, because Authors lack manage_categories. Use an Editor or Administrator account if your workflow requires creating categories programmatically.
If You Are Automating WordPress Publishing
If you are building an automated content pipeline that publishes to WordPress via the REST API, term assignment is one of several friction points that slow down development. Featured images, SEO plugin fields, internal linking, and post status management all have their own quirks. ClearPost handles this entire pipeline natively: it resolves category and tag IDs, publishes via the REST API with all fields pre-configured, and surfaces every draft in an approval queue before anything goes live. You approve, it publishes. No need to debug capability chains or term ID mismatches manually.
For a broader look at how AI content platforms integrate with the WordPress REST API, including how they handle categories, tags, featured images, and SEO plugin fields in a single publish step, see our comparison of the best AI content writers for WordPress. For a detailed breakdown of the full publishing pipeline from research to draft to publish, our guide on AI SEO content generators walks through each stage with real time estimates.
Try ClearPost free for 7 days. AI does the heavy lifting, you approve every post before it goes live. No long onboarding, no agency overhead, cancel anytime.
Frequently Asked Questions
What does rest_cannot_assign_term mean in WordPress?
It means the authenticated user does not have the assign_term capability for one or more term IDs you sent in the categories or tags fields of a POST or PUT request to /wp/v2/posts. The REST API checks current_user_can(‘assign_term’, $term_id) for every term, and if any check fails, the entire request is rejected with a 403.
Can I pass category names or slugs instead of IDs to the WordPress REST API?
No. The REST API casts every value in the categories and tags arrays to an integer using absint(). Non-numeric strings like ‘News’ become 0, which is not a valid term ID. The value is silently skipped, and your post is created without that category. You must look up or create the term first via /wp/v2/categories or /wp/v2/tags, then pass the returned integer ID.
Why can an Author create tags but not categories via the REST API?
Creating a new term in a hierarchical taxonomy like categories requires the edit_terms capability, which maps to manage_categories. Only Editors and Administrators have manage_categories by default. Creating a new term in a non-hierarchical taxonomy like tags requires the assign_terms capability, which maps to edit_posts. Authors have edit_posts, so they can create tags but not categories.
How do I fix rest_cannot_assign_term for a custom taxonomy?
First, ensure the taxonomy is registered with show_in_rest set to true and a rest_base value. Second, verify the assign_terms capability in your register_taxonomy call maps to a capability the user’s role has. If you set a custom capability string like manage_genres, add that capability to the user’s role via add_cap() or map it to an existing capability using the map_meta_cap filter. Third, use the rest_base value as the JSON key in your post request, not the taxonomy name.
Does the WordPress REST API create terms automatically when I assign them to a post?
No. The REST API does not have a ‘create if not found’ behavior on the post creation endpoint. If you pass a term ID that does not exist, it is skipped. If you pass a string name, it is cast to 0 and skipped. You must create new terms first by POSTing to /wp/v2/categories or /wp/v2/tags, then reference the returned ID in your post creation request.
