cuatrolabs-scm-ms / docs /taxonomy_api.md
rajeshbms's picture
feat: fixed adding and updaing taxonomies
d249b90

Taxonomy API

Endpoints

Method URL Description
POST /taxonomy/ Create or append taxonomy values
PUT /taxonomy/ Replace (overwrite) taxonomy field values

Both endpoints require a Bearer token (JWT). merchant_id is always extracted from the JWT — do not pass it in the request body.


POST /taxonomy/ — Create or Append

Overview

Handles both create and append of taxonomy data. The operation is determined automatically based on whether a taxonomy document already exists for the merchant.

How Create vs Append Works

Scenario Behaviour
No taxonomy document exists for the merchant Creates a new document (operation: "created")
Document already exists Appends new values using MongoDB $addToSet — no duplicates (operation: "updated")
is_delete: true is passed Removes the specified values from the arrays

The key behaviour for updates: values are appended, not replaced. If you send brands: ["Nike"] and the document already has ["Adidas"], the result will be ["Adidas", "Nike"]. To replace entirely, use PUT /taxonomy/.


PUT /taxonomy/ — Replace Field Values

Overview

Replaces the value of each provided field entirely. Only fields included in the request body are affected — omitted fields remain unchanged.

  • Method: PUT
  • URL: /taxonomy/
  • Auth: Bearer token (JWT) required
  • Prerequisite: A taxonomy document must already exist for the merchant (use POST to create it first)

How Replace Works

Scenario Behaviour
Field is included in request body Existing array is overwritten with the new values
Field is omitted from request body Field is left unchanged
Document does not exist Returns 422 — use POST /taxonomy/ to create first

Example: if the document has brands: ["Adidas", "Nike"] and you PUT with brands: ["Puma"], the result will be brands: ["Puma"].

Response — 200 OK

{
  "success": true,
  "message": "Taxonomy fields replaced successfully",
  "data": {
    "taxonomy_id": "64f1a2b3c4d5e6f7a8b9c0d1",
    "operation": "replaced",
    "merchant_id": "af59ab73-4ef4-43a0-aac3-6909f3ba87e4",
    "modified_count": 1
  },
  "timestamp": "2026-03-14T10:30:00.000Z"
}

Usage Example

curl -X PUT https://<host>/taxonomy/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "brands": ["Puma"],
    "categories": ["Hair", "Skin"]
  }'


Request Body

All fields are optional. Only send the fields you want to add/update.

{
  "brands": ["L'Oréal Professional", "Schwarzkopf"],
  "categories": ["Hair", "Skin", "Nails"],
  "lines": ["Hair Care", "Hair Styling"],
  "classes": ["Premium", "Luxury", "Basic"],
  "subcategories": {
    "Hair": ["Hair Cut", "Hair Color", "Hair Treatment"],
    "Skin": ["Facial", "Cleanup"]
  },
  "job_role": ["Senior Stylist", "Director", "Massage Therapist"],
  "specializations": ["Hairdresser", "Nail Technician", "Esthetician"],
  "languages": ["English", "Hindi", "Tamil"],
  "designation": ["CTO", "CEO", "RSM", "ASM"],
  "regions": ["East", "West", "North", "South"],
  "customer_group": ["VIP", "Regular", "Premium", "Gold"],
  "pos_tender_modes": ["Cash", "Credit Card", "Debit Card", "Mobile Payment"],
  "payment_types": ["Cash and Carry", "Credit"],
  "payment_methods": ["Bank Transfer", "Cash", "Cheque"],
  "pos_modes": ["Walk-in", "Appointment", "Online"],
  "pricing_model": ["Weight_Based", "Fixed_Rate", "Zone_Based"],
  "asset_location": ["Store Floor", "Warehouse", "Back Office"],
  "asset_category": ["Electronics", "Furniture", "Equipment"],
  "stock_bin_location": ["A1-01", "B2-15", "C3-07"],
  "branch_types": ["Flagship", "Outlet", "Franchise"],
  "store_types": ["Retail", "Wholesale", "Online"],
  "expense_types": ["Travel", "Office Supplies"],
  "region_codes": ["IN-KA-BLR", "IN-TN-CHN"],
  "states": ["Maharashtra", "Karnataka", "Tamil Nadu"],
  "relations": ["Parent", "Subsidiary", "Partner", "Vendor"],
  "document_types": ["Invoice", "Receipt", "Purchase Order"],
  "merchant_types": [
    { "code": "cnf", "label": "CNF", "order": 1 },
    { "code": "distributor", "label": "Distributor", "order": 2 },
    { "code": "retail", "label": "Retail", "order": 3 }
  ],
  "is_delete": false
}

Field Reference

Simple String Arrays

All of these follow the same pattern — send an array of strings to add values:

Field Description Example
brands Product brands ["L'Oréal", "Schwarzkopf"]
categories Product categories ["Hair", "Skin", "Nails"]
lines Product lines ["Hair Care", "Hair Styling"]
classes Product classes ["Premium", "Luxury"]
job_role Employee job roles ["Senior Stylist", "Director"]
specializations Employee specializations ["Hairdresser", "Esthetician"]
languages Languages ["English", "Hindi"]
designation Employee designations ["CTO", "RSM", "ASM"]
regions Region groups ["East", "West", "North"]
customer_group Customer groups ["VIP", "Regular", "Gold"]
pos_tender_modes POS tender modes ["Cash", "Credit Card"]
payment_types Payment types ["Cash and Carry", "Credit"]
payment_methods Payment methods ["Bank Transfer", "Cash"]
pos_modes POS modes ["Walk-in", "Appointment"]
pricing_model Pricing models ["Fixed_Rate", "Weight_Based"]
asset_location Asset locations ["Store Floor", "Warehouse"]
asset_category Asset categories ["Electronics", "Furniture"]
stock_bin_location Stock bin locations ["A1-01", "B2-15"]
branch_types Branch types ["Flagship", "Outlet"]
store_types Store types ["Retail", "Wholesale"]
expense_types Expense types ["Travel", "Office Supplies"]
region_codes Region codes ["IN-KA-BLR", "IN-TN-CHN"]
states States ["Maharashtra", "Karnataka"]
relations Merchant relations ["Parent", "Subsidiary"]
document_types Document types ["Invoice", "Receipt"]

subcategories — Object (category → values)

Maps a parent category to its subcategory values.

{
  "subcategories": {
    "Hair": ["Hair Cut", "Hair Color", "Hair Treatment"],
    "Skin": ["Facial", "Cleanup", "Peel"]
  }
}

merchant_types — Structured Array

Each item must have all three fields:

Field Type Required Description
code string Unique identifier (e.g., "cnf", "distributor")
label string Display name (e.g., "CNF", "Distributor")
order integer ≥ 1 Sort order — must be positive, no duplicates
{
  "merchant_types": [
    { "code": "cnf", "label": "CNF", "order": 1 },
    { "code": "distributor", "label": "Distributor", "order": 2 }
  ]
}

is_delete — Boolean (default: false)

When true, the values in the payload are removed from the existing arrays instead of being added.

{
  "brands": ["OldBrand"],
  "is_delete": true
}

Response

Success — 201 Created

{
  "success": true,
  "message": "Taxonomy operation completed successfully",
  "data": {
    "taxonomy_id": "64f1a2b3c4d5e6f7a8b9c0d1",
    "operation": "created",
    "merchant_id": "af59ab73-4ef4-43a0-aac3-6909f3ba87e4",
    "modified_count": 1
  },
  "timestamp": "2026-03-14T10:30:00.000Z"
}
Field Description
data.taxonomy_id MongoDB document ID of the taxonomy record
data.operation "created" for new documents, "updated" for existing
data.modified_count Number of documents modified (typically 1)

Usage Examples

1. Initial Setup — Create taxonomy for a new merchant

Send all the values you want to seed:

curl -X POST https://<host>/taxonomy/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "brands": ["L'\''Oréal Professional", "Schwarzkopf"],
    "categories": ["Hair", "Skin", "Nails"],
    "job_role": ["Senior Stylist", "Junior Stylist"],
    "customer_group": ["VIP", "Regular"]
  }'

2. Add new values to existing taxonomy

Only send the new values — existing ones are preserved:

curl -X POST https://<host>/taxonomy/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "brands": ["Kerastase"],
    "categories": ["Makeup"]
  }'

Result: brands will now contain ["L'Oréal Professional", "Schwarzkopf", "Kerastase"]

3. Delete specific values

curl -X POST https://<host>/taxonomy/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "brands": ["OldBrand"],
    "is_delete": true
  }'

4. Add subcategories

curl -X POST https://<host>/taxonomy/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "subcategories": {
      "Hair": ["Hair Cut", "Hair Color"],
      "Skin": ["Facial", "Cleanup"]
    }
  }'

5. Set up merchant types

curl -X POST https://<host>/taxonomy/ \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_types": [
      { "code": "cnf", "label": "CNF", "order": 1 },
      { "code": "distributor", "label": "Distributor", "order": 2 },
      { "code": "retail", "label": "Retail", "order": 3 }
    ]
  }'

Error Responses

Status Scenario
401 Unauthorized Missing or invalid JWT token
403 Forbidden User has no associated merchant
422 Unprocessable Entity Validation failure (e.g., order <= 0, empty code)
500 Internal Server Error Database operation failed

Validation Error Example (422)

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Order must be a positive integer",
    "details": {}
  }
}

Important Notes

  1. merchant_id is never sent in the body — it is always extracted from the JWT token.
  2. Append-only by default — sending existing values again has no effect (idempotent for duplicates).
  3. merchant_types deduplication — duplicate code or order values within the same request are silently skipped; the first occurrence wins.
  4. String sanitization — all string values are trimmed of whitespace and empty strings are ignored automatically.
  5. subcategories merge — new keys are added, existing keys get new values appended. Existing subcategory values are not removed unless is_delete: true.