TMS Shop Module

  1011. TMS Shop
SKYBIZ API — TMS Module

TMS Shop

The TMS Shop module allows you to retrieve, create, update, and delete shop/outlet location records in your SKYBIZ TMS shop master. Supports read, create, update, and delete operations.

Endpoint: /apiv2/modules/tms_shop.php

Required Permissions: TMS Shop — Read, TMS Shop — Create, TMS Shop — Update, TMS Shop — Delete (as applicable to the action used)

Primary Key: LocationCode — unique per shop, and fixed once created (cannot be changed via update).

Batch Limit: Maximum 500 documents per request for create, update, and delete.

Data Wrapper Key: Documents for this module are wrapped under shop_data.


Allowed Fields

These are the only fields recognized by this module — for reading, creating, and updating. Any other field name sent in a create or update request is rejected. Every field in tms_shop is a string — there are no numeric columns on this module.

Field Type Description
LocationCode string Unique shop/outlet location code. Primary key. Required for every action. Fixed — cannot be changed once created
LocationName string Shop/outlet name
Address string Shop address
PostCode string Postal code
City string City
State string State
Area string General area/region the shop falls under
ContactNo string Shop contact phone number
MallID string Identifier of the mall the shop is located in, if applicable
MallName string Name of the mall the shop is located in, if applicable
ShopAreaCode string Retail zone/cluster code this shop belongs to
ShopAreaName string Retail zone/cluster name this shop belongs to
MnemonicCode string Short mnemonic code used to reference the shop
ShopCategory string Category classification for the shop
BoutiqueGroup string Boutique group classification for the shop

Read TMS Shop

Retrieves shop location records. No date range is required for this module. You may optionally select specific fields and/or filter by exact field values.

Base Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "read",
  "fields": ["LocationCode", "LocationName", "City", "State", "ShopCategory"],
  "filters": {
    "State": "Selangor"
  }
}

Request Parameters

Parameter Description Required
api_key Your API key Yes
api_secret Your API secret Yes
action Must be "read" Yes
fields Array of field names to return. Any name not in the Allowed Fields list is silently dropped. Omit or leave empty to return all allowed fields No
filters Object of field: value pairs. Each is applied as an exact-match (=) condition, combined with AND. Only keys in the Allowed Fields list are applied; other keys are ignored No

Read Rules

  • Results are ordered by LocationCode
  • No date range parameter exists or is required for this module
  • No pagination — all matching rows are returned in a single response
  • Filters are exact-match only (no partial/LIKE matching, no operators)
  • If fields is provided but none of the values match an allowed field, the request is rejected with "No valid fields requested"

Example 1 — Get All Shops (All Fields)

Request

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "read"
}

Example 2 — Get Selected Fields, Filtered by State

Request

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "read",
  "fields": ["LocationCode", "LocationName", "City", "ShopCategory"],
  "filters": {
    "State": "Selangor"
  }
}

Sample Code for making an API READ request using php

<?php
header('Content-Type: application/json');

// ============================================================
// STEP 1: CONFIGURATION
// ============================================================

$API_KEY = "your-api-key";
$API_SECRET = "your-api-secret";
$ACTION = "read";

// ============================================================
// STEP 2: ENDPOINT
// ============================================================

$BASE_URL = "https://domain-name/01/clientportal/apiv2/modules"; //(replace it with your skybiz domain name url)
$ENDPOINT = "tms_shop.php";

// ============================================================
// STEP 3: REQUEST PARAMETERS (both optional)
// ============================================================

$FIELDS = ["LocationCode", "LocationName", "City", "ShopCategory"];
$FILTERS = [
    "State" => "Selangor"
];

// ============================================================
// STEP 4: BUILD PAYLOAD
// ============================================================

$payload = [
    "api_key"    => $API_KEY,
    "api_secret" => $API_SECRET,
    "action"     => $ACTION,
    "fields"     => $FIELDS,
    "filters"    => $FILTERS
];

// ============================================================
// STEP 5: SEND REQUEST
// ============================================================

$url = rtrim($BASE_URL, '/') . '/' . ltrim($ENDPOINT, '/');

$ch = curl_init($url);

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_TIMEOUT => 30,
]);

$response = curl_exec($ch);

if ($response === false) {
    echo json_encode([
        "status" => "error",
        "timestamp" => date("c"),
        "request_id" => uniqid("req_"),
        "message" => curl_error($ch)
    ], JSON_PRETTY_PRINT);
    curl_close($ch);
    exit;
}

curl_close($ch);

$result = json_decode($response, true);

echo json_encode([
    "status" => $result['status'] ?? "error",
    "timestamp" => date("c"),
    "request_id" => $result['request_id'] ?? uniqid("req_"),
    "data" => $result['data'] ?? null,
    "message" => $result['message'] ?? null
], JSON_PRETTY_PRINT);

Response (Success)

{
  "status": "response",
  "timestamp": "2026-09-25T04:07:04+00:00",
  "request_id": "req_69f2d56891e57",
  "data": {
    "requested_by": "your-api-key",
    "mode": "2",
    "total_returned": 2,
    "data": [
      {
        "LocationCode": "SHOP001",
        "LocationName": "Sunway Pyramid Outlet",
        "City": "Petaling Jaya",
        "ShopCategory": "Mall"
      },
      {
        "LocationCode": "SHOP002",
        "LocationName": "Mid Valley Outlet",
        "City": "Kuala Lumpur",
        "ShopCategory": "Mall"
      }
    ]
  }
}

Create TMS Shop

Creates one or more shop location records. All documents are validated before any are saved. If any document fails validation, the entire batch is rejected — nothing is inserted.

Field Rules

Field Type Rule
LocationCode string Compulsory. Must not be empty. Must not already exist — checked both within the request batch and against existing records
LocationName, Address, PostCode, City, State, Area, ContactNo, MallID, MallName, ShopAreaCode, ShopAreaName, MnemonicCode, ShopCategory, BoutiqueGroup string Optional. Blank/empty if not provided

⚠️ Duplicate Check Order: The batch is first checked for duplicate LocationCode values within the same request, then checked against existing records in SKYBIZ. Either kind of duplicate fails the entire batch, not just the duplicate document.

Base Create Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "create",
  "shop_data": {
    "documents": [
      {
        "LocationCode": "SHOP001",
        "LocationName": "Sunway Pyramid Outlet",
        "Address": "3 Jalan PJS 11/15, Bandar Sunway",
        "PostCode": "47500",
        "City": "Petaling Jaya",
        "State": "Selangor",
        "Area": "Klang Valley",
        "ContactNo": "0322345678",
        "MallID": "MALL001",
        "MallName": "Sunway Pyramid",
        "ShopAreaCode": "PJ01",
        "ShopAreaName": "Petaling Jaya Central",
        "MnemonicCode": "SPY01",
        "ShopCategory": "Mall",
        "BoutiqueGroup": "Flagship"
      }
    ]
  }
}

Sample Code for making an API CREATE request using php

<?php
header('Content-Type: application/json');

// ============================================================
// STEP 1: CONFIGURATION
// ============================================================

$API_KEY = "your-api-key";
$API_SECRET = "your-api-secret";
$ACTION = "create";

// ============================================================
// STEP 2: ENDPOINT
// ============================================================

$BASE_URL = "https://domain-name/01/clientportal/apiv2/modules"; //(replace it with your skybiz domain name url)
$ENDPOINT = "tms_shop.php";

// ============================================================
// STEP 3: DATA KEY
// ============================================================

$DATA_KEY = "shop_data";

// ============================================================
// STEP 4: BUILD YOUR DOCUMENTS ARRAY
// ============================================================

$DOCUMENTS = [];

$DOCUMENTS[] = [
    "LocationCode"  => "SHOP001",
    "LocationName"  => "Sunway Pyramid Outlet",
    "Address"       => "3 Jalan PJS 11/15, Bandar Sunway",
    "PostCode"      => "47500",
    "City"          => "Petaling Jaya",
    "State"         => "Selangor",
    "Area"          => "Klang Valley",
    "ContactNo"     => "0322345678",
    "MallID"        => "MALL001",
    "MallName"      => "Sunway Pyramid",
    "ShopAreaCode"  => "PJ01",
    "ShopAreaName"  => "Petaling Jaya Central",
    "MnemonicCode"  => "SPY01",
    "ShopCategory"  => "Mall",
    "BoutiqueGroup" => "Flagship"
];

// ============================================================
// STEP 5: CLIENT-SIDE COUNT CHECK (Please do not modify this)
// ============================================================

$totalDocuments = 0;

foreach ($DOCUMENTS as $document) {
    $totalDocuments++;

    if (empty($document['LocationCode'])) {
        echo json_encode([
            "status" => "REJECTED_BY_CLIENT",
            "timestamp" => date("c"),
            "request_id" => uniqid("req_"),
            "message" => "CLIENT-SIDE REJECTION: Document at position {$totalDocuments} has empty LocationCode",
            "action_required" => "Fix the document before sending to server"
        ], JSON_PRETTY_PRINT);
        exit;
    }
}

if ($totalDocuments > 500) {
    echo json_encode([
        "status" => "REJECTED_BY_CLIENT",
        "timestamp" => date("c"),
        "request_id" => uniqid("req_"),
        "message" => "CLIENT-SIDE REJECTION: You have {$totalDocuments} documents. Maximum is 500.",
        "your_document_count" => $totalDocuments,
        "max_allowed" => 500,
        "action_required" => "Reduce your documents to 500 or less BEFORE sending to server"
    ], JSON_PRETTY_PRINT);
    exit;
}

echo "Sending {$totalDocuments} document(s) to server...\n\n";

// ============================================================
// STEP 6: SEND REQUEST
// ============================================================

$url = rtrim($BASE_URL, '/') . '/' . ltrim($ENDPOINT, '/');

$payload = [
    "api_key"    => $API_KEY,
    "api_secret" => $API_SECRET,
    "action"     => $ACTION,
    $DATA_KEY    => ["documents" => $DOCUMENTS]
];

$ch = curl_init($url);

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_TIMEOUT => 120,
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);

echo "=== SERVER RESPONSE ===\n";
echo json_encode($result, JSON_PRETTY_PRINT);

Response (Success)

{
  "status": "response",
  "timestamp": "2026-09-25T10:30:00+08:00",
  "request_id": "req_69fc0070d83cf",
  "data": {
    "requested_by": "your-api-key",
    "mode": "2",
    "summary": {
      "total_documents": 1,
      "inserted": 1,
      "failed": 0
    },
    "successful_documents": ["SHOP001"],
    "failed_documents": [],
    "fail_details": {
      "duplicate_in_request": [],
      "duplicate_shops": [],
      "validation_errors": []
    }
  }
}

Response (Error — Duplicate Shop)

{
  "status": "response",
  "timestamp": "2026-09-25T10:30:00+08:00",
  "request_id": "req_69fc03e446a20",
  "data": {
    "requested_by": "your-api-key",
    "mode": "2",
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "successful_documents": [],
    "failed_documents": ["SHOP001"],
    "fail_details": {
      "duplicate_in_request": [],
      "duplicate_shops": [
        {
          "locationcode": "SHOP001",
          "error": "Shop Location code already exists"
        }
      ],
      "validation_errors": []
    }
  }
}

Update TMS Shop

Updates one or more existing shop location records, matched by LocationCode. This is a partial update — only send the fields you want to change, plus LocationCode to identify the record. All documents are pre-validated before anything is written; if any document in the batch fails, the entire batch is rejected.

Field Rules

Field Rule
LocationCode Compulsory in every document — used to look up the record. Fixed — including it only identifies the row, it is never itself updated
Any other Allowed Field Optional — include only the fields you want to change. At least one field besides LocationCode must be present
Any field not in the Allowed Fields list Not permitted — fails the whole batch

Pre-flight Validation (checked before any write, whole batch fails together)

  • Every document must have a non-empty LocationCode
  • No duplicate LocationCode within the same request batch
  • No unrecognized field names (anything outside the Allowed Fields list)
  • Every document must include at least one field to update besides LocationCode — sending only LocationCode is rejected as “nothing to update”
  • Every LocationCode must already exist in SKYBIZ — if even one is not found, the whole batch is rejected

⚠️ All-or-Nothing: A single bad document (not found, duplicate, unknown field, or nothing to update) causes every document in the request — including the valid ones — to fail.

Base Update Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "update",
  "shop_data": {
    "documents": [
      {
        "LocationCode": "SHOP001",
        "ContactNo": "0322345699",
        "ShopCategory": "Flagship Mall"
      }
    ]
  }
}

Sample Code for making an API UPDATE request using php

<?php
header('Content-Type: application/json');

$API_KEY = "your-api-key";
$API_SECRET = "your-api-secret";
$ACTION = "update";

$BASE_URL = "https://domain-name/01/clientportal/apiv2/modules"; //(replace it with your skybiz domain name url)
$ENDPOINT = "tms_shop.php";

$DOCUMENTS = [];

// Only send LocationCode + the fields you want to change
$DOCUMENTS[] = [
    "LocationCode" => "SHOP001",
    "ContactNo"    => "0322345699",
    "ShopCategory" => "Flagship Mall"
];

if (count($DOCUMENTS) > 500) {
    echo json_encode([
        "status" => "REJECTED_BY_CLIENT",
        "message" => "Maximum 500 documents per request."
    ], JSON_PRETTY_PRINT);
    exit;
}

$url = rtrim($BASE_URL, '/') . '/' . ltrim($ENDPOINT, '/');

$payload = [
    "api_key"    => $API_KEY,
    "api_secret" => $API_SECRET,
    "action"     => $ACTION,
    "shop_data"  => ["documents" => $DOCUMENTS]
];

$ch = curl_init($url);

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_TIMEOUT => 120,
]);

$response = curl_exec($ch);
curl_close($ch);

echo json_encode(json_decode($response, true), JSON_PRETTY_PRINT);

Response (Success)

{
  "status": "response",
  "timestamp": "2026-09-25T11:00:00+08:00",
  "request_id": "req_69fc1234abcd",
  "data": {
    "requested_by": "your-api-key",
    "mode": "2",
    "summary": {
      "total_documents": 1,
      "updated": 1,
      "not_found": 0,
      "failed": 0
    },
    "successful_documents": ["SHOP001"],
    "not_found_documents": [],
    "failed_documents": [],
    "fail_details": {
      "not_found": [],
      "nothing_to_update": [],
      "unknown_fields": [],
      "validation_errors": []
    }
  }
}

Response (Error — Shop Not Found)

{
  "status": "response",
  "timestamp": "2026-09-25T11:00:00+08:00",
  "request_id": "req_69fc5678efgh",
  "data": {
    "requested_by": "your-api-key",
    "mode": "2",
    "summary": {
      "total_documents": 1,
      "updated": 0,
      "not_found": 1,
      "failed": 1
    },
    "successful_documents": [],
    "not_found_documents": ["SHOP999"],
    "failed_documents": ["SHOP999"],
    "fail_details": {
      "not_found": ["SHOP999"],
      "nothing_to_update": [],
      "unknown_fields": [],
      "validation_errors": []
    }
  }
}

Response (Error — Unknown Field)

{
  "status": "response",
  "data": {
    "summary": { "total_documents": 1, "updated": 0, "not_found": 0, "failed": 1 },
    "successful_documents": [],
    "not_found_documents": [],
    "failed_documents": ["SHOP001"],
    "fail_details": {
      "not_found": [],
      "nothing_to_update": [],
      "unknown_fields": {
        "SHOP001": ["OpeningHours"]
      },
      "validation_errors": []
    }
  }
}

Delete TMS Shop

Deletes one or more shop location records, matched by LocationCode. This permanently removes the record — there is no undo. All documents are pre-validated before anything is deleted; if any document fails, the entire batch is rejected.

Pre-flight Validation (checked before any write, whole batch fails together)

  • Every document must have a non-empty LocationCode
  • No duplicate LocationCode within the same request batch
  • Every LocationCode must already exist in SKYBIZ — if even one is not found, the whole batch is rejected and nothing is deleted

Base Delete Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "delete",
  "shop_data": {
    "documents": [
      { "LocationCode": "SHOP001" },
      { "LocationCode": "SHOP002" }
    ]
  }
}

Sample Code for making an API DELETE request using php

<?php
header('Content-Type: application/json');

$API_KEY = "your-api-key";
$API_SECRET = "your-api-secret";
$ACTION = "delete";

$BASE_URL = "https://domain-name/01/clientportal/apiv2/modules"; //(replace it with your skybiz domain name url)
$ENDPOINT = "tms_shop.php";

$DOCUMENTS = [
    ["LocationCode" => "SHOP001"],
    ["LocationCode" => "SHOP002"]
];

if (count($DOCUMENTS) > 500) {
    echo json_encode([
        "status" => "REJECTED_BY_CLIENT",
        "message" => "Maximum 500 documents per request."
    ], JSON_PRETTY_PRINT);
    exit;
}

$url = rtrim($BASE_URL, '/') . '/' . ltrim($ENDPOINT, '/');

$payload = [
    "api_key"    => $API_KEY,
    "api_secret" => $API_SECRET,
    "action"     => $ACTION,
    "shop_data"  => ["documents" => $DOCUMENTS]
];

$ch = curl_init($url);

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($payload),
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
    CURLOPT_TIMEOUT => 120,
]);

$response = curl_exec($ch);
curl_close($ch);

echo json_encode(json_decode($response, true), JSON_PRETTY_PRINT);

Response (Success)

{
  "status": "response",
  "timestamp": "2026-09-25T11:15:00+08:00",
  "request_id": "req_69fc9999ijkl",
  "data": {
    "requested_by": "your-api-key",
    "mode": "2",
    "summary": {
      "total_documents": 2,
      "deleted": 2,
      "not_found": 0,
      "failed": 0
    },
    "successful_documents": ["SHOP001", "SHOP002"],
    "not_found_documents": [],
    "failed_documents": [],
    "fail_details": {
      "not_found": [],
      "validation_errors": []
    }
  }
}

Response (Error — Shop Not Found)

{
  "status": "response",
  "data": {
    "summary": { "total_documents": 1, "deleted": 0, "not_found": 1, "failed": 1 },
    "successful_documents": [],
    "not_found_documents": ["SHOP999"],
    "failed_documents": ["SHOP999"],
    "fail_details": {
      "not_found": ["SHOP999"],
      "validation_errors": []
    }
  }
}

Rate Limits & Quota

  • Rate limit: 5 requests per 10 seconds, enforced per IP address and per API key. Exceeding it returns HTTP 429 with "Rate limit exceeded"
  • Monthly quota: checked only on create, against the record count already used this month for TMS Shop. read, update, and delete are not quota-limited
  • Batch cap: 500 documents per request for create, update, and delete (not applicable to read)

Error Responses

Missing API Credentials

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "API key and secret are required"
}

Invalid API Credentials

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "Invalid API credentials"
}

Expired API Key

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "API key expired on 2026-08-01. Please delete the expired credentials and create a new API key set to continue using the API."
}

Missing / Invalid Action

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "action is required. Use 'read', 'create', 'update', or 'delete'"
}

No Permission for Action

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "tms_shop update permission denied"
}

Missing shop_data / documents (CREATE / UPDATE / DELETE)

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "shop_data.documents must be a non-empty array"
}

Batch Limit Exceeded (CREATE / UPDATE / DELETE)

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "Maximum 500 documents allowed. You sent 600. Please split into multiple requests with max 500 documents each."
}

Monthly Quota Exceeded (CREATE only)

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "Quota exceeded! You have used 950 out of 1000 records this month. You requested 100 more records, but only 50 remaining. Please reduce your request or contact administrator to increase quota."
}

Rate Limit Exceeded

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "data": {
    "key": "your-api-key",
    "limit": 5,
    "window_seconds": 10
  },
  "message": "Rate limit exceeded"
}

No Valid Fields Requested (READ)

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "No valid fields requested"
}

Common Errors — TMS Shop Module

Error Message Cause Fix
"API key and secret are required" Missing api_key or api_secret Include both in the request
"Invalid action. Use 'read', 'create', 'update', or 'delete'" Unsupported action value Use one of the four supported actions
"shop_data is required for create action" (or update/delete) Missing shop_data object Wrap your documents in shop_data.documents
"Maximum 500 documents allowed..." More than 500 documents in one create/update/delete request Split into batches of 500 or fewer
"Document with empty LocationCode found in batch" A document is missing LocationCode (CREATE) Every document needs a non-empty LocationCode
"Duplicate LocationCode 'X' found in the same batch" Same LocationCode sent twice in one request Remove the duplicate, or send it in a separate request
"Shop Location code already exists" LocationCode already exists in SKYBIZ (CREATE) Use a unique LocationCode, or use update instead
LocationCode listed under not_found_documents LocationCode does not exist in SKYBIZ (UPDATE / DELETE) Check the code exists, or use create first
Field name listed under unknown_fields A field outside the Allowed Fields list was sent (UPDATE) Remove the field, or check spelling against the Allowed Fields table
"No updatable fields provided (only PK was sent)" Document contained only LocationCode, nothing to change (UPDATE) Include at least one other field to update
"No valid fields requested" fields array contains no recognized field names (READ) Use field names from the Allowed Fields table
"tms_shop read/create/update/delete permission denied" API key’s module permissions don’t include that action Enable the permission in the portal by contacting your SkyBiz Admin
"Rate limit exceeded" More than 5 requests in a 10-second window Slow down request frequency / add retry backoff
"Quota exceeded!..." Monthly record quota reached (CREATE only) Reduce batch size or contact administrator to raise quota