Skip to content

Catalog API ​

Returns the complete product catalog with field configurations and fulfillment mapping. Designed for device caching — fetch once, render dynamically.

Migrating from /v2/product-list?

See the Migration Guide for endpoint mapping and a checklist.

Endpoint ​

http
GET https://api.iimmpact.com/v2/catalog

Headers ​

HeaderDescriptionRequired
X-Api-KeyYour API keyYes
X-TimestampUnix timestamp in secondsYes
X-NonceUnique request identifierYes
X-SignatureHMAC-SHA256 signature (v1=...)Yes
X-API-VersionContract version date, e.g. 2026-09-16 (see Versioning)No

See API Key Authentication for signing details.

Query Parameters ​

ParameterTypeRequiredDescription
product_codestringNoReturn only one product
is_activebooleanNoFilter by your account's effective active status (default: true). false returns only products you have disabled
include_inactivebooleanNoWhen true, ignores is_active and also returns products you have disabled, with is_active: false (default: false)

Products that are globally inactive, or that your account is restricted from purchasing, are never listed, regardless of these parameters. Hidden products are not listed either, except Fund Disbursement (FD), which is listed for accounts with Fund Disbursement access.

Example Request ​

bash
curl -X GET "https://api.iimmpact.com/v2/catalog" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "X-Timestamp: 1704067200" \
  -H "X-Nonce: req-1704067200-a1b2c3d4e5f6" \
  -H "X-API-Version: 2026-09-16" \
  -H "X-Signature: v1=BASE64_HMAC_SIGNATURE"

Response ​

json
{
  "last_updated": "2025-01-07T00:00:00Z",
  "tree": { "groups": [...] },
  "products": { "D": {...}, "M": {...} }
}
FieldTypeDescription
last_updatedstringISO 8601 timestamp of last catalog update
treeobjectHierarchical structure for UI navigation
productsobjectFlat map of products keyed by product_code

Cache Strategy

last_updated tracks changes to groups, categories, products and your customisations. It does not move when options or wholesale pricing change, so also refresh periodically, together with the relevant /v2/options pages (and on catalog webhooks). This endpoint does not guarantee ETag or Last-Modified headers.

Tree ​

Navigation hierarchy: group → category → product codes.

json
{
  "groups": [
    {
      "id": "grp_mobile",
      "name": "Mobile",
      "icon_url": "https://cdn.iimmpact.com/icons/mobile.png",
      "categories": [
        {
          "id": "cat_prepaid",
          "name": "Prepaid Reload",
          "product_codes": ["D", "M", "C", "U"]
        }
      ]
    }
  ]
}

The tree controls UI ordering; products is a flat map for O(1) lookup.

Product ​

Each entry in the products map:

json
{
  "D": {
    "code": "D",
    "name": "Digi Prepaid",
    "note": "Prepaid reload for Digi mobile numbers",
    "image_url": "https://dashboard.iimmpact.com/img/D.png",
    "processing_time": "instant",
    "is_active": true,
    "denomination": "5,10,30,50,100",
    "denomination_currency": "MYR",
    "denomination_unit_price": 1.0,
    "fields": [...],
    "fulfillment": {...},
    "pricing": {...}
  }
}
FieldTypeDescription
codestringUnique product identifier
namestringDisplay name
notestring | nullOptional description
image_urlstringProduct logo URL
processing_timestringinstant, 24_hours, 3_days, link, or pin
is_activebooleanEffective status after reseller overlay
denominationstring | nullRaw denomination string from backend
denomination_currencystring | nullCurrency or unit code for the denomination (for example MYR, BDT, Diamonds)
denomination_unit_pricenumberMYR price per denomination unit. Standard MYR products return 1, while foreign-currency products can return values like 0.059 for BDT
fieldsarrayForm field definitions
fulfillmentobjectMapping to payment request
pricingobjectWholesale cost and adjustments
min_amountobject | nullCustom minimum amount override for pricing fields
max_amountobject | nullCustom maximum amount override for pricing fields

Display processing_time to users before checkout: instant = seconds, 24_hours = 1 business day, 3_days = 3 business days, link = fulfilled via a redemption link, pin = fulfilled as a PIN/voucher code.

For international topup and other non-MYR denomination products, multiply the displayed denomination by denomination_unit_price to derive the MYR face value before any reseller price adjustment.

Fields ​

Each field in the fields array defines a form input:

json
{
  "id": "phone",
  "type": "text",
  "input_mode": "tel",
  "label": "Phone Number",
  "placeholder": "e.g. 0123456789",
  "required": true,
  "order": 1,
  "role": "account",
  "validation": {
    "pattern": "^01[0-9]{8,9}$",
    "message": "Enter valid Malaysian phone number"
  }
}
PropertyTypeDescription
idstringUnique identifier within product
typestringtext, number, select, or money
input_modestringKeyboard hint: text, tel, numeric, email, decimal
labelstringDisplay label
placeholderstringInput placeholder text
requiredbooleanWhether field is required
ordernumberDisplay order (ascending)
rolestringaccount, pricing, or none
validationobjectpattern (regex), message (error text), min/max (for money/number)
data_sourceobjectSelect field options source
currencystringFor money fields only (default: MYR)

A product may omit an account field from its catalog fields, for example for PIN products. The account field remains required when calling POST /v2/topup.

Field Types ​

TypeUse CaseExample Fields
textPhone, NRIC, account numbersinput_mode: "tel" for phones, "numeric" for NRICs
numberPlayer IDs, quantitiesNumeric keyboard
selectPlans, packages, billersOptions from /v2/options
moneyPayment amountsvalidation.min/max for range, input_mode: "decimal"

Select Fields ​

Select fields declare how to fetch their options via data_source:

json
{
  "data_source": {
    "type": "dynamic",
    "depends_on": ["phone"],
    "endpoint": "/options",
    "params": {
      "product_code": { "static": "HI" },
      "field_id": { "static": "plan" },
      "account_number": { "from_field": "phone" }
    }
  }
}
PropertyDescription
typereference = static list, cache locally. dynamic = user-specific, fetch per request
depends_onField IDs that must be filled before fetching (dynamic only)
endpointAlways /options — call via Options API
paramsQuery params: { "static": "value" } for constants, { "from_field": "id" } for user input

See Product Examples for complete select field flows.

Fulfillment ​

Maps form fields to the POST /v2/topup payment request. The product field is always the product's code. You provide your own refid and optional remarks.

json
{
  "fulfillment": {
    "account": { "from_field": "phone" },
    "amount": { "from_field": "plan", "path": "denomination" },
    "extras": {
      "subproduct_code": { "from_field": "plan", "path": "code" },
      "ref2": { "from_field": "ref2", "omit_if_empty": true }
    }
  }
}
PropertyTypeDescription
from_fieldstringField ID to get value from
pathstringProperty or dot-path into the selected option object (for pricing selects, denomination)
omit_if_emptybooleanExclude from request if value is empty

Store the Full Option Object

For select fields, store the entire selected item from /v2/options — not just the code. Fulfillment reads denomination for the transaction amount and may read code for extras.subproduct_code.

For a pricing select field, amount is the option's face value or quantity from denomination, not the adjusted customer-facing price.amount. When a product supports extras, its fulfillment includes extras.subproduct_code mapped from the selected option's stable code. Products that do not support extras do not gain that mapping. Direct money fields continue to map their entered value without a path.

Returned when you send X-API-Version: 2026-09-16; default from 2027-01-11. See Versioning.

The option code identifies the selected plan or package, while denomination is the face value or quantity sent for fulfillment. price.amount is the adjusted customer-facing selling price, and cost.amount is the reseller's wholesale cost.

Versioning ​

X-API-Version takes a date (YYYY-MM-DD). Dates later than tomorrow (UTC) and malformed values are ignored, and the current default contract is used.

ReleaseChangeDefault for all requests
2026-09-16Options expose denomination; catalog amount fulfillment maps denomination and extras.subproduct_code2027-01-11

Until the default date, send X-API-Version: 2026-09-16 on GET /v2/catalog and GET /v2/options to receive this contract.

Pricing ​

B2B Data

Pricing is for your backend only. Do not expose unit_price, discount, or price_adjustment to end users.

json
{
  "pricing": {
    "unit_price": "0.9710",
    "currency": "MYR",
    "discount": {
      "type": "percentage",
      "value": "2.9%"
    },
    "price_adjustment": null,
    "has_loss_risk": false
  }
}
FieldTypeDescription
unit_pricestringComputed unit price as a decimal string (e.g. "0.9710")
currencystringCurrency or unit code. Fiat currencies (e.g. MYR, IDR, BDT) for standard products, or game/virtual currencies (e.g. UC, Diamonds) for game top-up products
discountobjectWholesale discount applied to this product
price_adjustmentobject | nullOptional reseller price adjustment
has_loss_riskbooleantrue when adjustment can produce a loss at some denomination

Discount ​

The discount object describes the wholesale discount for the product:

FieldTypeDescription
typestringpercentage or fixed
valuestringHuman-readable discount value (e.g. "2.9%", "0%", "30 cent")

Percentage discount — unit_price is the cost multiplier per RM 1 face value:

ExampleMeaningCost for RM 100
unit_price: "0.9710", value: "2.9%"2.9% discount off face value100 × 0.9710 = RM 97.10
unit_price: "1.0000", value: "0%"No discount100 × 1.0000 = RM 100.00

Fixed discount — unit_price is a negative fixed amount deducted per transaction:

ExampleMeaningCost for RM 100
unit_price: "-0.3000", value: "30 cent"RM 0.30 fixed discount per transaction100 - 0.30 = RM 99.70
unit_price: "-0.6000", value: "60 cent"RM 0.60 fixed discount per transaction100 - 0.60 = RM 99.40

Price Adjustment ​

Optional. If null, no adjustment is applied.

TypeCalculationExample (RM 100)
fixeduser_pays = price + value100 + 0.50 = RM 100.50
percentageuser_pays = price × value100 × 1.03 = RM 103.00

See the Pricing Guide for margin strategies and the Product Examples for worked calculations.

Error Responses ​

400 — Validation Error ​

json
{
  "message": "The given data was invalid.",
  "errors": {
    "is_active": ["The value 'yes' is not valid."]
  }
}

401 — Authentication Failed ​

json
{
  "message": "Unauthorized",
  "metadata": { "status_code": "401" }
}

See API Key Authentication for the complete list of 401 causes.

Product Code No Match ​

Returns 200 OK with an empty products map. Groups and categories that contain no matching products are removed from the tree, so the tree is empty too:

json
{
  "last_updated": "2025-01-07T00:00:00Z",
  "tree": { "groups": [] },
  "products": {}
}

IIMMPACT API Documentation