Gateway API Reference v1.0.1

REST API for products, packages, and delivery. Authenticate with your merchant credentials and integrate in minutes.

← Back to Developers

VHMExpress Gateway API v1.0.1

Use the Gateway API to register products, create packages, list and track deliveries, and calculate delivery fees. All endpoints require merchant authentication via HTTP headers.

API Version: 1.0.1
Base URL: https://<your-domain>/api/v1/gateway
Content-Type: application/json for POST request/response bodies.
All gateway endpoints require merchant authentication. Requests and responses are logged for support and auditing. Base URL, API keys, and merchant IDs are provided upon request.

1. Authentication

All gateway endpoints require merchant authentication via HTTP headers.

Required headers

HeaderRequiredDescription
HVMX-MERCHANT-IDYesYour merchant identifier.
HVMX-API-KEYYesYour API key.
Content-TypeYes (for POST)application/json for request bodies.

401 Unauthorized

When authentication fails, the response body includes a machine-readable error code:

{
  "status": 0,
  "error": "<code>",
  "message": "Unauthorized"
}
errorMeaning
missing_merchant_idHVMX-MERCHANT-ID header is missing or empty.
missing_api_keyHVMX-API-KEY header is missing or empty.
invalid_credentialsMerchant ID and/or API key are invalid.
environment_not_allowedSandbox environment is not allowed (e.g. on production gateway).
environment_mismatchHVMX-ENVIRONMENT does not match the key's environment.

2. Common response format

  • Success: status: 1, optional message, and usually a data object or array.
  • Error: status: 0 and a message (and sometimes error) describing the issue.
  • 404 / not found: Same JSON shape with status: 0 and an appropriate message.

3. Data API

Endpoints for districts and cities (e.g. address dropdowns, filters). Base path: /api/v1/gateway/data.

Get districts list

GET /api/v1/gateway/data/get-districts

Returns all active districts (for dropdowns, filters, etc.).

Request

Method: GET. Headers: Authentication headers only. No body.

Success response (200)

{
  "status": 1,
  "message": "OK",
  "data": [
    { "id": 1, "name": "Colombo" },
    { "id": 2, "name": "Gampaha" }
  ]
}

data: array of { id: number, name: string }, sorted by name.

Get cities by district

GET /api/v1/gateway/data/get-cities/:district_id

Returns active cities for a given district (e.g. for address forms).

Request

Method: GET. URL parameter: district_id (integer) — district ID from the districts list. Headers: Authentication only. No body.

Example: GET /api/v1/gateway/data/get-cities/1

Success response (200)

{
  "status": 1,
  "message": "OK",
  "data": [
    { "id": 10, "name": "Colombo 1", "district_id": 1 },
    { "id": 11, "name": "Colombo 2", "district_id": 1 }
  ]
}

data: array of { id: number, name: string, district_id: number }, sorted by name.

Error: Missing or invalid district_id: status: 0, message: "district_id is required" (or similar).

Get city by district and name

POST /api/v1/gateway/data/get-city-by-district-and-name

Returns a single city when it exists in the Cities table for the given district and city name. Matching is case-insensitive (e.g. colombo, COLOMBO, and Colombo all match the same city). Only active cities (flag = 1) are considered. Use this endpoint to resolve a city id from a district and city name (e.g. for delivery address or package creation).

Request

Method: POST. Headers: Authentication headers and Content-Type: application/json. Body: JSON object with the parameters below.

Body parameters:

ParameterTypeRequiredDescription
district_idnumber/stringYesDistrict ID (e.g. from Get Districts or Get Cities by District).
city_namestringYesCity name. Matched case-insensitively.

Example request body:

POST /api/v1/gateway/data/get-city-by-district-and-name
Content-Type: application/json

{
  "district_id": 5,
  "city_name": "Colombo"
}

Success response (200)

data: object with the matched city.

FieldTypeDescription
idnumberCity ID.
namestringCity name (as stored in DB).
district_idnumberDistrict ID.
{
  "status": 1,
  "message": "OK",
  "data": {
    "id": 42,
    "name": "Colombo",
    "district_id": 5
  }
}

Validation error (200, status 0)

When district_id or city_name is missing or empty:

{
  "status": 0,
  "message": "district_id is required"
}

Or message: "city_name is required".

Not found (404)

When no active city exists for the given district and name (case-insensitive):

{
  "status": 0,
  "message": "City not found for the given district and name"
}

Unauthorized (401)

When authentication fails (missing or invalid headers). See Authentication for error codes and response format.

Note: For a list of all cities in a district, use GET /api/v1/gateway/data/get-cities/:district_id instead.

4. Products API

Base path: /api/v1/gateway/products

4.1 Create product

POST /api/v1/gateway/products/create

Creates a new product for the authenticated vendor. Product is created in pending state until approved by admin.

Request

Headers: HVMX-MERCHANT-ID, HVMX-API-KEY, Content-Type: application/json

Body:

FieldTypeRequiredDescription
namestringYesProduct name. Must be unique per vendor (among pending/approved).
weightnumberYesWeight in kg. Can be 0.
pricenumberYesPrice (e.g. LKR). ≥ 0.
descriptionstringNoProduct description.
product_type_idnumberYesID from product types. Must exist and be active.
dimension_widthnumberNoWidth. Can be 0.
dimension_heightnumberNoHeight. Can be 0.
dimension_lengthnumberNoLength. Can be 0.

Example request body:

{
  "name": "Sample Product",
  "weight": 1.5,
  "price": 500,
  "description": "",
  "product_type_id": 1,
  "dimension_width": 0,
  "dimension_height": 0,
  "dimension_length": 0
}

Response

Success (201 Created):

FieldTypeDescription
statusnumber1
messagestringe.g. "Product created successfully (pending approval)"
dataobjectCreated product (see below)

data object: id, name, weight, price, description, product_type_id, dimension_width, dimension_height, dimension_length, flag (2 = pending), created_at (milliseconds since epoch, as string).

{
  "status": 1,
  "message": "Product created successfully (pending approval)",
  "data": {
    "id": 123,
    "name": "Sample Product",
    "weight": 1.5,
    "price": 500,
    "description": "",
    "product_type_id": 1,
    "dimension_width": 0,
    "dimension_height": 0,
    "dimension_length": 0,
    "flag": 2,
    "created_at": "1234567890123"
  }
}

Errors: Validation (wrong types, missing required): status: 0, message describes the issue. Duplicate name (pending/approved): status: 0, message indicates duplicate product name. Invalid product_type_id: status: 0, message indicates invalid or inactive type.

4.2 Get product types

GET /api/v1/gateway/products/types

Returns the list of active product types (for dropdowns and when creating products).

Headers: HVMX-MERCHANT-ID, HVMX-API-KEY. Body: None.

Success (200): status: 1, data = array of { "id": number, "name": string }.

{
  "status": 1,
  "message": "OK",
  "data": [
    { "id": 1, "name": "Electronics" },
    { "id": 2, "name": "Clothing" }
  ]
}

5. Packages API

Base path: /api/v1/gateway/packages

Packages are delivery consignments created via the gateway. Status in responses is normalized to: pending | completed | cancelled | returned | moving.

5.1 Create package

POST /api/v1/gateway/packages/create

Creates a delivery package for the authenticated vendor. Requires a valid receiver city from the city list; delivery fee is calculated by weight (slab-based). If the city is invalid, creation is rejected with "invalid city".

Request

Headers: Authentication + Content-Type: application/json

Body (JSON):

FieldTypeRequiredDescription
receiver_city_idnumberYesCity ID from the data API (get-cities). Must be active.
receiver_namestringYesReceiver full name.
receiver_phonestringYesReceiver phone number.
receiver_addressstringYesDelivery address line (street, building, etc.).
weightnumberYesPackage weight in kg. Must be > 0.
pricenumberYesPackage value/price. ≥ 0.
collection_typestringYesOne of: delivery_fee_only, delivery_fee_with_package_price, delivery_only.
custom_package_idstringYesYour unique reference for this package (unique per vendor).
productsarrayNoOptional list of { product_id: number, quantity: number }. Only approved products.
package_price_includes_delivery_feebooleanNoWhen true, the vendor indicates that the given price already includes delivery (or delivery should not be charged). The amount to collect from receiving person (total_price) is adjusted per collection_type (see behaviour table below). Accepted as true/false, 1/0, or "true"/"false". Omit or false for default behaviour.

Validation: If package_price_includes_delivery_fee is present, the value must be a boolean or one of: 0, 1, "true", "false". Any other type returns: status: 0, message: "package_price_includes_delivery_fee must be a boolean (true/false)".

Behaviour: total_price and collection

The amount to collect from receiving person (total_price) is derived from price, calculated delivery_fee, collection_type, and package_price_includes_delivery_fee:

collection_typepackage_price_includes_delivery_feetotal_price (amount to collect from receiving person)
delivery_fee_onlyfalse or omittedDelivery fee only
delivery_fee_onlytrue0 (nothing to collect)
delivery_fee_with_package_pricefalse or omittedprice + delivery fee
delivery_fee_with_package_pricetrueprice (delivery already included; no extra charge)
delivery_onlyany0 (ignored; always 0)

total_price is always ≥ 0. For delivery_only, package_price_includes_delivery_fee has no effect.

collection_type

Who pays what at delivery:

ValueMeaning
delivery_fee_onlyCustomer pays delivery fee only; package price is already paid.
delivery_fee_with_package_priceCustomer pays delivery fee + package price.
delivery_onlyNo charge at delivery (both fee and package price already paid).

Example request body:

{
  "receiver_city_id": 10,
  "receiver_name": "John Doe",
  "receiver_phone": "0771234567",
  "receiver_address": "123 Main St",
  "weight": 2.5,
  "price": 1000,
  "collection_type": "delivery_fee_with_package_price",
  "custom_package_id": "ORDER-001",
  "products": [
    { "product_id": 1, "quantity": 1 }
  ]
}

Example: price already includes delivery (no extra delivery charge) — send package_price_includes_delivery_fee: true; then total_price will equal price for delivery_fee_with_package_price, or 0 for delivery_fee_only.

{
  "weight": 2,
  "price": 2000,
  "receiver_city_id": 1,
  "receiver_name": "Jane Doe",
  "receiver_phone": "0779876543",
  "receiver_address": "456 Park Ave, Colombo",
  "collection_type": "delivery_fee_with_package_price",
  "custom_package_id": "ORD-2025-002",
  "package_price_includes_delivery_fee": true
}

Success response (201)

data follows the same package object shape as List and Details (see Package object). package_status is one of: pending, completed, cancelled, returned, moving.

Errors: receiver_city_id missing/invalid or city not in list: status: 0, message: "invalid city". custom_package_id already used for this vendor: status: 0, message indicates duplicate. No delivery route found for address: status: 0, message about serviced area. Invalid package_price_includes_delivery_fee (non-boolean): status: 0, message: "package_price_includes_delivery_fee must be a boolean (true/false)". Validation (e.g. invalid collection_type, product not found): status: 0, message describes the issue.

5.2 List packages

GET /api/v1/gateway/packages/list/:page

Returns a paginated list of packages created via the gateway for the authenticated vendor. Each page has a fixed size (e.g. 100).

Request

Headers: HVMX-MERCHANT-ID, HVMX-API-KEY

URL parameters:

ParameterTypeRequiredDescription
pagenumberYesPage number (1-based).

Example: GET /api/v1/gateway/packages/list/1

Success (200): data.packages = array of Package objects; data.pagination = { page, per_page, total, total_pages }.

{
  "status": 1,
  "message": "OK",
  "data": {
    "packages": [
      {
        "id": 456,
        "package_id": "D1C2ABC123XYZ",
        "custom_package_id": "ORDER-001",
        "package_status": "pending",
        "weight": 2.5,
        "price": 1000,
        "delivery_fee": 250,
        "total_price": 1250,
        "delivery_city_id": 10,
        "collection_type": "delivery_fee_with_package_price",
        "receiver_name": "John Doe",
        "receiver_phone": "0771234567",
        "receiver_address": "123 Main St",
        "delivery_date": "",
        "created_at": "2025-03-05T10:00:00.000Z",
        "products": [
          { "product_id": 1, "product_name": "Widget", "total_price": 500 }
        ]
      }
    ],
    "pagination": {
      "page": 1,
      "per_page": 100,
      "total": 50,
      "total_pages": 1
    }
  }
}

5.3 Get package details

POST /api/v1/gateway/packages/details

Returns a single package by package_id, only if it belongs to the authenticated vendor.

Request

Headers: HVMX-MERCHANT-ID, HVMX-API-KEY, Content-Type: application/json

Body:

FieldTypeRequiredDescription
package_idstringYesThe package_id (e.g. D1C2ABC123XYZ).

Example: { "package_id": "D1C2ABC123XYZ" }

Success (200): status: 1, message, data = Package object.

Error: Package not found or not owned by vendor: 404, status: 0, message: "Package not found".

5.4 Calculate delivery fee

POST /api/v1/gateway/packages/delivery-fee

Returns the delivery fee for a given city (by ID) and weight. Uses the same slab-based calculation as create package. If the city is invalid, returns "invalid city" instead of a fee.

Request

Headers: Authentication + Content-Type: application/json

Body (JSON):

FieldTypeRequiredDescription
city_idnumberYesCity ID from the data API (get-cities). Must be active.
weightnumberYesWeight in kg. Must be > 0.

Example: { "city_id": 10, "weight": 2.5 }

Success response (200)

{
  "status": 1,
  "message": "City recognized successfully.",
  "data": {
    "delivery_fee": 250,
    "recognized_city": "Colombo 1",
    "city_status": "City recognized successfully.",
    "weight": 2.5
  }
}

Errors: Missing/invalid city_id or city not in list: status: 0, message: "invalid city". Missing or invalid weight: status: 0, message describes the issue (e.g. weight required and must be > 0).

Note: The fee is calculated by weight only (slab-based: first kg, 2nd–5th kg, 6+ kg). City is used only for validation; the same weight in any valid city yields the same fee.

Package object

Used in Create Package response, List Packages (data.packages[]), and Get Package Details (data).

FieldTypeDescription
idnumberInternal package ID.
package_idstringUnique package identifier (e.g. D1C2…).
custom_package_idstringYour reference (unique per vendor).
package_statusstringpending | completed | cancelled | returned | moving.
weightnumberWeight (kg).
pricenumberPackage value.
delivery_feenumberDelivery fee (LKR).
total_pricenumberTotal (depends on collection_type).
delivery_city_idnumber | nullDelivery city ID from city list.
collection_typestringdelivery_fee_only | delivery_fee_with_package_price | delivery_only.
receiver_namestringRecipient name.
receiver_phonestringRecipient phone.
receiver_addressstringDelivery address line.
delivery_datestringFilled when delivered/scheduled.
created_atstringUTC ISO 8601.
productsarray{ product_id, product_name, total_price }[].

products array element:

FieldTypeDescription
product_idnumberProduct ID.
product_namestringProduct name.
total_pricenumberLine total (unit price × quantity).

Delivery fee calculation (reference)

Delivery fee is weight-based only (no percentage, no city-based pricing):

  • Weight is rounded up to the next whole kg (e.g. 2.3 kg → 3 kg).
  • Three slab types from system pricing: First kg (0.01–1 kg): single price; Second slab (2–5 kg): per-kg price for 2nd–5th kg; Rest (6+ kg): per-kg price for 6th kg onwards.
  • Formula: Rounded weight ≤ 1 kg → fee = first-kg price. 2–5 kg → fee = first-kg + (rounded − 1) × second-slab price. > 5 kg → fee = first-kg + 4 × second-slab + (rounded − 5) × rest-slab price.

This logic is used for both create package and delivery-fee API.

Endpoint summary

MethodPathDescription
GET/api/v1/gateway/data/get-districtsGet districts list.
GET/api/v1/gateway/data/get-cities/:district_idGet cities by district.
POST/api/v1/gateway/data/get-city-by-district-and-nameGet city by district and name (case-insensitive).
POST/api/v1/gateway/products/createCreate product (pending approval).
GET/api/v1/gateway/products/typesGet product types.
POST/api/v1/gateway/packages/createCreate package.
GET/api/v1/gateway/packages/list/:pageList packages (paginated).
POST/api/v1/gateway/packages/detailsGet package details by package_id.
POST/api/v1/gateway/packages/delivery-feeCalculate delivery fee by city_id and weight.

All requests require headers HVMX-MERCHANT-ID and HVMX-API-KEY. POST bodies must be application/json. Error responses use status: 0 and include message.

Back to Developers