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

Tags (Semantic Module)

← Back to Modules Reference

The Tags semantic module annotates a Many-to-Many association to provide structured or ad-hoc keyword labeling (a folksonomy).

In modern applications, records frequently need flexible, multi-valued keywords to power search filters, discovery badges, and content grouping:

  • A blog post tagged with ["api-design", "security", "graphql"].
  • An e-commerce product tagged with ["bestseller", "eco-friendly", "clearance"].
  • A customer support ticket tagged with ["billing", "tier-1", "urgent"].

Rather than requiring API consumers to manually query tag entities, resolve primary keys, and execute separate association join inserts, decorating an association with Tags unlocks an automated name-based find-or-create workflow. Clients pass simple string arrays directly in parent write payloads, while the API Now Serverless Engine handles validation, case normalization, database lookups, and transactional join-table linking.


AttributeSpecification
ScopeAssociation (Applied to a multi-valued relationship between entities)
Relationship TypeMany-to-Many (multiple: true required; single-valued associations are rejected at compile time)
Target EntityExactly one target entity (union targets are not supported) with exactly one property annotated with SemanticType.Name
Hierarchy GuardStrictly flat; target entity cannot have self-referential associations
Default Custom TagsEnabled (allowCustomTags: true)
Default Case SensitivityCase-insensitive (caseSensitive: false)
Default Character SetASCII only (allowUnicode: false)
Inline Response FormatFlat string array (string[], e.g., ["wireless", "audio"])
Database ArchitectureAutomatic join table provisioning with foreign key cascading and composite primary key

1. Simplified Name-Based Tagging (Find-or-Create)

Section titled “1. Simplified Name-Based Tagging (Find-or-Create)”

API consumers never need to look up internal tag primary keys before associating them with records. When creating or updating a parent record (such as an article or product), the client passes a flat array of strings:

  • Existing Tags: If the tag label already exists in the database for the active organization, the engine reuses the existing tag record.
  • New Tags (allowCustomTags: true): If a tag does not yet exist, the engine automatically inserts a new tag record into the target table and links it within the active database transaction.

2. Controlled Tag Vocabularies (allowCustomTags)

Section titled “2. Controlled Tag Vocabularies (allowCustomTags)”

You can control whether callers can introduce arbitrary tags or must link from an existing tag catalog:

  • Open Folksonomy (allowCustomTags: true, default): Callers can assign existing tags or create new tags on the fly. Ideal for blog topics, user notes, and collaborative labeling.
  • Curated Allowed Catalog (allowCustomTags: false): Callers may only assign tags that already exist as records in the database. Submitting an unrecognized tag label immediately rejects the request with a 422 Unprocessable Entity status. To maintain a curated catalog, administrators pre-seed tags via direct tag endpoints (POST /tags), while client-facing mutations are constrained to existing records.

When updating a parent record via POST or PATCH, submitting a tag array performs a full synchronization (replace-all):

  • Existing pivot rows in the join table are cleared, and the new set of tags is linked atomically within the transaction.
  • If duplicate tags are passed in the payload array (or resolve to the same record after case normalization), duplicates are deduplicated cleanly without database constraint errors.
  • Partial Updates: Omitting the tags field entirely in a PATCH payload preserves all existing tags without modification.

To prevent fragmentation across search filters—such as having "React", "react", and "REACT" stored as duplicate records—case sensitivity is disabled by default (caseSensitive: false). The runtime normalizes tag strings during lookup so variants resolve to the same underlying record.

5. Character Set Validation (allowUnicode)

Section titled “5. Character Set Validation (allowUnicode)”

By default (allowUnicode: false), tags are restricted to standard ASCII characters (code points 0–127). Submitting non-ASCII characters or emojis immediately triggers a 422 Unprocessable Entity error. When international or multi-language tagging is required, enabling allowUnicode: true permits accented characters, non-Latin scripts (e.g., "家電"), and emojis.

Section titled “6. Label-First Sub-Resource Operations (Link & Unlink)”

In addition to inline arrays on parent resources, tags can be managed via dedicated sub-resource routes without primary key lookups:

  • Append Tag (POST /products/:id/tags): Accepts {"name": "bluetooth"}. Appends the tag to the product without modifying existing tags.
  • Remove Tag (DELETE /products/:id/tags/:label): The path parameter is the human-readable tag label (e.g., /tags/bluetooth), not a database primary key. The engine resolves the label to its internal foreign key and removes the pivot row, returning 204 No Content.

When an API uses an organization-based authorization strategy (EnterpriseRBAC or OrganizationRBAC), the target tag entity must carry an association annotated with SemanticType.OrganizationResource. Tag lookups, creation, and unique constraints are automatically scoped to the active organization, ensuring strict tenant isolation while sharing a unified tag vocabulary across all projects within that organization.


When configuring the Tags semantic on an association in the Domain Modeler, you can adjust the following parameters:

OptionTypeDefaultDescription
allowCustomTagsbooleantrueWhen true, unrecognized tag labels are created automatically. When false, only pre-existing database tag records can be assigned.
caseSensitivebooleanfalseWhen false, performs case-insensitive lookups so casing variations share a single record. When true, matches exact casing.
allowUnicodebooleanfalseWhen false, restricts tag labels to ASCII characters. When true, permits international scripts and special characters.

The client submits a new article with an array of tag strings:

POST /articles
Content-Type: application/json
{
"title": "Building Scalable APIs with API Now!",
"content": "A comprehensive guide to declarative backend modeling...",
"tags": ["serverless", "api-design", "getting-started"]
}

Server Response (201 Created):

{
"data": {
"id": "art_901",
"title": "Building Scalable APIs with API Now!",
"content": "A comprehensive guide to declarative backend modeling...",
"tags": ["serverless", "api-design", "getting-started"]
}
}

Submitting a new array on an existing record replaces the previous associations:

PATCH /articles/art_901
Content-Type: application/json
{
"tags": ["serverless", "architecture"]
}

Server Response (200 OK):

{
"data": {
"id": "art_901",
"title": "Building Scalable APIs with API Now!",
"tags": ["serverless", "architecture"]
}
}

When an association is configured with allowCustomTags: false, submitting a tag label that does not exist in the database is rejected:

POST /articles
Content-Type: application/json
{
"title": "Quarterly Financial Review",
"tags": ["internal-confidential"]
}

Server Response (422 Unprocessable Entity):

{
"errors": [
{
"field": "tags",
"message": "Tag \"internal-confidential\" does not exist and custom tags are not allowed.",
"code": "E_VALIDATION_ERROR"
}
]
}

4. Appending a Tag via Sub-Resource Endpoint

Section titled “4. Appending a Tag via Sub-Resource Endpoint”

You can append a tag to a record without modifying other tags by calling the sub-resource link endpoint with the tag’s name:

POST /articles/art_901/tags
Content-Type: application/json
{
"name": "performance"
}

Server Response (201 Created):

{
"data": {
"id": "tag_554",
"name": "performance"
}
}

To unlink a tag, pass the human-readable tag label directly in the URL:

DELETE /articles/art_901/tags/performance

Server Response (204 No Content): (Empty response body indicating the tag association was removed from the article)

If the specified tag label does not exist, the endpoint returns:

Server Response (404 Not Found):

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

6. Listing Full Tag Definitions via Sub-Resource

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

If the tags association is exposed with a list action in the API Modeler, clients can query the sub-resource endpoint to inspect full Tag entity definitions (including primary keys, audit timestamps, and any custom metadata properties):

GET /articles/art_901/tags

Server Response (200 OK):

{
"data": [
{
"id": "tag_01",
"name": "serverless"
},
{
"id": "tag_02",
"name": "architecture"
}
],
"meta": {
"total": 2,
"per_page": 20,
"current_page": 1,
"last_page": 1,
"has_more_pages": false
}
}

  • Tags vs. Categories: Use Tags for informal, flat, non-hierarchical keywords. If your domain requires structured classification with configurable response styles (such as returning object summaries or labels), use the Categories semantic module.
  • 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 tagged (for example, Article or Product).
      • The Target Entity: The entity that stores the tag vocabulary records (for example, Tag, Topic, or Skill).
    • Single Name Property: The target entity (Tag) must define exactly one property annotated with the Name semantic module. The engine uses this property automatically as the tag’s display label (e.g., tag.name).
    • Strictly Flat (No Self-References): The target entity cannot have self-referential relationships (such as a tag linking to a parent tag). Tags are flat keywords by design; introducing hierarchy triggers a build error (tags_target_has_self_reference). If you need parent-child trees, use Categories instead.
    • Multi-Tenant Organization Scoping: In APIs configured with an organization authorization strategy (EnterpriseRBAC or OrganizationRBAC), the target entity (Tag) must have an association decorated with SemanticType.OrganizationResource so tags are isolated per organization. The compiler verifies this at build time (tags_target_not_org_scoped_in_org_api).
  • Enforcing Curated Catalogs: In enterprise systems, set allowCustomTags: false on the association. Pre-populate valid tags in your database (e.g., via administrative endpoints or data migrations). This prevents typographical duplicates such as "wifi", "wi-fi", and "wireless" from polluting your search indexes.