Skip to content

Options API

The Options API returns selectable items for select fields. This is a unified endpoint that replaces the product-specific subproduct endpoints, providing consistent structure for all product types.

Endpoint

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

Request Headers

HeaderDescriptionRequired
AuthorizationIdToken (Bearer)Yes

Query Parameters

ParameterTypeRequiredDescription
product_codestringYesProduct code (e.g., D, HI, JOMPAY)
field_idstringYesField identifier from catalog (e.g., amount, plan, biller)
account_numberstringConditionalRequired for dynamic fields (phone number, NRIC, etc.)
biller_codestringNoFilter by specific biller code (JomPAY only)
limitnumberNoItems per page (default: 100, max: 500)
pagenumberNoPage number for pagination (default: 1)

Example Request with Pagination

bash
curl -X GET "https://api.iimmpact.com/v2/options?product_code=JOMPAY&field_id=biller&limit=50&page=2" \
  -H "Authorization: Bearer {idToken}"

Response Structure

json
{
  "product_code": "HI",
  "field_id": "plan",
  "items": [
    {
      "code": "HI_UNL_30_3M",
      "label": "Unlimited 30 Days (3Mbps)",
      "description": "Unlimited data with hotspot and calls",
      "price": { "amount": "40.00", "currency": "MYR" },
      "validity": "30 days"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 10,
    "total": 5
  }
}
FieldTypeDescription
product_codestringEchoed product code
field_idstringEchoed field identifier
itemsarrayList of selectable items
metaobjectPagination metadata

Item Schema

Items follow a consistent structure with optional fields based on product type:

json
{
  "code": "string",
  "label": "string",
  "description": "string",

  "value": { "amount": 325, "unit": "UC" },

  "price": { "amount": "23.00", "currency": "MYR" },
  "cost": { "amount": "19.50", "currency": "MYR" },
  "rrp": { "amount": "25.00", "currency": "MYR" },

  "validity": "30 days",
  "account_number": "12345678",

  "min_amount": { "amount": "1.00", "currency": "MYR" },
  "max_amount": { "amount": "30000.00", "currency": "MYR" }
}
FieldTypeDescriptionUsed By
codestringStable identifier for fulfillmentAll products
labelstringDisplay textAll products
descriptionstringAdditional detailsPlans, accounts
valueobjectWhat user receivesGames, international top-ups
priceobjectBase selling priceAll priced options
costobjectYour wholesale cost (B2B)fixed_per_item products
rrpobjectRecommended retail priceGames (for strikethrough)
validitystringPlan durationData plans
account_numberstringMaps to topup.accountPTPTN accounts
min_amountobjectMinimum payment amountJomPAY billers
max_amountobjectMaximum payment amountJomPAY billers

Value Object

The value field represents what the user receives, which may differ from the price:

json
// Games - user receives UC/Diamonds
{ "amount": 325, "unit": "UC" }

// International top-up - user receives foreign currency
{ "amount": 100, "currency": "BDT" }

B2B Data

The cost field contains your wholesale pricing. Do not expose this to end users.


Product-Specific Examples

Digi Prepaid — Fixed Amounts

Static denomination list with pricing.

Request:

bash
curl -X GET "https://api.iimmpact.com/v2/options?product_code=D&field_id=amount" \
  -H "Authorization: Bearer {idToken}"

Response:

json
{
  "product_code": "D",
  "field_id": "amount",
  "items": [
    {
      "code": "5",
      "label": "RM 5",
      "price": { "amount": "5.00", "currency": "MYR" }
    },
    {
      "code": "10",
      "label": "RM 10",
      "price": { "amount": "10.00", "currency": "MYR" }
    },
    {
      "code": "30",
      "label": "RM 30",
      "price": { "amount": "30.00", "currency": "MYR" }
    },
    {
      "code": "50",
      "label": "RM 50",
      "price": { "amount": "50.00", "currency": "MYR" }
    },
    {
      "code": "100",
      "label": "RM 100",
      "price": { "amount": "100.00", "currency": "MYR" }
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 5,
    "total": 5
  }
}

Plans tailored to the specific phone number.

Request:

bash
curl -X GET "https://api.iimmpact.com/v2/options?product_code=HI&field_id=plan&account_number=0178855286" \
  -H "Authorization: Bearer {idToken}"

Response:

json
{
  "product_code": "HI",
  "field_id": "plan",
  "items": [
    {
      "code": "Everything Unlimited with Unlimited Weekly Pass RM12",
      "label": "Unlimited Weekly Pass",
      "description": "Everything Unlimited with Unlimited Weekly Pass RM12",
      "price": { "amount": "12.00", "currency": "MYR" },
      "validity": "7 days"
    },
    {
      "code": "Unlimited data with hotspot and calls 30-days (3Mbps) H",
      "label": "Unlimited 30 Days (3Mbps)",
      "description": "Unlimited data with hotspot and calls",
      "price": { "amount": "40.00", "currency": "MYR" },
      "validity": "30 days"
    },
    {
      "code": "Unlimited data with hotspot and calls 30-days (6Mbps) H",
      "label": "Unlimited 30 Days (6Mbps)",
      "description": "Unlimited data with hotspot and calls",
      "price": { "amount": "50.00", "currency": "MYR" },
      "validity": "30 days"
    },
    {
      "code": "Unlimited RM55 12Mbps with 5G (100GB) 30D",
      "label": "Unlimited 5G (12Mbps)",
      "description": "100GB 5G data included",
      "price": { "amount": "55.00", "currency": "MYR" },
      "validity": "30 days"
    },
    {
      "code": "Unlimited data with hotspot and calls 30-days (18Mbps) H",
      "label": "Unlimited 30 Days (18Mbps)",
      "description": "Unlimited data with hotspot and calls",
      "price": { "amount": "60.00", "currency": "MYR" },
      "validity": "30 days"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 5,
    "total": 5
  }
}

Dynamic Plans

Plans are uniquely tailored to individual phone numbers based on user eligibility. Always pass the account_number parameter for dynamic fields.


PTPTN — Loan Accounts

Returns accounts linked to the NRIC, with account numbers for payment.

Request:

bash
curl -X GET "https://api.iimmpact.com/v2/options?product_code=PTPTN&field_id=loan_account&account_number=941123045001" \
  -H "Authorization: Bearer {idToken}"

Response:

json
{
  "product_code": "PTPTN",
  "field_id": "loan_account",
  "items": [
    {
      "code": "S",
      "label": "KELVIN LEE WEI SERN",
      "description": "SSPN Prime",
      "account_number": "009411230450014"
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 1,
    "total": 1
  }
}

Account Number Mapping

For PTPTN, the account_number in the item response is used as the account field in the payment request, not the NRIC.


JomPAY — Biller List

Large reference list (21,000+ billers) with dynamic validation limits.

Request (paginated):

bash
curl -X GET "https://api.iimmpact.com/v2/options?product_code=JOMPAY&field_id=biller&limit=3" \
  -H "Authorization: Bearer {idToken}"

Response:

json
{
  "product_code": "JOMPAY",
  "field_id": "biller",
  "items": [
    {
      "code": "818625",
      "label": "TADIKA DIDIKAN SOLEH PLT.",
      "min_amount": { "amount": "1.00", "currency": "MYR" },
      "max_amount": { "amount": "30000.00", "currency": "MYR" }
    },
    {
      "code": "779264",
      "label": "RIM PRO AUTOMOTIVE.",
      "min_amount": { "amount": "1.00", "currency": "MYR" },
      "max_amount": { "amount": "30000.00", "currency": "MYR" }
    },
    {
      "code": "249722",
      "label": "ALANG MILA ENTERPRISE",
      "min_amount": { "amount": "1.00", "currency": "MYR" },
      "max_amount": { "amount": "30000.00", "currency": "MYR" }
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 7096,
    "per_page": 3,
    "total": 21287
  }
}

Filter by Biller Code:

bash
curl -X GET "https://api.iimmpact.com/v2/options?product_code=JOMPAY&field_id=biller&biller_code=818625" \
  -H "Authorization: Bearer {idToken}"
json
{
  "product_code": "JOMPAY",
  "field_id": "biller",
  "items": [
    {
      "code": "818625",
      "label": "TADIKA DIDIKAN SOLEH PLT.",
      "min_amount": { "amount": "1.00", "currency": "MYR" },
      "max_amount": { "amount": "30000.00", "currency": "MYR" }
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 1,
    "total": 1
  }
}

Dynamic Validation

Use min_amount and max_amount from the selected biller to validate the payment amount field dynamically.

Caching Recommendation

For large lists like JomPAY billers, we recommend caching the full list in your database and refreshing periodically (e.g., daily). This provides faster lookups and reduces API calls.


PUBG Mobile — Game Packages (Fixed Per Item Pricing)

Shows what user receives (UC), what they pay (price), and your wholesale cost.

Request:

bash
curl -X GET "https://api.iimmpact.com/v2/options?product_code=PUBG&field_id=package" \
  -H "Authorization: Bearer {idToken}"

Response:

json
{
  "product_code": "PUBG",
  "field_id": "package",
  "items": [
    {
      "code": "60",
      "label": "60 UC",
      "value": { "amount": 60, "unit": "UC" },
      "price": { "amount": "5.00", "currency": "MYR" },
      "cost": { "amount": "4.50", "currency": "MYR" },
      "rrp": { "amount": "5.50", "currency": "MYR" }
    },
    {
      "code": "325",
      "label": "325 UC",
      "value": { "amount": 325, "unit": "UC" },
      "price": { "amount": "23.00", "currency": "MYR" },
      "cost": { "amount": "19.50", "currency": "MYR" },
      "rrp": { "amount": "25.00", "currency": "MYR" }
    },
    {
      "code": "660",
      "label": "660 UC",
      "value": { "amount": 660, "unit": "UC" },
      "price": { "amount": "50.00", "currency": "MYR" },
      "cost": { "amount": "42.00", "currency": "MYR" },
      "rrp": { "amount": "55.00", "currency": "MYR" }
    }
  ],
  "meta": {
    "current_page": 1,
    "last_page": 1,
    "per_page": 3,
    "total": 3
  }
}

RRP Display

Use rrp (recommended retail price) to show a strikethrough price, indicating savings compared to official pricing.

B2B Data

The cost field is for your backend only. Your margin = price - cost. Do not expose cost to end users.


Pagination

For large lists (like JomPAY billers with 21,000+ items), use page-based pagination:

javascript
async function fetchAllBillers() {
  const billers = [];
  let page = 1;
  let lastPage = 1;

  do {
    const params = new URLSearchParams({
      product_code: "JOMPAY",
      field_id: "biller",
      limit: "100",
      page: String(page),
    });

    const response = await fetch(
      `https://api.iimmpact.com/v2/options?${params}`,
      { headers: { Authorization: `Bearer ${idToken}` } },
    );

    const data = await response.json();
    billers.push(...data.items);
    lastPage = data.meta.last_page;
    page++;
  } while (page <= lastPage);

  return billers;
}

Caching Strategy

Data TypeRecommendation
referenceCache in database, refresh daily
dynamicCache per input value, short TTL (5-10 mins)

For large reference lists like JomPAY billers:

  1. Initial sync: Fetch all pages and store in your database
  2. Daily refresh: Re-sync the full list overnight
  3. Lookup: Query your local database for fast biller search

For dynamic options (mobile plans, PTPTN accounts):

  1. Cache per account: Store results keyed by product_code:account_number
  2. Short TTL: Refresh every 5-10 minutes as plans may change

Error Responses

400 Bad Request

Missing required parameters:

json
{
  "error": "validation_error",
  "message": "The given data was invalid",
  "details": {
    "product_code": ["The product_code field is required."],
    "field_id": ["The field_id field is required."]
  }
}

400 Bad Request — Missing Account Number

Dynamic field requires account number:

json
{
  "error": "validation_error",
  "message": "account_number is required for dynamic fields",
  "details": {
    "account_number": [
      "This field depends on phone and requires account_number parameter."
    ]
  }
}

404 Not Found

Invalid product or field:

json
{
  "error": "not_found",
  "message": "Product or field not found",
  "details": {
    "product_code": "INVALID",
    "field_id": "amount"
  }
}

Webhooks

When options change (denominations added/removed, costs updated), IIMMPACT sends an options.sync webhook with the full options payload. Your backend should replace the existing data.

options.sync Payload

json
{
  "event": "options.sync",
  "timestamp": "2025-01-08T10:30:00Z",
  "data": {
    "product_code": "PUBG",
    "field_id": "package",
    "items": [
      {
        "code": "60",
        "label": "60 UC",
        "price": { "amount": "5.00", "currency": "MYR" },
        "cost": { "amount": "4.50", "currency": "MYR" }
      },
      {
        "code": "325",
        "label": "325 UC",
        "price": { "amount": "23.00", "currency": "MYR" },
        "cost": { "amount": "19.50", "currency": "MYR" }
      }
    ]
  }
}

Handling

javascript
app.post("/iimmpact/webhook", async (req, res) => {
  const { event, data } = req.body;

  if (event === "options.sync") {
    // Full replace for this product+field
    await db.options.replaceOne(
      { product_code: data.product_code, field_id: data.field_id },
      { ...data, synced_at: new Date() },
      { upsert: true },
    );
  }

  res.status(200).send("OK");
});

See the Overview for webhook registration and signature verification.

IIMMPACT API Documentation