Sales Module

SKYBIZ API — Sales Module

Sales

The Sales module allows you to retrieve and create sales invoice records in your SKYBIZ account. Supports both read and create operations.

Endpoint: /apiv2/modules/sales.php

Required Permissions: Sales — Read, Sales — Create

Allowed DocTypes: CusInv (Invoice), CS (Cash Sale), CusCN (Credit Note), CusDN (Debit Note)


Read Sales

Retrieves sales invoices created within the given date range. Returns all fields — no field filtering or custom filters are supported for this module.

Base Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "read",
  "date_from": "YYYY-MM-DD",
  "date_to": "YYYY-MM-DD"
}

Request Parameters

Parameter Description Required
api_key Your API key Yes
api_secret Your API secret Yes
action Must be "read" Yes
date_from Start date (YYYY-MM-DD) Yes
date_to End date (YYYY-MM-DD). Max 31 days range. Yes

Date Rules

  • Format must be YYYY-MM-DD — e.g. "2026-04-01"
  • Maximum range is 31 days
  • date_from cannot be after date_to

Available Fields (Read Only)

The Sales module returns all fields. Field filtering is not supported for this module.

Field Type Description
D_ate string Invoice date
Doc1No string Primary document number (invoice number)
Doc2No / Doc3No string Secondary document reference numbers
CusCode string Customer code associated with the invoice
DocType string Document type — CusInv, CS, CusCN, or CusDN
HCNetAmt float Home currency net invoice amount
HCDtTax float Home currency detail tax total
CurRate1 float Currency exchange rate used on the invoice
AdjAmt float Adjustment amount applied to the invoice
ItemCode string Item code for the line item
Description string Line item description
Qty float Quantity sold
FactorQty float Factored quantity (adjusted for UOM conversion)
UOM string Unit of measure used on this line
HCUnitCost float Home currency unit cost on the line
HCLineAmt float Home currency line total amount
HCDiscount float Home currency discount amount on the line
DisRate1 float Discount rate applied to the line
HCTax float Home currency tax amount on the line
TaxRate1 float Tax rate applied to the line
DetailTaxCode string Tax code applied to the line item
BlankLine string Line type — "0" Stock Item, "4" Other Charge, "6" GL Account
SalesPersonCode string Salesperson code assigned to the line
BranchCode string Branch code associated with the line
DepartmentCode string Department code associated with the line
ProjectCode string Project code associated with the line
LocationCode string Stock location from which the item was sold
WarrantyDate string Warranty date for the line item
ItemBatch string Batch number of the item sold
DUD1 to DUD6 string User-defined fields 1 through 6

Example 1 — Get All Sales (All Fields)

This request returns all sales invoice fields for invoices created between April 1, 2026 and April 30, 2026.

Request

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "read",
  "date_from": "2026-04-01",
  "date_to": "2026-04-30"
}

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";

// Date range (required for all endpoints, max 31 days)
$DATE_FROM = "2026-04-01";
$DATE_TO = "2026-04-30";

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

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

// ============================================================
// STEP 3: REQUEST PARAMETERS
// ============================================================

$requestParams = [
    "date_from" => $DATE_FROM,
    "date_to" => $DATE_TO
];

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

$payload = array_merge(
    [
        "api_key" => $API_KEY,
        "api_secret" => $API_SECRET,
        "action" => $ACTION
    ],
    $requestParams
);

// ============================================================
// 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 => http_build_query($payload),
    CURLOPT_TIMEOUT => 30,
]);

$response = curl_exec($ch);

// ============================================================
// STEP 6: HANDLE CURL ERROR
// ============================================================

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;
}

$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// ============================================================
// STEP 7: DECODE RESPONSE
// ============================================================

$result = json_decode($response, true);

if (!$result) {
    echo json_encode([
        "status" => "error",
        "timestamp" => date("c"),
        "request_id" => uniqid("req_"),
        "message" => "Invalid JSON from API",
        "raw_response" => $response
    ], JSON_PRETTY_PRINT);
    exit;
}

// ============================================================
// STEP 8: RETURN CLEAN JSON FORMAT
// ============================================================

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-04-30T04:07:04+00:00",
  "request_id": "req_69f2d56891e57",
  "data": {
    "requested_by": "your-api-key",
    "mode": "2",
    "date_range": {
      "from": "2026-04-01",
      "to": "2026-04-30",
      "days": 30
    },
    "total_returned": 2,
    "data": [
      {
        "Doc1No": "INV-001",
        "CusCode": "CUST001",
        "DocType": "CusInv",
        "HCNetAmt": 1500.00,
        "HCDtTax": 120.00,
        "D_ate": "2026-04-15",
        "DateTimeModified": "2026-04-15 10:30:00"
      },
      {
        "Doc1No": "INV-002",
        "CusCode": "CUST002",
        "DocType": "CusInv",
        "HCNetAmt": 2500.00,
        "HCDtTax": 200.00,
        "D_ate": "2026-04-20",
        "DateTimeModified": "2026-04-20 14:45:00"
      }
    ]
  }
}

Postman Steps — READ Request

  1. Set method to POST

2. Enter URL: https://your-domain/01/clientportal/apiv2/modules/sales.php

3. Set header: Content-Type: application/json

4. Under Body, select raw and format JSON

5. Paste your request payload and click Send


Create Sales Invoice

Creates one or more sales invoice records. All documents are validated before any are saved. If any document fails, the entire batch is rejected.

Compulsory Fields — Header

Field Type Description
Doc1No string Unique document number. Must not already exist.
CusCode string Customer code. Must exist in SKYBIZ.
DocType string Document type — CusInv, CS, CusCN, or CusDN
HCNetAmt float Net amount before tax. Must equal sum of all item HCLineAmt.
HCDtTax float Total tax amount. Must equal sum of all item HCTax.

Compulsory Fields — Items

Field Type Description
ItemCode string Item code. Must exist in SKYBIZ (stock item, other charge, or GL account based on BlankLine).
Description string Item description (max 255 characters)
Qty float Quantity. Must be greater than 0 for stock items and other charges.
FactorQty float UOM conversion factor. Usually same as Qty. Must be greater than 0.
UOM string Unit of measure. Must exist in SKYBIZ UOM table.
HCUnitCost float Unit price. Must be greater than 0 for stock items.
HCLineAmt float Line total. Must equal Qty × HCUnitCost.
BlankLine string Line type — "0" Stock Item, "4" Other Charge, "6" GL Account

Optional Fields — Header

Field Type Description
Doc2No / Doc3No string Reference document numbers
D_ate string Document date in YYYY-MM-DD format. Defaults to today.
CurCode string Currency code. Default: MYR. Must exist in SKYBIZ if provided.
CurRate1 float Exchange rate. Default: 1. Must be greater than 0 if provided.

Optional Fields — Items

Field Type Description
Description2 string Second description line (max 255 characters)
UOMSingular string Singular form of UOM (auto-populated from UOM if empty)
HCDiscount / DisRate1 float Discount amount and discount rate (%)
HCTax float Tax amount for this line. Must equal HCLineAmt × (TaxRate1 / 100) if TaxRate1 > 0.
TaxRate1 float Tax rate in percent — e.g. 8. If provided, HCTax is validated.
DetailTaxCode string Tax code. If provided, must exist in SKYBIZ with TaxType ‘0’ or ‘1’.
SalesPersonCode string Salesperson code. If provided, must exist in SKYBIZ salesman table.
BranchCode string Branch code. If provided, must exist in SKYBIZ branch table.
DepartmentCode string Department code. If provided, must exist in SKYBIZ department table.
ProjectCode string Project code. If provided, must exist in SKYBIZ project table.
LocationCode string Stock location code. If provided, must exist in SKYBIZ location table.
WarrantyDate string Warranty date in YYYY-MM-DD format
DUD1 to DUD6 string User-defined detail fields 1 through 6 (max 40 characters each)
LineNo integer Line sequence number. Auto-assigned if omitted.

BlankLine Values

Value Type Description
"0" Stock Item Item code must exist.
"4" Other Charge Item code must exist.
"6" GL Account Item code must exist (GL account table).

⚠️ Amount Validation Rules: SKYBIZ validates that HCLineAmt = Qty × HCUnitCost and that the sum of all item HCLineAmt values matches the document header HCNetAmt. Mismatches will cause the entire document to fail.

Base Create Request Structure

{
  "api_key": "your-api-key",
  "api_secret": "your-api-secret",
  "action": "create",
  "sales_data": {
    "documents": [
      {
        "Doc1No": "INV-001",
        "CusCode": "CUST001",
        "DocType": "CusInv",
        "D_ate": "2026-05-20",
        "HCNetAmt": 550.00,
        "HCDtTax": 44.00,
        "CurCode": "RM",
        "CurRate1": 1,
        "items": [
          {
            "ItemCode": "ITEM001",
            "Description": "Product A",
            "Qty": 10,
            "FactorQty": 1,
            "UOM": "Piece",
            "HCUnitCost": 50.00,
            "HCLineAmt": 500.00,
            "BlankLine": "0",
            "HCTax": 40.00,
            "TaxRate1": 8,
            "LineNo": 1
          }
        ]
      }
    ]
  }
}

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

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

$DATA_KEY = "sales_data";

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

$DOCUMENTS = [];

// --- Sales example ---
$DOCUMENTS[] = [
    "Doc1No" => "INV-001",
    "CusCode" => "CUST001",
    "DocType" => "CusInv",
    "D_ate" => "2026-05-20",
    "HCNetAmt" => 550.00,
    "HCDtTax" => 44.00,
    "CurCode" => "RM",
    "CurRate1" => 1,
    "items" => [
        [
            "ItemCode" => "ITEM001",
            "Description" => "Product A",
            "Qty" => 10,
            "FactorQty" => 1,
            "UOM" => "Piece",
            "HCUnitCost" => 50.00,
            "HCLineAmt" => 500.00,
            "BlankLine" => "0",
            "HCTax" => 40.00,
            "TaxRate1" => 8,
            "LineNo" => 1
        ]
    ]
];

// ============================================================
// 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 ($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 "=== CLIENT-SIDE VALIDATION ===\n";
echo "Looped through all {$totalDocuments} documents one by one\n";
echo "All documents passed client validation\n";
echo "Document count: {$totalDocuments} (max 500)\n";
echo "Sending 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);

// ============================================================
// STEP 7: HANDLE CURL ERROR
// ============================================================

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;
}

$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

// ============================================================
// STEP 8: DECODE RESPONSE
// ============================================================

$result = json_decode($response, true);

if (!$result) {
    echo json_encode([
        "status" => "error",
        "timestamp" => date("c"),
        "request_id" => uniqid("req_"),
        "message" => "Invalid JSON from API",
        "raw_response" => $response
    ], JSON_PRETTY_PRINT);
    exit;
}

// ============================================================
// STEP 9: RETURN CLEAN JSON FORMAT
// ============================================================

echo "=== SERVER RESPONSE ===\n";
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-05-20T10: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": ["INV-001"],
    "failed_documents": [],
    "fail_details": {
      "compulsory_fields_missing": [],
      "duplicate_documents": [],
      "invalid_customers": [],
      "invalid_items": [],
      "invalid_tax_codes": [],
      "invalid_sales_persons": [],
      "invalid_locations": [],
      "invalid_departments": [],
      "invalid_projects": [],
      "invalid_branches": [],
      "invalid_currencies": [],
      "invalid_uoms": [],
      "amount_mismatch": [],
      "validation_errors": []
    }
  }
}

Response (Error — Duplicate Document)

{
  "status": "response",
  "timestamp": "2026-05-20T10: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": ["INV-001"],
    "fail_details": {
      "duplicate_documents": ["INV-001"],
      "invalid_currencies": [],
      "invalid_uoms": [],
      "amount_mismatch": [],
      "validation_errors": {}
    }
  }
}

Error Responses

Missing Date Range (READ)

{
  "status": "error",
  "timestamp": "2026-04-07T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "date_from and date_to are required"
}

Invalid Date Format

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

Date Range Exceeds 31 Days

{
  "status": "error",
  "timestamp": "2026-04-07T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "Date range cannot exceed 31 days."
}

Invalid DocType (CREATE)

{
  "status": "error",
  "timestamp": "2026-04-07T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "Invalid DocType 'XXX'. Allowed: CusInv, CS, CusCN, CusDN"
}

Missing Compulsory Fields (CREATE)

{
  "status": "response",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "data": {
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "failed_documents": ["INV-001"],
    "fail_details": {
      "compulsory_fields_missing": [
        "Document 1 (Doc1No: INV-001): Missing header fields - CusCode, HCNetAmt, HCDtTax, DocType"
      ]
    }
  }
}

Invalid Customer Code (CREATE)

{
  "status": "response",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "data": {
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "failed_documents": ["INV-001"],
    "fail_details": {
      "invalid_customers": [
        {
          "customer_code": "INVALID",
          "document_no": "INV-001",
          "error": "Customer Code 'INVALID' does not exist"
        }
      ]
    }
  }
}

Invalid Item Code (CREATE)

{
  "status": "response",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "data": {
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "failed_documents": ["INV-001"],
    "fail_details": {
      "invalid_items": [
        {
          "item_code": "INVALID",
          "document_no": "INV-001",
          "line_no": 1,
          "blank_line_type": "0 (Stock Item)"
        }
      ]
    }
  }
}

Invalid UOM Code (CREATE)

{
  "status": "response",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "data": {
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "failed_documents": ["INV-001"],
    "fail_details": {
      "invalid_uoms": [
        {
          "uom_code": "INVALID",
          "document_no": "INV-001",
          "line_no": 1,
          "error": "UOM code 'INVALID' does not exist"
        }
      ]
    }
  }
}

Invalid Currency Code (CREATE)

{
  "status": "response",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "data": {
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "failed_documents": ["INV-001"],
    "fail_details": {
      "invalid_currencies": [
        "Document 1 (Doc1No: INV-001): Currency code 'INVALID' does not exist"
      ]
    }
  }
}

Amount Mismatch — Line Level (CREATE)

{
  "status": "response",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "data": {
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "failed_documents": ["INV-001"],
    "fail_details": {
      "amount_mismatch": {
        "INV-001": [
          {
            "line_no": 1,
            "item_code": "ITEM001",
            "provided_hclineamt": "100.00",
            "expected_hclineamt": "500.00",
            "error": "HCLineAmt does not match Qty × HCUnitCost"
          }
        ]
      }
    }
  }
}

Amount Mismatch — Header Level (CREATE)

{
  "status": "response",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "data": {
    "summary": {
      "total_documents": 1,
      "inserted": 0,
      "failed": 1
    },
    "failed_documents": ["INV-001"],
    "fail_details": {
      "amount_mismatch": {
        "INV-001": [
          "Header HCNetAmt (100.00) does not match sum of HCLineAmt (500.00)"
        ]
      }
    }
  }
}

No Read Permission

{
  "status": "error",
  "timestamp": "2026-04-07T10:46:15+08:00",
  "request_id": "req_6612f3b9c21a7",
  "message": "Sales read permission denied"
}

No Create Permission

{
  "status": "error",
  "timestamp": "2026-05-20T10:30:00+08:00",
  "request_id": "req_123456",
  "message": "Sales create permission denied"
}

Common Errors — Sales Module

Error Message Cause Fix
"date_from and date_to are required" Missing date range (READ only) Add both fields to your request
"Invalid date format. Use YYYY-MM-DD format." Wrong date format Use YYYY-MM-DD — e.g. "2026-04-01"
"Date range cannot exceed 31 days." Date range too wide Narrow range to 31 days or fewer
"Invalid DocType 'XXX'. Allowed: CusInv, CS, CusCN, CusDN" Wrong DocType in CREATE request Use one of the allowed DocTypes
"Missing fields - ItemCode, Description, Qty, FactorQty, UOM, HCUnitCost, HCLineAmt, BlankLine" Compulsory item fields not provided (CREATE) Include all required fields for each item
"UOM code 'X' does not exist" Invalid Unit of Measure (CREATE) Check the UOM exists in SKYBIZ (singular or plural form)
"Currency code 'X' does not exist" Invalid currency code (CREATE) Use a valid currency code (e.g., RM, SGD, USD)
"Customer Code 'X' does not exist" Invalid customer code Check the customer code exists in SKYBIZ
"ItemCode 'X' does not exist" Invalid item code Check the item code exists in SKYBIZ (stock item, other charge, or GL account based on BlankLine)
"HCLineAmt does not match Qty × HCUnitCost" Amount calculation mismatch (CREATE) Ensure HCLineAmt = Qty × HCUnitCost (tolerance: ±0.01)
"Header HCNetAmt does not match sum of HCLineAmt" Header total doesn’t match item totals (CREATE) Set HCNetAmt to the sum of all item HCLineAmt (tolerance: ±0.01)
"HCTax does not match HCLineAmt × TaxRate1%" Tax calculation mismatch (CREATE) Ensure HCTax = HCLineAmt × (TaxRate1 / 100) (tolerance: ±0.01) when TaxRate1 > 0
"Document already exists" Duplicate Doc1No (CREATE) Use a unique Doc1No for each document
"Qty must be greater than 0 for Stock Item" Quantity is zero or negative for stock item (CREATE) Set Qty to a positive number greater than 0
"HCUnitCost must be greater than 0 for stock items" Unit cost is zero or negative for stock item (CREATE) Set HCUnitCost to a positive number greater than 0
"FactorQty must be greater than 0" Factor quantity is zero or negative (CREATE) Set FactorQty to a positive number (default is 1)
"Sales read permission denied" No read permission for Sales module Enable read permission in portal by contacting your SkyBiz Admin