Skip to content
API Now! is currently in closed beta. We are constantly updating these guides as we release updates!

Categories (Semantic Module)

← Back to Modules Reference

The Categories semantic module annotates an association between a source entity and a taxonomy entity to provide formal, structured classification.

While informal tagging modules like Tags are designed for lightweight keywords (such as featured or bestseller) and default to open user-generated creation, Categories represents structured classification where records belong to defined organizational groupings:

  • E-Commerce Catalogs: Products classified under ["Electronics", "Audio", "Accessories"].
  • Help Centers & Knowledge Bases: Support tickets categorized as ["Billing", "Invoicing"].
  • Corporate Asset Management: Documents grouped under ["Legal", "Contracts"].

Decorating an association with Categories enables automated name-based lookups, configurable inline response formatting (flat string labels or rich object summaries), and strict curated taxonomy enforcement by default.


AttributeSpecification
ScopeAssociation (Applied to a relationship between entities)
Relationship CardinalityMany-to-Many (multiple: true, join table) or Many-to-One (multiple: false, foreign key column)
Target EntityExactly one target entity (union targets are not supported) with exactly one property annotated with SemanticType.Name
Hierarchy StatusFlat taxonomy in current iteration (hierarchy nesting and depth limits are not supported)
Default Custom CategoriesDisabled (allowCustomCategories: false — strict curated catalog by default)
Default Case SensitivityCase-insensitive (caseSensitive: false)
Default Character SetASCII only (allowUnicode: false)
Default Response Style'labels' (flat string array, e.g., ["Electronics", "Gadgets"])
Supported Response Styles'labels', 'summary' (returns [{ id, name }]), or 'none' (omits inline)
Database ArchitectureAutomatic join table for Many-to-Many (_source_assoc) or foreign key column for Many-to-One

1. Curated Taxonomy by Default (allowCustomCategories: false)

Section titled “1. Curated Taxonomy by Default (allowCustomCategories: false)”

Unlike open-ended tags, categorization typically requires strict administrative curation. For this reason, allowCustomCategories defaults to false:

  • Callers may only link records to categories that already exist in the database catalog for the active organization.
  • Submitting an unrecognized category label immediately rejects the mutation with a 422 Unprocessable Entity status.
  • When open categorization is needed, setting allowCustomCategories: true enables automatic find-or-create provisioning.

2. Configurable Inline Exposition (responseStyle)

Section titled “2. Configurable Inline Exposition (responseStyle)”

Categories allows domain modelers to control how category data is returned in parent resource responses (e.g., GET /products/:id):

  • 'labels' (default): Returns a flat array of category names: ["Electronics", "Audio"]. Minimizes response payload size while delivering immediate readability.
  • 'summary': Returns an array of lightweight summary objects containing both the primary key and the name property: [{"id": "cat_01", "name": "Electronics"}]. Ideal when frontend clients need IDs to power navigation links or filter state.
  • 'none': Excludes category associations from the parent response payload entirely. Clients retrieve category details on demand through sub-resource endpoints (GET /products/:id/categories).

3. Multi-Valued and Single-Valued Cardinality

Section titled “3. Multi-Valued and Single-Valued Cardinality”

Categories supports two architectural relationship types:

  • Many-to-Many (multiple: true): Used when a record can belong to multiple categories simultaneously. The Serverless Engine automatically manages the underlying join table, performing transactional replace-all synchronization on parent writes.
  • Many-to-One (multiple: false): Used when each record belongs to exactly one category. The engine manages foreign key assignment natively.

When mutating a Many-to-Many category association via parent POST or PATCH requests:

  • The engine synchronizes associations in a single database transaction, clearing existing pivot rows and linking the newly specified categories.
  • Duplicate entries in the submitted array are automatically deduplicated.
  • Partial Updates: Omitting the categories property in a PATCH payload preserves existing category assignments without alteration.
Section titled “5. Label-First Sub-Resource Operations (Link & Unlink)”

API consumers do not need internal primary keys to manage category links:

  • Append Category (POST /products/:id/categories): Accepts {"name": "Gadgets"} and attaches the category without affecting existing links.
  • Remove Category (DELETE /products/:id/categories/:name): The path parameter is the category name string (e.g., /categories/Gadgets). The engine resolves the name to the target foreign key and removes the pivot row, returning 204 No Content.

In APIs utilizing organization authorization strategies (EnterpriseRBAC or OrganizationRBAC), the target category entity must carry an association annotated with SemanticType.OrganizationResource. Category lookups, insertions, and uniqueness checks are automatically scoped to the calling organization.


When configuring the Categories semantic on an association in the Domain Modeler, the following parameters are available:

OptionTypeDefaultDescription
allowCustomCategoriesbooleanfalseWhen false (default), only existing database category records can be assigned. When true, new categories are created on the fly.
caseSensitivebooleanfalseWhen false, normalizes category casing so casing variations share the same record. When true, matches exact casing.
allowUnicodebooleanfalseWhen false, restricts category names to ASCII. When true, permits international scripts and special symbols.
responseStyle'labels' | 'summary' | 'none''labels'Controls how categories appear inline in parent GET responses. 'labels' emits string arrays, 'summary' emits object arrays with IDs, and 'none' omits the field.

1. Assigning Categories on Record Creation (responseStyle: 'labels')

Section titled “1. Assigning Categories on Record Creation (responseStyle: 'labels')”

With the default 'labels' response style, categories serialize as a flat string array:

POST /products
Content-Type: application/json
{
"title": "Noise-Cancelling Studio Headphones",
"price": 299.99,
"categories": ["Electronics", "Audio"]
}

Server Response (201 Created):

{
"data": {
"id": "prod_502",
"title": "Noise-Cancelling Studio Headphones",
"price": 299.99,
"categories": ["Electronics", "Audio"]
}
}

2. Category Summary Response (responseStyle: 'summary')

Section titled “2. Category Summary Response (responseStyle: 'summary')”

When the association is configured with responseStyle: 'summary', parent GET responses include category IDs alongside names:

GET /products/prod_502

Server Response (200 OK):

{
"data": {
"id": "prod_502",
"title": "Noise-Cancelling Studio Headphones",
"price": 299.99,
"categories": [
{ "id": "cat_101", "name": "Electronics" },
{ "id": "cat_102", "name": "Audio" }
]
}
}

3. Rejection on Unauthorized Custom Category

Section titled “3. Rejection on Unauthorized Custom Category”

Because allowCustomCategories defaults to false, attempting to assign a category that does not already exist in the database fails validation:

POST /products
Content-Type: application/json
{
"title": "Gardening Shovel",
"categories": ["Outdoor & Gardening"]
}

Server Response (422 Unprocessable Entity):

{
"errors": [
{
"field": "categories",
"message": "Category \"Outdoor & Gardening\" does not exist and custom categories are not allowed.",
"code": "E_VALIDATION_ERROR"
}
]
}

To attach an additional category to a product without overwriting existing assignments:

POST /products/prod_502/categories
Content-Type: application/json
{
"name": "Accessories"
}

Server Response (201 Created):

{
"data": {
"id": "cat_103",
"name": "Accessories"
}
}

To unlink a category, provide the category label in the URL path:

DELETE /products/prod_502/categories/Accessories

Server Response (204 No Content): (Empty response body confirming the association was removed)

If the category label is not found in the database, the API responds with:

Server Response (404 Not Found):

{
"error": "Relation record not found"
}

6. Listing Full Category Definitions via Sub-Resource

Section titled “6. Listing Full Category Definitions via Sub-Resource”

If the categories association is exposed with a list action in the API Modeler, clients can query the sub-resource endpoint to inspect full Category entity records (including primary keys and any additional metadata properties):

GET /products/prod_502/categories

Server Response (200 OK):

{
"data": [
{
"id": "cat_101",
"name": "Electronics"
},
{
"id": "cat_102",
"name": "Audio"
}
],
"meta": {
"total": 2,
"per_page": 20,
"current_page": 1,
"last_page": 1,
"has_more_pages": false
}
}

  • Categories vs. Tags:
    • Use Categories for formal, controlled classification schemes where categories are managed centrally by administrators (allowCustomCategories: false) and where clients may require IDs in responses (responseStyle: 'summary').
    • Use Tags for user-generated, multi-faceted keywords where open creation (allowCustomTags: true) and flat string arrays are standard.
  • Pre-Seeding Curated Categories: Since custom categories are blocked by default, pre-populate your taxonomy table using administrative API endpoints (POST /categories) or data migrations before opening client creation endpoints.
  • Target Entity Requirements (What is the “Target”?):
    • When you create an association in the Domain Modeler, it connects two entities:
      • The Source Entity: The resource being categorized (for example, Product or SupportTicket).
      • The Target Entity: The entity that stores the category taxonomy records (for example, Category, Department, or Genre).
    • Single Name Property: The target entity (Category) must define exactly one property annotated with the Name semantic module. The engine uses this property automatically as the category label.
    • Multi-Tenant Organization Scoping: When organization-level authorization is enabled, the target entity (Category) must include an association decorated with SemanticType.OrganizationResource to ensure category records are isolated per organization.
  • Choosing Cardinality:
    • Use multiple: true (Many-to-Many) if products or records belong to multiple functional categories.
    • Use multiple: false (Many-to-One) when business logic enforces a single primary category per item.