Skip to main content
This guide covers what the API reference doesn’t: how to structure your provider offerings for Nexio and interpret the results. For request/response schemas, see Submit Run and Get Run Status.

Flow

Persist run_id. Polling remains the canonical reconciliation path even when using webhooks.

What you send

{
  "input": { ... },
  "offerings": [ ... ]
}
input is the subject’s profile and run context. offerings is one entry per provider per requirement category.
input.current_coverage describes existing state (e.g., incumbent products or services). It provides context for scoring but does not replace the offerings array.

Building offerings from your data

Each provider entry becomes one offering per requirement category. If you have 3 providers each covering 3 categories, that’s 9 offerings (3 providers x 3 categories). Keep offerings[*].category aligned with input.coverage_types. Extra offerings for categories not in coverage_types are ignored.

Multi-provider input

When comparing packages from multiple providers, submit offerings from each. The engine evaluates all valid combinations — both single-provider bundles and mixed-provider packages:
Example: Insurance — Personal Lines. The input and offerings schema depends on your engine’s configuration. This example shows a personal-lines insurance placement engine.
{
  "input": {
    "address": { "state": "NY", "zip_code": "11231" },
    "coverage_types": ["home", "auto", "umbrella"],
    "appetite_bucket": "balanced",
    "vehicles": 1,
    "premium": 3557.23
  },
  "offerings": [
    {
      "id": "quote_line_carrier_a_home_001",
      "provider_id": "prov_carrier_a",
      "provider_name": "Carrier A",
      "category": "home",
      "quality_rating": "A+",
      "pricing_tier": "premium",
      "commission": 0.14,
      "constraints": { "excluded_states": [], "prohibited_naics": [], "avoided_naics": [] },
      "coverage": { "product_name": "Select Homeowners", "program_type": "quoted_line" },
      "attributes": { "quote_id": "qt_carrier_a_home_001", "line_premium_annual": 6120, "package_premium_annual": 12840 }
    },
    {
      "id": "quote_line_carrier_b_home_001",
      "provider_id": "prov_carrier_b",
      "provider_name": "Carrier B",
      "category": "home",
      "quality_rating": "A",
      "pricing_tier": "economy",
      "commission": 0.12,
      "constraints": { "excluded_states": [], "prohibited_naics": [], "avoided_naics": [] },
      "coverage": { "product_name": "Select Home", "program_type": "quoted_line" },
      "attributes": { "quote_id": "qt_carrier_b_home_001", "line_premium_annual": 5050, "package_premium_annual": 10950 }
    }
  ]
}
Results may include an all-Carrier A bundle, an all-Carrier B bundle, and mixed-provider combinations (e.g. Carrier A home + Carrier C auto) — all ranked by composite scorecard.

Polling

Poll GET /api/v1/runs/{run_id} until terminal. Start at 2s delay, backoff 1.5x, cap at 30s.
StatusAction
queuedKeep polling
processingKeep polling
completedConsume solutions
failedRead error, stop
import time, httpx

def wait_for_result(api_key: str, run_id: str) -> dict:
    headers = {"Authorization": f"Bearer {api_key}"}
    delay = 2
    while True:
        data = httpx.get(
            f"https://api.usenexio.com/api/v1/runs/{run_id}",
            headers=headers,
        ).json()
        if data["status"] in ("completed", "failed"):
            return data
        time.sleep(delay)
        delay = min(delay * 1.5, 30)

Reading results

Key fields on a completed response:
FieldPurpose
solutions[*].rankRank order (1 = best)
solutions[*].cluster_labelSolution label: recommended, best_value, best_coverage, simplest
solutions[*].scorecard.overall_levelPackage score (lower is better)
solutions[*].provider_count1 = single provider, 2+ = mixed
solutions[*].est_cost_low / est_cost_highAnnual cost range
solutions[*].offerings[*].provider_nameProvider per category
solutions[*].offerings[*].categoryRequirement category (domain-specific, e.g. home, auto)
There is no solutions[*].carrier_name — use offerings[*].provider_name. There is no solutions[*].score — use scorecard.overall_level.

Failure cases

completed with 0 solutionsoutput.diagnostic explains why. Common causes: coverage_types / offerings[*].category mismatch, missing required categories, or all offerings filtered out. This is a valid terminal state, not an error. failed — read error and error_details. Terminal. Stuck in processing — the EVALUATE stage can take time. Use backoff, don’t tight-loop.

Integration checklist

  1. offerings generated per category per provider, not per package
  2. offerings[*].category aligns with input.coverage_types
  3. Polling handles all four status values
  4. Package score read from scorecard.overall_level
  5. Provider identity read from offerings[*].provider_name
  6. Both request and terminal response persisted

Example artifacts

  • Canonical request — complete POST /api/v1/engines/default/runs body with input and offerings (insurance — personal lines)
  • Canonical response — complete GET /api/v1/runs/{run_id} response for a completed run (insurance — personal lines)