How to use Benefit Calculator with Package Search API

Endpoint: POST /product/compare/benefit-analysis

This guide explains how to call the Benefit Calculator API, build valid request payloads, and interpret the response. Use it together with the IMG Comparator API OpenAPI spec for full schema details.


1. What is the Benefit Calculator?

The Benefit Calculator takes a customer’s current (previous) health fund policy and actual annual usage of extras services, then compares that against other packages in the IMG system.
It returns:

  • Claimable benefits — How much the customer can claim from each policy for their usage.
  • Net benefit — Benefits received minus premiums (can be negative).
  • Switch & Save — Yearly (or weekly/fortnightly/monthly) savings if they move from their current policy to each suggested one.

So agents can recommend policies based on real usage and out-of-pocket costs, not only on headline limits.


2. Prerequisites

You can only get benefit calculator results when all of the following are true:

RequirementDetails
Previous fundThe customer must have a previous fund.
Extras coverageThe customer cannot be on hospital-only.
Membership typeapplication.policy.membership_type must match benefit_analysis_data.previous_fund.membership_type.
Usage dataYou must send at least one participant with at least one benefit (service usage) in benefit_analysis_data.participants.
Corporate accessThe corporate must have benefit calculator API access enabled.

3. Request overview

The request body is the same shape as the standard package search, with two differences:

  1. Filters: filters.apply_benefit_analysis must be true.
  2. Root-level object: benefit_analysis_data is required and must contain participants, state, cover_type, membership_type, and previous_fund.

Top-level fields:

FieldRequiredDescription
applicationYesSame as package search: personal, primary_address, policy; optional partner.
filtersYesSame as package search; must include "apply_benefit_analysis": true.
servicesYeshospital (array of service IDs), ancillary (array of service IDs). From /product/services.
benefit_analysis_dataYesSee section 4.

application.personal.previous_fund must be populated (has_previous_fund, fund_id, fund_name, hospital_product and/or ancillary_product, membership) so the backend knows the current policy. Product IDs there should match the IDs you put in benefit_analysis_data.previous_fund.


4. benefit_analysis_data

This object drives the benefit calculation. All of these are required at the root of benefit_analysis_data:

FieldTypeRequiredDescription
participantsarrayYesAt least one participant; see participant rules below.
statestringYesAustralian state code (e.g. "VIC"). Must match application.primary_address.state.
cover_typeintegerYes0 = Hospital, 1 = Extras, 2 = Combined. Cannot be 0 (hospital-only).
membership_typeintegerYes0 = Single, 1 = Couple, 2 = Family, 3 = Single Parent, etc. Must match application.policy.membership_type and previous_fund.membership_type.
previous_fundobjectYesCurrent policy identifiers; see below.

4.1 previous_fund

Describes the customer’s current policy so the engine can compare it to alternatives.

Required for all:

FieldTypeDescription
membership_typeintegerSame as above (0/1/2/3).
fund_namestringe.g. "BUPA", "NIB Health Funds Ltd.".
product_typeinteger0 = Hospital, 1 = Extras, 2 = Combined.
product_namestringProduct name (e.g. combined product name or hospital product name).
hospital_product_idintegerRequired. Price ID from /product/ (Product Search). For combined products this is the single combined price ID. For separate hospital + extras, this is the hospital price ID.

Required only for synthetic (separate hospital + extras) policies:

FieldTypeDescription
ancillary_product_idintegerExtras product price ID from /product/. Omit for combined products.

Example — combined product (single product for hospital + extras):

"previous_fund": {
  "membership_type": 0,
  "fund_name": "BUPA",
  "product_type": 2,
  "product_name": "Gold Ultimate Health Cover",
  "hospital_product_id": 6529563
}

Example — synthetic (separate hospital and extras):

"previous_fund": {
  "membership_type": 2,
  "fund_name": "ACA Health Benefits Fund",
  "product_type": 0,
  "product_name": "Bronze Essentials Hospital 750",
  "hospital_product_id": 6435350,
  "ancillary_product_id": 6435563
}

Get these IDs from the Product Search API (/product/) using fund, membership, state, policy type, and product name.

4.2 participants

Participants are the people on the policy whose extras usage you are modelling. Rules:

  • Exactly one primary.
  • At most one partner.
  • Up to five dependants: dependent_1dependent_5.

If you send more than 1 primary, or more than 1 partner, or more than 5 dependants, the API returns 400: "Maximum 1 primary, 1 partner, and 5 dependants allowed".

Each participant has:

FieldTypeRequiredDescription
typestringYesOne of: primary, partner, dependent_1, dependent_2, dependent_3, dependent_4, dependent_5.
benefitsarrayYesAt least one benefit object; see below.

4.3 benefits (per participant)

Each item in benefits is one type of service usage for that participant.

FieldTypeRequiredDescription
service_idintegerYesAncillary service ID from /product/services (ancillary section). e.g. General dental = 1, Major dental = 8.
namestringYesBenefit name from /product/service-benefits. Use the value field (e.g. "CheckAndClean", "SimpleExtraction", "Crown").
number_of_visitsstringYesNumber of times this service is used per year by this participant. Pattern: digits only, e.g. "3".
gross_paystringYesOut-of-pocket cost per visit (not annual). Pattern: number, optional 2 decimal places, e.g. "100" or "99.50".

You need to map:

  • Service IDs from /product/services (ancillary).
  • Benefit names from /product/service-benefits (use the value field in the response).

Example for one primary:

"participants": [
  {
    "type": "primary",
    "benefits": [
      {
        "service_id": 1,
        "name": "CheckAndClean",
        "number_of_visits": "3",
        "gross_pay": "100"
      },
      {
        "service_id": 1,
        "name": "SimpleExtraction",
        "number_of_visits": "2",
        "gross_pay": "100"
      }
    ]
  }
]

5. Enabling the benefit calculator in the request

In filters, set:

"apply_benefit_analysis": true

You can combine this with other package-search filters (e.g. page_size, offset, display_order, hospital_classifications, show_current_policy, separate_always_include_packages). For benefit calculator, show_current_policy and show_products_from_current_fund are often set to true so the current policy appears and can be compared.


6. Capturing service usage (conversation → payload)

The calculator is only as good as the usage you send. Agents should ask:

  • How many times per year they use each type of service (e.g. dentist, physio, optical).
  • How much they pay out of pocket per visit for that service.

Example:

  • “How many times a year do you go to the dentist?” → 3
  • “Do you have extractions or similar?” → 1 or 2 a year
  • “Roughly how much do you pay per visit?” → $100

Mapping into the payload:

  • Check and clean: service_id 1 (General dental), name "CheckAndClean", number_of_visits "3", gross_pay "100".
  • Simple extraction: same service_id 1, name "SimpleExtraction", number_of_visits "2", gross_pay "100".

Use /product/services to get service_ids and /product/service-benefits to get the exact name (value) for each benefit you want to model.


7. Response structure

On success (200), the body matches the Package Search response shape, with these additions:

FieldLocationDescription
packagesrootSame as package search, but each price in packages[].prices[] may include benefit_analysis_price.
totalrootTotal number of packages (before pagination).
limitrootPage size (e.g. from filters.page_size).
applicantrootDerived applicant info (DOB, LHC, rebate, etc.).
benefit_analysis_previous_productrootNew. Benefit analysis result for the current policy (name, net benefits, fund, amount, benefits, total_rebate, etc.). Use this to show “your current policy” in the comparison.
always_include_packagesrootPresent when filters.separate_always_include_packages is true. Packages that are always shown (e.g. current policy).

Each price inside packages[].prices[] can have:

FieldDescription
benefit_analysis_pricenull for the current policy (when it appears in the list). For other packages, an object with net benefit and switch & save figures; see below.

For each alternative package, benefit_analysis_price contains the comparison against the current policy. The most important fields for clients are the switch & save and total_rebate values.

8.1 Switch & Save (most important for “how much will I save?”)

These are the savings (or extra cost) if the customer switches from their current policy to this package. Positive = saving; negative = extra cost.

FieldDescription
switch_and_save_weeklyWeekly saving (if present).
switch_and_save_fortnightlyFortnightly saving (including LHC).
switch_and_save_monthlyMonthly saving (including LHC).
switch_and_save_yearlyYearly saving (including LHC).
switch_and_save_fortnightly_no_lhcFortnightly saving excluding LHC.
switch_and_save_monthly_no_lhcMonthly saving excluding LHC.
switch_and_save_yearly_no_lhcYearly saving excluding LHC.

Example:

"switch_and_save_fortnightly": 33.08,
"switch_and_save_monthly": 71.28,
"switch_and_save_yearly": 855.41,
"switch_and_save_fortnightly_no_lhc": 33.08,
"switch_and_save_monthly_no_lhc": 71.28,
"switch_and_save_yearly_no_lhc": 855.41

It represents the estimated difference in what the customer pays overall when comparing their current policy to this one, based on the usage data you provided.

8.2 Net benefit

Net benefit = benefits received minus premiums. Negative means the customer pays more in premium than they get back in benefits for the usage you sent.

FieldDescription
net_benefit_weekly / net_benefit_fortnightly / net_benefit_monthly / net_benefit_yearlyWith LHC.
net_benefit_*_no_lhcSame, excluding LHC.

When benefit analysis is on, results are ordered by switch and save (largest savings first, depending on sort order).

8.3 total_rebate and claims

benefit_analysis_price.total_rebate is an object keyed by benefit group (e.g. "DentalGeneral-DentalMajor-Endodontic"). Each value has:

FieldMeaning
rebateTotal rebate/benefit amount for that group.
claimsClaimable amount for the usage you sent — this is what the customer can get back from this policy for that group.

Example:

"total_rebate": {
  "DentalGeneral-DentalMajor-Endodontic": {
    "rebate": 498.0,
    "claims": 498.0
  }
}

Here, for that group the customer can claim $498 (e.g. against a $650 limit). The best policy for the customer is often not the one with the highest limit, but the one where:

  • Claimable amount is close to (but within) their actual spending.
  • Premium is acceptable.
  • Waiting periods are acceptable.

You can use claims to explain: “With your usage, you’d get $X back from this policy for dental.”

8.4 amount and benefits

  • amount — Array of pricing breakdowns (base premium, LHC, final premium by frequency). Useful for displaying premiums.
  • benefits — Array of service groups with limits, waiting periods, and individual benefit policies. Useful for “what’s covered” and limits.

9. benefit_analysis_previous_product (current policy)

This root-level object describes the current policy under the same usage. It includes:

  • name — Combined product name (e.g. hospital + ancillary).
  • net_benefit_* and net_benefit_*_no_lhc — Net benefit for the current policy.
  • fund — id, name, discount.
  • amount — Pricing breakdown.
  • benefits — Benefit groups and limits.
  • total_rebate — Same structure as above (rebate and claims per group).
  • is_current_producttrue.

Use it to show “Your current policy” and compare it side by side with packages[].prices[].benefit_analysis_price for alternatives.


10. Example scenario (how the numbers fit together)

MessageCause
benefit_analysis_data is required when apply_benefit_analysis is trueYou set apply_benefit_analysis: true but omitted or invalidated benefit_analysis_data.
Benefit calculator requires extras coverage. Hospital-only policies are not supported.cover_type is 0 or customer has no extras.
Membership type must match previous fund membership typeapplication.policy.membership_typebenefit_analysis_data.previous_fund.membership_type.
Previous fund information is required for benefit calculatorNo or incomplete previous fund in application or benefit_analysis_data.
Maximum 1 primary, 1 partner, and 5 dependents allowedToo many or duplicate participant types.
ancillary_product_id is required for synthetic products (separate hospital and extras)Customer has separate hospital and extras but you didn’t send previous_fund.ancillary_product_id.

Always check the message field in the JSON body for the exact reason.


11. Example scenario (how the numbers fit together)

  • Current fund (e.g. nib): Premium $1,813.91/year. Claims (e.g. check & clean 3×, simple extraction 2×): $300 back. Net cost: $1,513.91.
  • Alternative 1 (e.g. Defence Health): Premium $1,227.35/year. Claims $417.40. Net cost: $809.95. Switch & Save: $703.96/year.
  • Alternative 2 (e.g. Australian Unity $750 excess): Premium $1,086.76/year. Claims $262. Net cost: $824.76. Switch & Save: $689.15/year.

The switch_and_save_yearly in the response is the difference between the current net cost and that alternative’s net cost. The “winner” is the package with the highest switch-and-save (or best net benefit) that still fits the customer’s needs (limits, waiting periods, premium).


12. Using the Benefit Calculator in sales

The response (packages, benefit_analysis_price, benefit_analysis_previous_product, total_rebate, switch & save) supports sales agents and workflows in several ways:

  • Explain why a policy is recommended (limits vs usage, premium vs claims).
  • Handle “switch and lose” cases (negative switch & save) by suggesting upgrades or explaining trade-offs.
  • Compare current vs recommended in plain language for the customer.

Results are not always positive; some customers will see negative switch & save. In those cases, context (e.g. better long-term benefits, different usage later) can still justify a recommendation and improve conversion.


13. Full example payloads

13.1 Single member, combined product (hospital + extras in one)

{
  "application": {
    "personal": {
      "medicare_card": true,
      "previous_fund": {
        "has_previous_fund": true,
        "fund_id": "bup",
        "fund_name": "BUPA",
        "membership": 0,
        "hospital_product": {
          "id": 6529563,
          "name": "Gold Ultimate Health Cover"
        }
      },
      "dob": "1990-11-22"
    },
    "primary_address": { "state": "VIC" },
    "policy": {
      "membership_type": 0,
      "cover_type": 2,
      "lhc_age": 30,
      "federal_rebate": true,
      "rebate_tier": 0
    }
  },
  "corporate_id": 1,
  "filters": {
    "display_order": "price_lowhi",
    "page_size": [15],
    "offset": 0,
    "show_current_policy": true,
    "show_products_from_current_fund": true,
    "include_future_pricing": true,
    "hospital_classifications": [
      { "code": "Bronze", "name": "Bronze", "selected": true },
      { "code": "Silver", "name": "Silver", "selected": true },
      { "code": "Gold", "name": "Gold", "selected": true }
    ],
    "apply_benefit_analysis": true,
    "separate_always_include_packages": true
  },
  "services": {
    "hospital": [16],
    "ancillary": [1, 8]
  },
  "benefit_analysis_data": {
    "participants": [
      {
        "type": "primary",
        "benefits": [
          { "service_id": 1, "name": "CheckAndClean", "number_of_visits": "3", "gross_pay": "100" },
          {
            "service_id": 1,
            "name": "SimpleExtraction",
            "number_of_visits": "2",
            "gross_pay": "100"
          }
        ]
      }
    ],
    "state": "VIC",
    "cover_type": 2,
    "membership_type": 0,
    "previous_fund": {
      "membership_type": 0,
      "fund_name": "BUPA",
      "product_type": 2,
      "product_name": "Gold Ultimate Health Cover",
      "hospital_product_id": 6529563
    }
  }
}

13.2 Family, synthetic product (separate hospital + extras)

{
  "application": {
    "personal": {
      "medicare_card": true,
      "previous_fund": {
        "has_previous_fund": true,
        "fund_id": "aca",
        "fund_name": "ACA Health Benefits Fund",
        "membership": 2,
        "hospital_product": { "id": 6435350, "name": "Bronze Essentials Hospital 750" },
        "ancillary_product": { "id": 6435563, "name": "Complete Ancillary" }
      },
      "dob": "1985-11-22"
    },
    "partner": { "dob": "1995-10-14" },
    "primary_address": { "state": "VIC" },
    "policy": {
      "membership_type": 2,
      "cover_type": 2,
      "lhc_age": 30,
      "lhc_age_partner": 30,
      "federal_rebate": true,
      "rebate_tier": 0
    }
  },
  "corporate_id": 1,
  "filters": {
    "display_order": "price_lowhi",
    "page_size": [15],
    "offset": 0,
    "show_current_policy": true,
    "apply_benefit_analysis": true
  },
  "services": { "hospital": [16], "ancillary": [1, 8] },
  "benefit_analysis_data": {
    "participants": [
      {
        "type": "primary",
        "benefits": [
          { "service_id": 1, "name": "CheckAndClean", "number_of_visits": "3", "gross_pay": "100" },
          {
            "service_id": 8,
            "name": "MajorExtraction",
            "number_of_visits": "2",
            "gross_pay": "200"
          }
        ]
      },
      {
        "type": "partner",
        "benefits": [
          { "service_id": 1, "name": "CheckAndClean", "number_of_visits": "4", "gross_pay": "150" },
          { "service_id": 8, "name": "Crown", "number_of_visits": "2", "gross_pay": "400" }
        ]
      },
      {
        "type": "dependent_1",
        "benefits": [
          { "service_id": 1, "name": "CheckAndClean", "number_of_visits": "2", "gross_pay": "80" }
        ]
      }
    ],
    "state": "VIC",
    "cover_type": 2,
    "membership_type": 2,
    "previous_fund": {
      "membership_type": 2,
      "fund_name": "ACA Health Benefits Fund",
      "product_type": 0,
      "product_name": "Bronze Essentials Hospital 750",
      "hospital_product_id": 6435350,
      "ancillary_product_id": 6435563
    }
  }
}

13.3 Minimal benefit_analysis_data (for reference)

"benefit_analysis_data": {
  "participants": [
    {
      "type": "primary",
      "benefits": [
        { "service_id": 1, "name": "CheckAndClean", "number_of_visits": "3", "gross_pay": "100" },
        { "service_id": 1, "name": "SimpleExtraction", "number_of_visits": "2", "gross_pay": "100" }
      ]
    }
  ],
  "state": "VIC",
  "cover_type": 2,
  "membership_type": 0,
  "previous_fund": {
    "membership_type": 0,
    "fund_name": "BUPA",
    "product_type": 2,
    "product_name": "Gold Ultimate Health Cover",
    "hospital_product_id": 6529563
  }
}

  • Product Search (GET /product/) — Find product/price IDs by fund, membership, state, policy type, and name. Use these IDs in application.personal.previous_fund and benefit_analysis_data.previous_fund.
  • Services (GET /product/services) — List hospital and ancillary services; use for services.hospital / services.ancillary and for service_id in participants’ benefits.
  • Service benefits (GET /product/service-benefits) — List benefit names per service; use the value field (e.g. CheckAndClean) for benefit_analysis_data.participants[].benefits[].name.

For full request/response schemas, enums, and examples, see the IMG Comparator API specification.