Tags (Semantic Module)
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.
Technical Specifications
Section titled “Technical Specifications”| Attribute | Specification |
|---|---|
| Scope | Association (Applied to a multi-valued relationship between entities) |
| Relationship Type | Many-to-Many (multiple: true required; single-valued associations are rejected at compile time) |
| Target Entity | Exactly one target entity (union targets are not supported) with exactly one property annotated with SemanticType.Name |
| Hierarchy Guard | Strictly flat; target entity cannot have self-referential associations |
| Default Custom Tags | Enabled (allowCustomTags: true) |
| Default Case Sensitivity | Case-insensitive (caseSensitive: false) |
| Default Character Set | ASCII only (allowUnicode: false) |
| Inline Response Format | Flat string array (string[], e.g., ["wireless", "audio"]) |
| Database Architecture | Automatic join table provisioning with foreign key cascading and composite primary key |
Key Capabilities
Section titled “Key Capabilities”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 a422 Unprocessable Entitystatus. To maintain a curated catalog, administrators pre-seed tags via direct tag endpoints (POST /tags), while client-facing mutations are constrained to existing records.
3. Atomic Replace-All Synchronization
Section titled “3. Atomic Replace-All Synchronization”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
tagsfield entirely in aPATCHpayload preserves all existing tags without modification.
4. Case Normalization (caseSensitive)
Section titled “4. Case Normalization (caseSensitive)”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.
6. Label-First Sub-Resource Operations (Link & Unlink)
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, returning204 No Content.
7. Multi-Tenant Organization Scoping
Section titled “7. Multi-Tenant Organization Scoping”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.
Configuration Options
Section titled “Configuration Options”When configuring the Tags semantic on an association in the Domain Modeler, you can adjust the following parameters:
| Option | Type | Default | Description |
|---|---|---|---|
allowCustomTags | boolean | true | When true, unrecognized tag labels are created automatically. When false, only pre-existing database tag records can be assigned. |
caseSensitive | boolean | false | When false, performs case-insensitive lookups so casing variations share a single record. When true, matches exact casing. |
allowUnicode | boolean | false | When false, restricts tag labels to ASCII characters. When true, permits international scripts and special characters. |
API Lifecycle Examples
Section titled “API Lifecycle Examples”1. Linking Tags on Record Creation
Section titled “1. Linking Tags on Record Creation”The client submits a new article with an array of tag strings:
POST /articlesContent-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"] }}2. Updating Tags with Replace-All
Section titled “2. Updating Tags with Replace-All”Submitting a new array on an existing record replaces the previous associations:
PATCH /articles/art_901Content-Type: application/json
{ "tags": ["serverless", "architecture"]}Server Response (200 OK):
{ "data": { "id": "art_901", "title": "Building Scalable APIs with API Now!", "tags": ["serverless", "architecture"] }}3. Rejection on Unauthorized Custom Tag
Section titled “3. Rejection on Unauthorized Custom Tag”When an association is configured with allowCustomTags: false, submitting a tag label that does not exist in the database is rejected:
POST /articlesContent-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/tagsContent-Type: application/json
{ "name": "performance"}Server Response (201 Created):
{ "data": { "id": "tag_554", "name": "performance" }}5. Removing a Tag by Name
Section titled “5. Removing a Tag by Name”To unlink a tag, pass the human-readable tag label directly in the URL:
DELETE /articles/art_901/tags/performanceServer 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/tagsServer 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 }}Domain Modeler Best Practices
Section titled “Domain Modeler Best Practices”- Tags vs. Categories: Use
Tagsfor informal, flat, non-hierarchical keywords. If your domain requires structured classification with configurable response styles (such as returning object summaries or labels), use theCategoriessemantic 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,
ArticleorProduct). - The Target Entity: The entity that stores the tag vocabulary records (for example,
Tag,Topic, orSkill).
- The Source Entity: The resource being tagged (for example,
- Single
NameProperty: The target entity (Tag) must define exactly one property annotated with theNamesemantic 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, useCategoriesinstead. - Multi-Tenant Organization Scoping: In APIs configured with an organization authorization strategy (
EnterpriseRBACorOrganizationRBAC), the target entity (Tag) must have an association decorated withSemanticType.OrganizationResourceso tags are isolated per organization. The compiler verifies this at build time (tags_target_not_org_scoped_in_org_api).
- When you create an association in the Domain Modeler, it connects two entities:
- Enforcing Curated Catalogs: In enterprise systems, set
allowCustomTags: falseon 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.