HyperExponential (Hx) Integration¶
HyperExponential (Hx) is an actuarial pricing platform. Workbench integrates with it so that underwriters can price risks in Hx while Workbench remains the system of record. The underwriter never leaves Workbench to trigger or complete the pricing cycle — Hx is accessed via an embedded link and the pricing data flows back automatically.
How it works¶
The integration is built around a pull/push model:
- When a quote action is triggered in Workbench, the Rating Service creates a policy in Hx via the Hx API.
- Hx pulls risk data from Workbench via a GET endpoint.
- The underwriter prices the risk in the Hx UI.
- Hx pushes the pricing results back to Workbench via a POST endpoint.
- The underwriter completes the quote in Workbench, which finalises the Hx policy.
The same core flow applies to new business, renewals, and MTAs — with some differences at step 1 and 2 depending on transaction type (covered below).
New business flow¶
1. Underwriter triggers Quote action in Workbench
│
▼
2. Rating Service creates a new policy in Hx
POST {hxApiUrl}/policies
→ Hx returns: policyId, policyOptionId, policyOptionURL (renew_url)
│
▼
3. Underwriter clicks through to Hx via the policyOptionURL
│
▼
4. Hx pulls risk data from Workbench
GET https://api.{client-env}.sendworkbench.com/policies/hx/mapped/{policyGroupId}/structured-quote
│
▼
5. Underwriter reviews data and prices the risk in Hx
│
▼
6. Hx pushes pricing results back to Workbench
POST https://api.{client-env}.sendworkbench.com/policies/hx/mapped/{policyGroupId}/structured-quote
│
▼
7. Underwriter triggers "Complete Quote" action in Workbench
→ Rating Service calls POST {hxApiUrl}/complete-quote
→ Hx policy status moves to FINAL
│
▼
8. Pricing data (technical price, achieved price, machine price) is written to the risk record
What Workbench sends to Hx on policy creation¶
When Workbench creates the Hx policy (step 2), it sends:
| Field | Source |
|---|---|
name |
Insured party name from the risk |
role |
From hxPricingConfig.role (e.g. "underwriter") |
team |
Derived from business class via teamsProviderConfig |
live |
From hxPricingConfig.policyCreatedAsLive (default: false) |
customerId |
Workbench policy group ID (if sendCustomerId: true) |
pasReferences |
Workbench policy group ID — used to link Hx and Workbench together |
modelVersionId |
Selected based on pricingStrategy config |
description |
Policy description |
tags |
Risk type + WORK_IN_PROGRESS |
initialOption.name |
Policy option name |
initialOption.inceptionDate / expiryDate |
Risk dates, formatted as yyyy-MM-dd |
Renewal flow¶
The renewal flow differs from new business at the policy creation step. Instead of creating a fresh policy, Workbench clones the expiring Hx policy.
1. Renewal risk created in Workbench (manually or via renewal action)
│
▼
2. Rating Service clones the expiring Hx policy
POST {hxApiUrl}/policies/{expiringPolicyId}/renew
→ Returns new policyId, policyOptionId, policyOptionURL
│
▼
3. If importExpiringPolicyData: true (default):
Hx imports expiring policy data automatically
POST {hxApiUrl}/policy-option-instances/{policyOptionId}/import-expiring-policy-data
→ Prefills the Hx model with last year's data
│
▼
4–8. Same pull/push cycle as new business
Key renewal configuration decisions¶
importExpiringPolicyData (default: true) — controls whether Hx automatically prefills the renewal with the expiring policy's data. This is almost always what you want. Set to false only if the expiring Hx model is incompatible with the renewal model (e.g. the model structure changed significantly between years).
shouldSetExpiringPolicyReferenceOnRenewals (default: false) — sets the expiringPolicyNumber field on the Hx renewal policy. Enable if the Hx model uses this field for continuity calculations (e.g. prior year premium comparisons).
pricingStrategy — controls which model version is used for the renewal:
| Value | Behaviour |
|---|---|
INCEPTION_DATE |
Uses the Hx model version that was live on the risk's inception date. Recommended for most clients — ensures the renewal uses the same model generation that was in use when the risk was originally written |
FIRST_QUOTE |
Locks to the model version used on the very first quote for the risk, regardless of model updates |
The pull/push data model¶
This is what Hx receives when it pulls data from Workbench, and what it sends back after pricing.
Pull (GET) — what Hx reads from Workbench¶
GET https://api.{client-env}.sendworkbench.com/policies/hx/mapped/{policyGroupId}/structured-quote
Top-level fields:
| Field | Description |
|---|---|
sendPolicyId |
Workbench policy group ID |
riskId |
Workbench risk ID |
transactionType |
NEW, RENEWAL, or MTA |
stage |
Current Workbench risk stage (e.g. QUOTE, WORK_IN_PROGRESS) |
currency |
Risk currency |
inceptionDate / expiryDate |
Policy period |
insuredName |
Name of the insured |
insuredDomicile |
Insured's country/domicile |
insuredAddress / insuredPostcode |
Insured address fields |
brokerName |
Broker party name |
businessClassName |
Business class name (e.g. "Property") |
businessClassTypeCode |
Business class code (e.g. "PR") |
underwriterExternalId |
Underwriter's external user ID |
quotingUserExternalId |
Quoting user's external ID |
customerProfessionCode |
Customer profession code from the risk |
initialPricing |
Whether this is the first pricing pass |
readOnly |
Whether the pricing session is read-only |
products |
List of active products on the risk |
productTypeLegend |
Code → display name mapping for products |
attributeTypeLegend |
Code → display name mapping for asset attribute types |
customerExtension |
Optional client-specific extension object |
Layers (layers[]):
Each layer maps to a policy in Workbench terms.
| Field | Description |
|---|---|
id |
Workbench layer/policy ID |
layerName |
Layer name |
exportId |
Optional export reference |
policyType |
PRIMARY, LOSS_LIMIT, XOL, or TIV |
technicalPrice |
Sum of active section technical prices |
achievedPrice |
Sum of active section achieved prices |
machinePrice |
Sum of active section machine prices |
netRenewalPremium |
Net renewal premium (renewals) |
netAdjustedExpiringPremium |
Net adjusted expiring premium (renewals) |
commission |
Commission rate (as decimal, e.g. 0.05 = 5%) |
signedLine |
Signed line percentage |
attachment |
Attachment point |
limitOfIndemnity |
Limit of indemnity |
quotedDate |
Date the layer was quoted |
assetAllocation |
Asset IDs applicable to this layer (null = all assets, [] = no assets) |
Sections (layers[].sections[]):
Each section maps to a product_cover in Workbench.
| Field | Description |
|---|---|
id |
Workbench product cover ID |
productCode |
Product code (e.g. "PD", "BI") |
subType |
Sub-class 1 code |
subType2 |
Sub-class 2 / contract type code |
status |
ACTIVE, INACTIVE, or DELETED |
technicalPrice |
Section-level technical price |
achievedPrice |
Section-level achieved price |
machinePrice |
Section-level machine price |
retentions[] |
Retention entries: retentionTypeCode, retentionValue, rowStatus |
limits[] |
Limit entries: limitTypeCode, limitValue, rowStatus |
currency |
Section currency (if multi-currency) |
exchangeRateToBaseCurrency |
Exchange rate if applicable |
signedLine |
Section-level signed line |
section |
Section reference |
Assets (assets[]):
| Field | Description |
|---|---|
id |
Workbench asset ID |
organisationName |
Asset organisation name |
address / postcode / country |
Asset location |
attributes[] |
Coverage values: attributeTypeCode, value, originalValue, name |
conflictOnOutOfSequenceMTA |
Whether this asset has an MTA conflict |
Push (POST) — what Hx sends back to Workbench¶
POST https://api.{client-env}.sendworkbench.com/policies/hx/mapped/{policyGroupId}/structured-quote
The push payload wraps the (modified) pull payload in a summary envelope:
{
"quoteExportId": "111",
"machinePrice": 251.0,
"technicalPrice": 250.0,
"achievedPrice": 252.0,
"netRenewalPremium": 0,
"netAdjustedExpiringPremium": 0,
"payload": { <<same structure as GET, with pricing populated>> },
"pricingEngineSpecificData": {}
}
Summary envelope fields:
| Field | Description |
|---|---|
quoteExportId |
Optional ID allocated within Hx |
achievedPrice |
Overall achieved price — must equal the sum of active layer achieved prices |
technicalPrice |
Overall technical price — must equal the sum of active layer technical prices |
machinePrice |
Overall machine price — must equal the sum of active layer machine prices |
netRenewalPremium |
Net renewal premium at the option level |
netAdjustedExpiringPremium |
Net adjusted expiring premium at the option level |
payload |
The GET structure, augmented with Hx-calculated pricing values |
pricingEngineSpecificData |
Used to pass back updated Hx/Workbench policy ID mappings if they changed in Hx (e.g. if the user worked on a different policy option than the one Workbench originally created) |
What Hx populates on the push:
- technicalPrice, achievedPrice, machinePrice at both layer and section level
- retentions and limits — Hx may modify these values
- currency — can be updated via the push
- customerProfessionCode — can be updated via the push
- inceptionDate / expiryDate — editable on new business and renewals, but not on MTAs
Warning
Prices at layer level must always equal the sum of prices across active sections on that layer. Similarly, prices in the top-level envelope must equal the sum across active layers. Hx is responsible for populating these correctly — but if the Hx model doesn't do this, Workbench will store inconsistent premium values.
be_config.json configuration¶
All Hx configuration lives in hxPricingConfig in be_config.json.
Minimum required configuration¶
{
"hxPricingConfig": {
"authScheme": "AZURE_AD",
"authenticationURL": "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token",
"grantType": "client_credentials",
"clientId": "${HX_CLIENT_ID}",
"clientSecret": "${HX_CLIENT_SECRET}",
"scope": "api://{hx-app-id}/.default",
"apiURL": "https://api.hyperexponential.com",
"role": "underwriter",
"pricingStrategy": "INCEPTION_DATE",
"onlyAllowPublishedModels": true
}
}
Warning
Credentials must be injected via environment variables or AWS Secrets Manager. Never commit them as plain text.
Authentication options¶
Azure AD (most common):
{
"authScheme": "AZURE_AD",
"authenticationURL": "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token",
"grantType": "client_credentials",
"clientId": "${HX_CLIENT_ID}",
"clientSecret": "${HX_CLIENT_SECRET}",
"scope": "api://{hx-app-id}/.default"
}
Auth0:
{
"authScheme": "AUTH0",
"authenticationURL": "https://{your-domain}.auth0.com/oauth/token",
"clientId": "${HX_CLIENT_ID}",
"clientSecret": "${HX_CLIENT_SECRET}",
"audience": "https://api.hyperexponential.com",
"userName": "${HX_USERNAME}",
"password": "${HX_PASSWORD}"
}
Confirm the scheme with your Hx account team — the scheme configured here must match what the Hx tenant is set up to accept.
Full configuration reference¶
| Field | Default | Description |
|---|---|---|
apiURL |
— | Base URL for the Hx API |
role |
— | The Hx role under which policies are created (e.g. "underwriter") |
policyCreatedAsLive |
false |
If true, policies are created as live immediately. Keep false — policies should only go live at bind |
sendCustomerId |
true |
Sends the Workbench policy group ID to Hx as customerId. Keep true |
pricingStrategy |
INCEPTION_DATE |
Model version selection strategy — see Renewal flow |
onlyAllowPublishedModels |
true |
Prevents rating against unpublished/draft Hx models. Set false in Dev only |
onlyAllowTestableModelVersions |
false |
Restricts to testable model versions only. Leave false unless specifically required |
forceQuotesOnSameModelToUseSameModelVersion |
false |
Forces all quote options on a risk to use the same Hx model version. Enable when multi-option is active and consistency is required |
importExpiringPolicyData |
true |
Auto-imports expiring policy data into Hx on renewal. Recommended: true |
shouldSetExpiringPolicyReferenceOnRenewals |
false |
Populates the expiring policy reference in Hx on renewals. Enable if the Hx model uses this field |
shouldReuseExpiringPolicyReferenceOnCopiedPolicies |
false |
Reuses expiring policy reference on copied (not renewed) risks |
priceSynchronizationEnforced |
false |
Blocks quote completion if AP and TP are not in sync between Hx and Workbench |
additionalPricingDetailRequired |
false |
Requires additional customer data before the rating action is available |
setRaterStatusOnUpdate |
true |
Updates the rater status field on the risk when Hx sends data back |
validationErrorIgnoredOnStatusUpdate |
false |
Suppresses Hx validation errors during status updates. Only enable if advised by Send |
enableMultiOption |
false |
Enables multi-option quoting — multiple Hx policy options per risk |
connectionTimeoutInSeconds |
15 |
HTTP connection timeout for Hx API calls |
readTimeoutInSeconds |
15 |
HTTP read timeout for Hx API calls |
hxSessionTimeoutInSeconds |
15 |
How long an Hx browser session is considered active |
statusUpdateThreadPoolSize |
20 |
Thread pool size for Hx status sync. Increase for high-volume environments (e.g. DUA) |
statusUpdateTimeoutInSeconds |
120 |
Maximum wait time for a status update response from Hx |
Teams configuration¶
teamsProviderConfig controls which Hx team newly created policies are assigned to. Set to null if team assignment is not needed.
Assign all policies to a single team:
Prefix team name with business class (e.g. "UW-Marine", "UW-Property"):
Explicit business class → team mapping:
{
"teamsProviderConfig": {
"type": "BUSINESS_CLASS_NAME_MAP",
"businessClassNameToTeamMap": {
"Marine Cargo": "Marine Team",
"Property": "Property & Casualty",
"Liability": "Property & Casualty"
}
}
}
If a business class has no entry in the map, the business class name itself is used as the team name.
Product cover matching strategy¶
productCoverMatchingStrategy controls how Workbench matches sections in the Hx push response back to product covers in its own data model.
| Value | When to use |
|---|---|
PRICING_ENGINE_ID |
Default and most common. Matches sections using their Hx section ID and pricing engine ID. Each section must have at least one of id or pricingEngineId |
PRODUCT_HIERARCHY |
Matches using the combination of productCode, subType, and subType2. Use when Hx model sections map directly to Workbench product hierarchy nodes and the combination of these three values is unique per section |
Async tasks¶
Some Hx operations are asynchronous. There are two triggering strategies:
Legacy strategy — tasks defined as a list, fired after policy creation:
{
"useNewHxAsyncTaskTriggering": false,
"postPolicyCreationAsyncTask": ["my_async_task"],
"asyncStatusUpdateEnabled": false,
"updatePoliciesStatusesTaskName": "workbench_sync_status",
"asyncTaskPollerMaxAttempts": 3,
"asyncTaskPollerBackOffPeriod": 2000
}
New strategy — tasks mapped to named lifecycle trigger points (recommended for new implementations):
{
"useNewHxAsyncTaskTriggering": true,
"asyncTaskTriggerPoints": {
"policyCreation": "hx_policy_created",
"quoteCompletion": "hx_quote_completed",
"brokerResponseCompletion": "hx_broker_response_completed"
},
"asyncTaskPollerMaxAttempts": 3,
"asyncTaskPollerBackOffPeriod": 2000
}
With the new strategy, Hx handles all internal task orchestration — you only configure one trigger name per lifecycle step. Post-creation async tasks are fire-and-forget: if the task fails to start, the error is surfaced; if the task itself fails internally, it won't be reported back to the user.
Pricing screen configuration¶
The Hx pricing screen in Workbench is configured via screen metadata files in the client config repository:
This file defines which placing types and risk types use the Hx pricing screen:
[
{
"placingType": "OPEN_MARKET",
"riskType": "NEW",
"operationName": "structured_policies_hx_pricing",
"dataFile": "structured-policies-hx-pricing-meta-data.json"
},
{
"placingType": "OPEN_MARKET",
"riskType": "RENEWAL",
"operationName": "structured_policies_hx_pricing",
"dataFile": "structured-policies-hx-pricing-meta-data.json"
},
{
"placingType": "OPEN_MARKET",
"riskType": "MTA",
"operationName": "structured_policies_hx_pricing",
"dataFile": "structured-policies-hx-pricing-meta-data.json"
}
]
The dataFile defines the layout and field definitions for the pricing panel. The reference implementation is at:
The screen is split into four groups:
| Group | Purpose |
|---|---|
policyGroup |
Quote name, description, rater status |
policy |
Layer-level details: dates, currency, commission rate |
productCoverSummary |
Section mapping: line of business, product, subType, contract type |
productCoverDetail |
Premium fields: technical price, achieved price, stamps, retentions, limits |
Common issues¶
Quote is blocked from completing
Check priceSynchronizationEnforced — if true, the achieved price and technical price must match between Hx and Workbench before completion is allowed. The underwriter must reconcile any discrepancy in Hx first.
Renewal data not prefilling in Hx
Check importExpiringPolicyData — should be true. Also verify that the expiring Hx policy ID has been stored on the previous risk in Workbench (identifiers: HX_POLICY_ID, HX_POLICY_OPTION_ID). If the expiring policy was priced in a structurally different Hx model, set importExpiringPolicyData: false to avoid import errors.
Validation error blocking finalisation
The underwriter needs to resolve the validation errors within the Hx UI. The error message will reference the specific Hx policy option ID. Only enable validationErrorIgnoredOnStatusUpdate: true as a last resort and with Send's guidance.
Session conflict error Hx prevents concurrent edits. If the user has the policy open in Hx in another browser tab or session, they must close it before completing actions in Workbench.
Prices not writing back correctly Verify that layer-level prices equal the sum of section prices, and that the top-level envelope prices equal the sum across layers. This is Hx's responsibility on the push — if the Hx model is calculating summations incorrectly, it needs to be fixed in Hx.
Wrong model version being used
Review pricingStrategy and confirm the correct inception date is set on the risk. For renewals, INCEPTION_DATE should use the renewal's inception date (not the expiring policy's). If forceQuotesOnSameModelToUseSameModelVersion is enabled, all options will lock to the first model version found — check that the first quote option was priced against the intended version.
See also¶
- Rating Service overview — Excel integration and general rating concepts
- Actions — wiring quote and complete-quote actions into the risk workflow
- Feature Switches — enabling the rating feature switch