Appearance
Are you an LLM? You can read better optimized documentation at /guide/integration/catalog/options-api.md for this page in Markdown format
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/options1
Request Headers
| Header | Description | Required |
|---|---|---|
Authorization | IdToken (Bearer) | Yes |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
product_code | string | Yes | Product code (e.g., D, HI, JOMPAY) |
field_id | string | Yes | Field identifier from catalog (e.g., amount, plan, biller) |
account_number | string | Conditional | Required for dynamic fields (phone number, NRIC, etc.) |
biller_code | string | No | Filter by specific biller code (JomPAY only) |
limit | number | No | Items per page (default: 100, max: 500) |
page | number | No | Page 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}"1
2
2
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
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| Field | Type | Description |
|---|---|---|
product_code | string | Echoed product code |
field_id | string | Echoed field identifier |
items | array | List of selectable items |
meta | object | Pagination 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" }
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| Field | Type | Description | Used By |
|---|---|---|---|
code | string | Stable identifier for fulfillment | All products |
label | string | Display text | All products |
description | string | Additional details | Plans, accounts |
value | object | What user receives | Games, international top-ups |
price | object | Base selling price | All priced options |
cost | object | Your wholesale cost (B2B) | fixed_per_item products |
rrp | object | Recommended retail price | Games (for strikethrough) |
validity | string | Plan duration | Data plans |
account_number | string | Maps to topup.account | PTPTN accounts |
min_amount | object | Minimum payment amount | JomPAY billers |
max_amount | object | Maximum payment amount | JomPAY 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" }1
2
3
4
5
2
3
4
5
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}"1
2
2
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
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Hotlink Internet — Dynamic Plans
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}"1
2
2
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
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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}"1
2
2
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
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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}"1
2
2
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
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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}"1
2
2
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
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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}"1
2
2
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
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Caching Strategy
| Data Type | Recommendation |
|---|---|
reference | Cache in database, refresh daily |
dynamic | Cache per input value, short TTL (5-10 mins) |
For large reference lists like JomPAY billers:
- Initial sync: Fetch all pages and store in your database
- Daily refresh: Re-sync the full list overnight
- Lookup: Query your local database for fast biller search
For dynamic options (mobile plans, PTPTN accounts):
- Cache per account: Store results keyed by
product_code:account_number - 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."]
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
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."
]
}
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
404 Not Found
Invalid product or field:
json
{
"error": "not_found",
"message": "Product or field not found",
"details": {
"product_code": "INVALID",
"field_id": "amount"
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
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" }
}
]
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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");
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
See the Overview for webhook registration and signature verification.
