Solved Enroll is in private beta ahead of a 2027 public rollout. Join the waitlist
Resources REST JSON MCP

API reference

A REST API over HTTPS with bearer-token auth, JSON bodies, cursor pagination, and idempotency keys on writes. The same engine also answers as an MCP server.

Conventions

These hold across every endpoint, so the resource groups below only describe what is specific to them.

Solved Enroll is in private beta. This page documents the shape of the API ahead of the 2027 public rollout. Credentials are issued through cohort onboarding or a licensing conversation, not self-service, and exact schemas ship with them. Join the waitlist or ask about licensing.

Request and response conventions
Base URL https://api.solvedenroll.com/v1. HTTPS only. Requests over plain HTTP are refused rather than redirected.
Authentication A bearer token on every request: Authorization: Bearer <token>. Tokens are scoped to one account and one integration, and inherit the permissions and hierarchy scope of the account they belong to.
Content type application/json on requests with a body, and on every response. Bodies are UTF-8.
Versioning The version is in the path, currently v1. Additive changes ship inside a version; anything that would break a client gets a new one.
Pagination Cursor-based on every list endpoint. Pass limit up to 100 and cursor; read next_cursor from the response and pass it back. A null next_cursor means you have reached the end. Offsets are not supported, because a book of business changes under you while you page through it.
Idempotency Writes accept an Idempotency-Key header. Replaying the same key with the same body inside 24 hours returns the original response instead of creating a second record, which is what you want when a timeout leaves you unsure whether a submission landed.
Filtering List endpoints accept changed_since as an RFC 3339 timestamp, plus resource-specific filters such as agent, agency node, state, product type, and external_ref.
External references Clients, quotes, and applications accept an external_ref you control, so you can reconcile against the keys your own system already trusts.
Identifiers Opaque prefixed strings, for example cli_8Fq2mR, qte_51Bn7c, app_0Kd3Vx. Treat them as strings; do not parse them.
Timestamps RFC 3339 in UTC, for example 2027-10-15T14:32:08Z. Dates without a time, such as a date of birth or an effective date, are YYYY-MM-DD.
Rate limits 600 requests per minute per account by default, reported on every response. See rate limiting.

Rank the plans a client can actually take, in one call.

Post a client and a line of business. Get back ordered plans with a score, the factors behind each score, and the rule set version the ranking ran against. Values below are illustrative.

curl -X POST https://api.solvedenroll.com/v1/recommendations/rank \
  -H "Authorization: Bearer $SOLVED_ENROLL_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8f14e45f-ea2b-4c1e-9d6a" \
  -d '{
      "client_id": "cli_8Fq2mR",
      "line": "medicare",
      "product_types": ["mapd", "pdp"],
      "limit": 3
    }'

{
  "id": "rec_3Wd9Lp",
  "client_id": "cli_8Fq2mR",
  "line": "medicare",
  "plan_year": 2027,
  "ruleset_version": "2027.04",
  "results": [{
    "rank": 1,
    "score": 92,
    "plan_id": "pln_4ac81b",
    "product_type": "mapd",
    "factors": [
      {"name": "drug_cost", "effect": 31},
      {"name": "provider_match", "effect": 18},
      {"name": "premium", "effect": -4}
    ]
  }],
  "excluded_count": 26,
  "next_cursor": null
}

See the same case worked through from intake

Endpoints

Paths are relative to https://api.solvedenroll.com/v1.

Clients

One client record carries every line of business. Everything else on this page hangs off a client.

Clients endpoints
POST
/clients
Create a client record: name, date of birth, contact details, address and county, and your external_ref. County matters, because Medicare service areas are county-level. Accepts an idempotency key.
GET
/clients/{id}
Fetch one client with the health profile summary, open cases, quotes, applications, and policies recorded in force.
PATCH
/clients/{id}
Update contact details, address, or external_ref. Health data is written through the health profile endpoints instead, so that every change to a medication or condition list lands in the audit trail as a health event.
GET
/clients
List clients with cursor pagination. Filter by agent, agency node, state, changed_since, or external_ref. Scope follows the token: a token belonging to an agent sees that agent's book, a token belonging to a principal sees the downline.
POST
/clients/{id}/merge
Merge a duplicate into a surviving record. Health profiles, quotes, applications, and documents move across, conflicting fields are reported rather than silently dropped, and the merge is written to the audit trail with attribution.

Health profiles

The profile that drives life underwriting and drug pricing. Free-text strings in, canonical terms out.

Health profiles endpoints
POST
/clients/{id}/health-profile
Create or replace the profile: height, weight, tobacco use, and the medication and condition lists. Returns the profile with resolved terms and the carrier-relevant flags derived from it.
POST
/clients/{id}/health-profile/medications
Add a medication from a free-text string. The response returns the resolved term with strength and form, or a candidate list when the string is genuinely ambiguous, so your interface can ask rather than guess.
POST
/clients/{id}/health-profile/conditions
Add a condition with onset date, treatment, and control status. Follow-up requirements are returned with it, because what a carrier needs next depends on what you just recorded.
POST
/terms/resolve
Resolve a free-text medication or condition string to a canonical term without writing anything to a profile. Handles abbreviations, brand and generic names, and misspellings. Useful for a type-ahead in your own interface.
GET
/clients/{id}/health-profile
Fetch the current profile, its resolved terms, and the derived flags, including which carriers the profile currently knocks out and why.

Quotes

One endpoint per line of business, because the inputs genuinely differ. All three return the same envelope.

Quotes endpoints
POST
/quotes/medicare
Create a Medicare quote. Accepts the client, product types (mapd, pdp, medsupp, snp), county, effective date, the pharmacy the client actually uses, and any named providers. Eligibility, service area, formulary, and network are applied before plans are returned.
POST
/quotes/life
Create a life quote. Accepts product types (term, final_expense, whole_life, preneed), a face amount or a target monthly premium, and term length. Products the health profile disqualifies do not come back as options.
POST
/quotes/ancillary
Create an ancillary quote for dental, vision, hearing, hospital indemnity, accident, or critical illness, standalone or alongside a medical plan on the same client.
GET
/quotes/{id}
Fetch a quote with the plans returned, priced drug costs by coverage phase, provider network matches, and the excluded list with a reason on every exclusion. The excluded list is the part most integrations end up relying on.
GET
/quotes
List quotes with cursor pagination. Filter by client, agent, line, product type, or changed_since.

Recommendations

The AI Plan Recommender. Ranked, scored, explainable, and deterministic, so a case can be reconstructed later.

Recommendations endpoints
POST
/recommendations/rank
Rank the eligible plans for a client and a line of business. Returns ordered results with a score and the factors that lifted or lowered each one, plus the rule set version and plan year the ranking ran against.
POST
/recommendations/explain
Return the full factor breakdown for one plan against one profile, including what would have to change for it to move up. This is the call to make when a client asks why the second option is not the first.
GET
/recommendations/{id}
Fetch a stored recommendation exactly as it was produced, with the weighting and rule set version in effect at the time. Re-running is not the same as retrieving, and an audit wants the original.

Plans

Plan, formulary, and provider data on its own, for systems that want the reference data rather than a quote.

Plans endpoints
GET
/plans/search
Search plans by county, plan year, product type, and carrier. Both the current and the upcoming plan year are searchable once next year's data has loaded.
GET
/plans/{id}
Fetch one plan with benefits, premium, service area, cost sharing, and the published star rating where CMS publishes one.
GET
/plans/{id}/formulary
Formulary lookup for a drug list: tier placement, prior authorization, step therapy, and quantity limits, per drug.
GET
/plans/{id}/providers
Provider lookup: network status for named doctors, facilities, or pharmacies, including whether a pharmacy is preferred or standard.

Applications

From a chosen plan to a signed, submitted, acknowledged application, without retyping anything.

Applications endpoints
POST
/applications
Create an application from a quote and a chosen plan. Returns the application with the carrier form version it will be written on.
POST
/applications/{id}/prefill
Populate carrier form fields from the client record, health profile, and quote. Returns the fields still required, so your interface can ask for exactly those and nothing else.
POST
/applications/{id}/send
Send for signature. delivery is sms, tablet, or email. Sending again after a failed delivery reuses the same packet rather than building a new one.
GET
/applications/{id}/status
Signature and submission state, the carrier reference, any requirements the carrier has raised, and the final disposition. The same transitions arrive as webhooks if you would rather not poll.
GET
/applications
List applications with cursor pagination. Filter by client, agent, carrier, state, status, or changed_since.

Documents

The case file and everything in it. This is the group a compliance team cares about.

Documents endpoints
GET
/documents/case-file/{case_id}
Generate the complete case file as one document: Scope of Appointment with its timestamp, disclosures presented and acknowledged, plans presented, plan chosen, the recommendation and its factors, the signed application, and the submission and acknowledgement record, in order.
GET
/documents
List documents on a client or a case, with type, size, and creation time.
POST
/documents/{id}/download-url
Issue a short-lived, single-use download URL. Documents are never served from a permanent public link, and issuing the URL is itself written to the audit trail.

Agencies

Structure, licensure, and volume. Read-only, because hierarchy changes belong in the portal where they are audited.

Agencies endpoints
GET
/agencies/{id}/hierarchy
The upline and downline structure as a tree, with the node identifiers the other endpoints filter on.
GET
/agencies/{id}/agents
Agents with their licenses, carrier appointments, and can-write status by carrier, product, and state. Check this before your system starts a case it cannot finish.
GET
/agencies/{id}/usage
Cases, quotes, recommendations, submissions, and issued policies by agent, product, carrier, and period, matching the numbers the agency dashboards show.

MCP

The same engine behind a Model Context Protocol endpoint, so an agent runtime can call it directly.

MCP endpoints
POST
/mcp (tools/list)
Tool discovery. Returns the available tools with their input schemas: resolve_term, check_eligibility, quote_medicare, quote_life, quote_ancillary, lookup_formulary, lookup_provider, rank_plans, and explain_plan.
POST
/mcp (tools/call)
Invoke one tool by name with its arguments. Results come back as structured content, not prose, so your model reads a ranking rather than parsing a paragraph. Authentication, permissions, hierarchy scope, rate limits, and audit logging are identical to the REST surface.

Webhooks

Eight event types, delivered as signed POSTs to your endpoint within seconds of the event, and retried with backoff for up to 24 hours.

client.created

A client record was created, by an agent in the portal or by an API call from your system.

quote.completed

A quote finished. The payload carries the quote identifier, the line, the plans returned, and the excluded count.

recommendation.ready

A ranking is available, with the top results and the rule set version it ran against.

application.sent

A signature packet was sent, with the delivery method used.

application.signed

The client completed signing. Signature metadata is on the case; the document is fetched through the documents endpoints.

application.submitted

The application was transmitted to the carrier.

application.acknowledged

The carrier acknowledged receipt, or raised a requirement. This is the event most agency management systems key their pipeline off.

soa.captured

A Scope of Appointment was captured and timestamped for a client, with the products it covers.

Delivery behavior

  • Signed and timestamped. Verify the signature against the raw request bytes before parsing, and reject a timestamp outside your tolerance so a captured delivery cannot be replayed at you.
  • At least once. Respond 2xx quickly and process asynchronously. Key on the event identifier, because a retry after a slow response will deliver the same event twice.
  • Retried with backoff. Failed deliveries retry on an increasing interval for up to 24 hours, then stop. Anything missed after that is recoverable with a changed_since list call.
  • Thin payloads. Events carry identifiers and the fields you need to route on, not whole documents. Fetch the rest, so protected health information is not sitting in your delivery logs.

POST /your-endpoint
Solved-Enroll-Event: recommendation.ready
Solved-Enroll-Timestamp: 1791040328
Solved-Enroll-Signature: v1=3a7f9c...

{
  "id": "evt_7Hs2Qa",
  "type": "recommendation.ready",
  "created": "2027-10-15T14:32:08Z",
  "data": {
    "recommendation_id": "rec_3Wd9Lp",
    "client_id": "cli_8Fq2mR",
    "external_ref": "crm-40219",
    "line": "medicare",
    "top_plan_id": "pln_4ac81b"
  }
}

Rate limiting

Limits are per account, not per token, so issuing a token per integration does not buy extra headroom. It does make it obvious which integration is spending the budget.

  • 600 requests per minute by default. Every response reports the limit, the remaining allowance, and when the window resets.
  • 429 means back off. Read Retry-After and honor it. Retrying immediately on a 429 is the fastest way to stay rate limited.
  • Quoting and ranking cost more. A Medicare quote prices a drug list across formularies, so quote and rank calls draw on a separate, smaller concurrency allowance than plain reads.
  • Bulk work has its own path. Do not page a list endpoint to move a whole book. Use a scheduled export or a warehouse extract, which are built for volume and do not compete with live agents for the limit.
  • Higher limits on request. Tell us the integration and the pattern, and the limit is raised where it makes sense.

Defaults

600 / min
Requests per account, reported on every response.
100
Maximum records per page on a list endpoint.
24 hours
Window in which an idempotency key replays the original response.
24 hours
Webhook retry window before delivery stops.

Error format

Standard HTTP status codes, plus one error object shape everywhere so you write the handler once.

HTTP/1.1 422 Unprocessable Entity

{
  "error": {
    "type": "eligibility_error",
    "code": "election_period_not_open",
    "message": "The client has no election period open on the requested effective date.",
    "param": "effective_date",
    "detail": {
      "effective_date": "2027-03-01",
      "next_open_period": "AEP",
      "next_open_date": "2027-10-15"
    },
    "request_id": "req_Pd81Zk"
  }
}

type is broad enough to switch on, code is stable and specific, message is safe to log but not written for a client to read, and request_id is the thing to quote when you email us. Validation failures list every offending field rather than stopping at the first.

HTTP status codes
200 OK The request succeeded and the body is the resource or the list.
201 Created A write created a resource. The body is the new resource, and Location points at it.
202 Accepted Work was queued: a case file generation, a warehouse export, or a carrier submission that the carrier answers asynchronously. Poll the resource or wait for the webhook.
400 Bad Request The body was not valid JSON, or a required parameter is missing.
401 Unauthorized No token, a malformed token, or a revoked token.
403 Forbidden The token is valid but the account cannot reach that record. Hierarchy scope and appointment-based permissions both produce this.
404 Not Found No such resource, or one outside the token's scope. We do not distinguish the two, because doing so leaks whether a record exists.
409 Conflict An idempotency key was replayed with a different body, or a merge target is already merged.
422 Unprocessable Entity The request was well formed but cannot be acted on: an election period that does not permit the plan, a county outside the service area, or a profile the carrier knocks out. The error detail names the rule.
429 Too Many Requests Rate limited. Read Retry-After and back off.
5xx Our problem. Writes are safe to retry with the same idempotency key.

Access and licensing

There are two reasons to want a token, and they are separate conversations.

  • Platform access. If your agency is using Solved Enroll, API tokens are issued as part of cohort onboarding. Ask during onboarding and you get a token scoped to your agency with the same hierarchy rules your users have.
  • Licensing the recommender. If you want ranking inside your own product without your users touching Solved Enroll, the AI Plan Recommender licenses separately as a REST endpoint or an MCP server. That is available before general availability. See licensing on the AI Plan Recommender page.
  • What ships with credentials. Field-level schemas, enumerations, sandbox data, the webhook signing secret, and a contact who has read your integration plan. This page is the shape; those are the details.
  • No self-service signup. Tokens reach protected health information and Medicare beneficiary data, so they are issued to a named agency or licensee after a conversation. See security and privacy for how credentials are handled.
Ranked plan cards glowing above a laptop

FAQs

API questions

Can I get credentials today?

Not self-service. Solved Enroll is in private beta, so tokens are issued through cohort onboarding for platform access, or through a licensing conversation if you want the AI Plan Recommender on its own. There is no signup form that hands you a key.

Is this documentation or a specification?

It documents the shape of the API: the resource groups, the endpoints, the conventions, the event types, and the error format. It is written so you can plan an integration and ask precise questions. Exact field lists, enumerations, and schemas come with your credentials, because they are still moving while cohorts are onboarding.

Can I license just the recommender?

Yes. The ranking engine is available as a REST endpoint and as an MCP server without platform access, so a CRM, a dialer, or an agent runtime can rank plans without rebuilding Medicare and underwriting logic. See the licensing section on the AI Plan Recommender page.

How do I avoid double-creating records when my job retries?

Send an Idempotency-Key header on writes. Replaying the same key with the same body inside 24 hours returns the original response rather than creating a second record. Replaying the same key with a different body returns 409, which is a bug in your job rather than a problem with the record.

What do the webhook signatures look like?

Each delivery carries a timestamp and a signature computed over the timestamp and the raw body with your endpoint secret. Verify the signature against the raw bytes before parsing, and reject deliveries whose timestamp is outside your tolerance window so a captured request cannot be replayed at you later.

Does a token see everything in my agency?

It sees exactly what its owning account sees. A token belonging to an agent reads that agent's book; a token belonging to a principal reads the downline. Hierarchy scope and appointment-based permissions are enforced on the API, not just in the interface, and every read is attributed to the token in the audit trail.

Something else? Contact us

Planning an integration?

Send us what you are building and which direction the data moves. We will tell you which endpoints and events you need before you write anything.