TMS GDN Module

  1014. TMS GDN
SKYBIZ API — TMS Module

TMS GDN (Goods Delivery Note)

The TMS GDN module allows you to retrieve, create, update, and delete Goods Delivery Note documents in SKYBIZ TMS. Each GDN has a header (document-level details such as origin/destination and dates) and a nested items array (the line items being delivered). Supports read, create, update, and delete operations.

Endpoint: /apiv2/modules/tms_gdn.php

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

Primary Key: Doc1No — unique per GDN document, 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 gdn_data.


Header Fields

These are the only document-level (header) fields recognized by this module. Any other field name sent at the header level in a create or update request is rejected.

Field Type Description
Doc1No string Unique GDN document number. Primary key. Required for every action. Fixed — cannot be changed once created
DocDate string Document date. Recommended format YYYY-MM-DD — also used by the date_from/date_to read filters
DelivMode string Delivery mode/method for this GDN
FromLocCode string Origin location code (e.g. warehouse/shop the goods ship from)
FromLocName string Origin location name
ToLocCode string Destination location code (e.g. warehouse/shop the goods ship to)
ToLocName string Destination location name
DocumentType string Document type classification for this GDN
OriginalDocNo string Reference to an original/source document, if any
AccountingDate string Accounting date associated with this GDN
Remarks string Header-level remarks/notes

Line Fields (items)

Each document carries a nested items array. These are the only fields recognized at the line level. A line’s internal sequence number is assigned automatically and is never sent or returned.

Field Type Description
ItemCode string Item code for this line
ItemName string Item name for this line
Qty float Quantity being delivered on this line. Returned as a float on read
ColorCode string Colour code of the item variant on this line
ColorName string Colour name of the item variant on this line
Size string Size of the item variant on this line
UnitPrice float Unit price for this line. Returned as a float on read
Amount float Line amount (total for this line). Returned as a float on read
Remarks string Line-level remarks/notes

Read TMS GDN

Retrieves GDN documents, each returned with its header fields plus a nested items array. A document with no lines is still returned, with items as an empty array.

Base Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "read",
  "fields": ["Doc1No", "DocDate", "FromLocCode", "ToLocCode"],
  "filters": {
    "ToLocCode": "SHOP001"
  },
  "date_from": "2026-09-01",
  "date_to": "2026-09-30",
  "doc_nos": ["GDN-0001", "GDN-0002"]
}

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 header field names to return. Any name not in the Header Fields list is silently dropped. Omit or leave empty to return all header fields. Does not affect which line fields are returned — line fields are always returned in full No
filters Object of field: value pairs, applied to header fields only. Each is an exact-match (=) condition, combined with AND. Keys outside the Header Fields list are ignored No
date_from Return documents with DocDate >= this date. Format YYYY-MM-DD No
date_to Return documents with DocDate <= this date. Format YYYY-MM-DD No
doc_nos Array of specific Doc1No values to limit the read to. Combined with AND alongside any other filters/date range No

Read Rules

  • Results are ordered by Doc1No, and each document’s lines are ordered by their internal sequence
  • date_from and date_to are both optional — unlike some modules, there is no maximum range enforced between them
  • date_from/date_to, if provided, must match YYYY-MM-DD or the request is rejected
  • No pagination — all matching documents are returned in a single response
  • Filters and fields apply to the header only; every line field is always returned in full for each item
  • If fields is provided but none of the values match a header field, the request is rejected with "No valid fields requested"

Example 1 — Get All GDNs (All Fields, No Filters)

Request

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

Example 2 — Filtered by Date Range and Destination

Request

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "read",
  "date_from": "2026-09-01",
  "date_to": "2026-09-30",
  "filters": {
    "ToLocCode": "SHOP001"
  }
}

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_gdn.php";

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

$DATE_FROM = "2026-09-01";
$DATE_TO   = "2026-09-30";
$FILTERS   = ["ToLocCode" => "SHOP001"];

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

$payload = [
    "api_key"    => $API_KEY,
    "api_secret" => $API_SECRET,
    "action"     => $ACTION,
    "date_from"  => $DATE_FROM,
    "date_to"    => $DATE_TO,
    "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": 1,
    "data": [
      {
        "Doc1No": "GDN-0001",
        "DocDate": "2026-09-10",
        "DelivMode": "Truck",
        "FromLocCode": "WH001",
        "FromLocName": "Shah Alam Central Warehouse",
        "ToLocCode": "SHOP001",
        "ToLocName": "Sunway Pyramid Outlet",
        "DocumentType": "GDN",
        "OriginalDocNo": "",
        "AccountingDate": "2026-09-10",
        "Remarks": "Restock for weekend sale",
        "items": [
          {
            "ItemCode": "STYLE001-RED-M",
            "ItemName": "Classic Tee",
            "Qty": 20,
            "ColorCode": "RD",
            "ColorName": "Red",
            "Size": "M",
            "UnitPrice": 39.90,
            "Amount": 798.00,
            "Remarks": ""
          }
        ]
      }
    ]
  }
}

Create TMS GDN

Creates one or more GDN documents, each with its header and its line items in a single call. All documents are validated before any are saved. If any document fails validation, the entire batch is rejected — nothing is inserted.

Field Rules

Field Level Rule
Doc1No Header Compulsory. Must not be empty. Must not already exist — checked both within the request batch and against existing records
Any other Header Field Header Optional. Blank if not provided
items Header (nested array) Compulsory. Must be present, must be an array, and must contain at least one line — an empty items array is rejected on create
Any Line Field (ItemCode, Qty, etc.) Line Optional per field. Qty, UnitPrice, Amount default to 0 if omitted; the rest default to blank

⚠️ Duplicate Check Order: The batch is first checked for duplicate Doc1No values within the same request, then checked against existing records in SKYBIZ. Either kind of duplicate fails the entire batch. Unrecognized fields — at either the header or the line level — also fail the whole batch, the same way a duplicate does.

Base Create Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "create",
  "gdn_data": {
    "documents": [
      {
        "Doc1No": "GDN-0001",
        "DocDate": "2026-09-10",
        "DelivMode": "Truck",
        "FromLocCode": "WH001",
        "FromLocName": "Shah Alam Central Warehouse",
        "ToLocCode": "SHOP001",
        "ToLocName": "Sunway Pyramid Outlet",
        "DocumentType": "GDN",
        "AccountingDate": "2026-09-10",
        "Remarks": "Restock for weekend sale",
        "items": [
          {
            "ItemCode": "STYLE001-RED-M",
            "ItemName": "Classic Tee",
            "Qty": 20,
            "ColorCode": "RD",
            "ColorName": "Red",
            "Size": "M",
            "UnitPrice": 39.90,
            "Amount": 798.00
          }
        ]
      }
    ]
  }
}

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_gdn.php";

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

$DATA_KEY = "gdn_data";

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

$DOCUMENTS = [];

$DOCUMENTS[] = [
    "Doc1No"         => "GDN-0001",
    "DocDate"        => "2026-09-10",
    "DelivMode"      => "Truck",
    "FromLocCode"    => "WH001",
    "FromLocName"    => "Shah Alam Central Warehouse",
    "ToLocCode"      => "SHOP001",
    "ToLocName"      => "Sunway Pyramid Outlet",
    "DocumentType"   => "GDN",
    "AccountingDate" => "2026-09-10",
    "Remarks"        => "Restock for weekend sale",
    "items"          => [
        [
            "ItemCode"  => "STYLE001-RED-M",
            "ItemName"  => "Classic Tee",
            "Qty"       => 20,
            "ColorCode" => "RD",
            "ColorName" => "Red",
            "Size"      => "M",
            "UnitPrice" => 39.90,
            "Amount"    => 798.00
        ]
    ]
];

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

$totalDocuments = 0;

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

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

    if (empty($document['items'])) {
        echo json_encode([
            "status" => "REJECTED_BY_CLIENT",
            "timestamp" => date("c"),
            "request_id" => uniqid("req_"),
            "message" => "CLIENT-SIDE REJECTION: Document '{$document['Doc1No']}' has no items",
            "action_required" => "Every document needs at least one item on create"
        ], 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": ["GDN-0001"],
    "failed_documents": [],
    "fail_details": {
      "duplicate_in_request": [],
      "duplicate_gdns": [],
      "validation_errors": []
    }
  }
}

Response (Error — Missing Items)

{
  "status": "response",
  "data": {
    "summary": { "total_documents": 1, "inserted": 0, "failed": 1 },
    "successful_documents": [],
    "failed_documents": ["GDN-0001"],
    "fail_details": {
      "duplicate_in_request": [],
      "duplicate_gdns": [],
      "validation_errors": {
        "EMPTY_ITEMS": ["Document 'GDN-0001': 'items' array cannot be empty on create"]
      }
    }
  }
}

Response (Error — Duplicate Doc1No)

{
  "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": ["GDN-0001"],
    "fail_details": {
      "duplicate_in_request": [],
      "duplicate_gdns": [
        {
          "doc1no": "GDN-0001",
          "error": "GDN Doc1No already exists"
        }
      ],
      "validation_errors": []
    }
  }
}

Update TMS GDN

Updates one or more existing GDN documents, matched by Doc1No. Header fields are a partial update — only send the header fields you want to change. All documents are pre-validated before anything is written; if any document in the batch fails, the entire batch is rejected.

🛑 Critical — items is a full replace, not a merge: If you include the items key in an update document at all, every existing line on that GDN is deleted first, then replaced with whatever lines you sent. This applies even if items is an empty array — that will wipe all lines from the document, leaving it with zero items. To update header fields only and leave the existing lines untouched, simply omit the items key entirely from that document.

Field Rules

Field Rule
Doc1No Compulsory in every document — used to look up the record. Fixed — including it only identifies the row, it is never itself updated
Any other Header Field Optional — include only the header fields you want to change
items Optional. When present, replaces all existing lines for that document (see warning above). When omitted, existing lines are left as-is
Any field not in the Header Fields or Line 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 Doc1No
  • No duplicate Doc1No within the same request batch
  • No unrecognized header field names
  • If items is present, it must be an array, and every line in it must use only recognized Line Fields — an unrecognized line field name fails the batch
  • Every document must include at least one header field to update, or an items key — a document with neither is rejected as “nothing to update”
  • Every Doc1No 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 — Header Fields Only (lines untouched)

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "update",
  "gdn_data": {
    "documents": [
      {
        "Doc1No": "GDN-0001",
        "DelivMode": "Van",
        "Remarks": "Rescheduled to next-day delivery"
      }
    ]
  }
}

Base Update Request Structure — Replacing Lines

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "update",
  "gdn_data": {
    "documents": [
      {
        "Doc1No": "GDN-0001",
        "items": [
          {
            "ItemCode": "STYLE001-RED-M",
            "ItemName": "Classic Tee",
            "Qty": 25,
            "ColorCode": "RD",
            "ColorName": "Red",
            "Size": "M",
            "UnitPrice": 39.90,
            "Amount": 997.50
          }
        ]
      }
    ]
  }
}

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_gdn.php";

$DOCUMENTS = [];

// Header-only update — omit 'items' entirely to leave existing lines untouched
$DOCUMENTS[] = [
    "Doc1No"    => "GDN-0001",
    "DelivMode" => "Van",
    "Remarks"   => "Rescheduled to next-day delivery"
];

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,
    "gdn_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": ["GDN-0001"],
    "not_found_documents": [],
    "failed_documents": [],
    "fail_details": {
      "not_found": [],
      "nothing_to_update": [],
      "unknown_fields": [],
      "unknown_item_fields": [],
      "validation_errors": []
    }
  }
}

Response (Error — GDN Not Found)

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

Response (Error — Unknown Line Field)

{
  "status": "response",
  "data": {
    "summary": { "total_documents": 1, "updated": 0, "not_found": 0, "failed": 1 },
    "successful_documents": [],
    "not_found_documents": [],
    "failed_documents": ["GDN-0001"],
    "fail_details": {
      "not_found": [],
      "nothing_to_update": [],
      "unknown_fields": [],
      "unknown_item_fields": {
        "GDN-0001": ["item 1: Discount"]
      },
      "validation_errors": []
    }
  }
}

Delete TMS GDN

Deletes one or more GDN documents, matched by Doc1No — this removes the header and all of its lines. 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 Doc1No
  • No duplicate Doc1No within the same request batch
  • Every Doc1No 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",
  "gdn_data": {
    "documents": [
      { "Doc1No": "GDN-0001" },
      { "Doc1No": "GDN-0002" }
    ]
  }
}

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_gdn.php";

$DOCUMENTS = [
    ["Doc1No" => "GDN-0001"],
    ["Doc1No" => "GDN-0002"]
];

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,
    "gdn_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": ["GDN-0001", "GDN-0002"],
    "not_found_documents": [],
    "failed_documents": [],
    "fail_details": {
      "not_found": [],
      "validation_errors": []
    }
  }
}

Response (Error — GDN Not Found)

{
  "status": "response",
  "data": {
    "summary": { "total_documents": 1, "deleted": 0, "not_found": 1, "failed": 1 },
    "successful_documents": [],
    "not_found_documents": ["GDN-9999"],
    "failed_documents": ["GDN-9999"],
    "fail_details": {
      "not_found": ["GDN-9999"],
      "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, counted as 1 quota unit per header document (line item count does not affect quota usage). 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'"
}

Invalid date_from / date_to Format (READ)

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "Invalid date_from format. Use YYYY-MM-DD"
}

No Permission for Action

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

Missing gdn_data / documents (CREATE / UPDATE / DELETE)

{
  "status": "error",
  "timestamp": "2026-09-25T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "gdn_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 GDN 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
"Invalid date_from format. Use YYYY-MM-DD" / date_to equivalent Date param supplied in the wrong format (READ) Use YYYY-MM-DD, e.g. "2026-09-01"
"gdn_data is required for create action" (or update/delete) Missing gdn_data object Wrap your documents in gdn_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 Doc1No found in batch" / "Missing Doc1No" A document is missing Doc1No Every document needs a non-empty Doc1No
"Duplicate Doc1No 'X' found in the same batch" Same Doc1No sent twice in one request Remove the duplicate, or send it in a separate request
"'items' array is required for create" / "'items' array cannot be empty on create" items missing or empty on a create document Every new GDN needs at least one line item
"GDN Doc1No already exists" Doc1No already exists in SKYBIZ (CREATE) Use a unique Doc1No, or use update instead
Doc1No listed under not_found_documents Doc1No does not exist in SKYBIZ (UPDATE / DELETE) Check the document exists, or use create first
Field name listed under unknown_fields A field outside the Header Fields list was sent (UPDATE) Remove the field, or check spelling against the Header Fields table
Field name listed under unknown_item_fields A field outside the Line Fields list was sent inside items Remove the field, or check spelling against the Line Fields table
"No updatable fields provided" Document had no header fields to change and no items key (UPDATE) Include at least one header field to update, or an items array
Lines unexpectedly gone after an update items was sent as an empty array — this intentionally wipes all lines Omit items entirely if you only meant to change header fields
"No valid fields requested" fields array contains no recognized header field names (READ) Use field names from the Header Fields table
"tms_gdn 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, 1 unit per header) Reduce batch size or contact administrator to raise quota