---
title: "Create a category"
method: POST
path: "/categories"
---

# Create a category

`POST /categories`

## Request

- Base URL: `https://www.merchkit.com/api/v1`
- URL: `https://www.merchkit.com/api/v1/categories`
- Auth: HTTP bearer

Creates a category and returns the full resource — no follow-up GET needed. `attributes` uses the same one-map write shape as PATCH (references inline as UUIDs or `{id}` stubs). Unknown attribute keys are rejected with 400.

Scope: `write:categories`.

## Request body

- object
  - `parent_id` string, uuid: Optional parent category (products: creates a variant).
  - `attributes` AttributeWriteMap: Map of attribute key → new value (sparse — only keys being written). Scalars set the value; `null` OR `""` clears it (the key then disappears from reads). A single-reference key accepts a bare UUID string or an `{id}` object; a list-reference key accepts an array of either — list writes REPLACE the current set. Read stubs re-sent verbatim are valid writes (extra stub fields like `type`/`label` are ignored). Unknown keys are a 400; select values must be one of the attribute's `acceptable_values`.

## Response `201`

The created category (full resource).

- object
  - `data` EntityResource: The one shape for every entity read and mutation response. System fields live at the top level; every customer-defined key lives in `attributes`, so customer keys can never collide with system ones.
    - `id` string, uuid, required
    - `type` 'product' | 'image' | 'vendor' | 'category' | 'source', required: The entity type discriminator.
    - `label` string, nullable, required: The workspace's primary-attribute value for this entity; null when no primary attribute is configured or it is unset (never a UUID posing as a name). Render `label ?? id`.
    - `parent_id` string, uuid, nullable, required: Parent entity id (products: the variant parent).
    - `created_at` string, date-time, nullable, required
    - `updated_at` string, date-time, nullable, required
    - `attributes` object, required: SPARSE map of customer-defined keys — only keys with values appear; a missing key means unset or not-applicable (read `attributes[key] ?? null`; never contains null or ""). Scalars are bare values; a single reference is a labeled stub; a list reference is a COMPLETE ordered stub array (no cap, array order = position order). Inverse-reference keys never appear — traverse them via filters or `/products/{id}/references`. The full key list comes from `GET /attributes`.

## Other responses

- `400`: Validation failed (unknown attribute key, bad value).

---

[API](https://skmtc.dev/merchkit/apis/merchkit-api.md) - [All operations](https://skmtc.dev/merchkit/apis/merchkit-api/llms.txt) - [OpenAPI document](https://skmtc.dev/merchkit/apis/merchkit-api/revisions/3062ca814757?raw)
