Rating API ยท v1
ISO & AAIS rating API
Rate a commercial risk against every program you are entitled to in one request, and get back the premium, the worksheet and the provenance behind each number, then carry the policy through its term: bind, endorsement, cancellation, reinstatement and renewal. One synchronous JSON API over the same rating engine your underwriters use.
What the API is for
Most rating platforms treat the API as an export. This one treats it as the same product reached a different way.
The Rating API adds no separate rating logic. A quote rated through it resolves the same effective-dated program configuration and runs the same engine as the same quote rated in Underwriter Workbench. Identical inputs, program and effective date produce an identical premium and an identical worksheet. The two channels cannot drift, because there is nothing to drift.
That matters because of what an API is usually for here: handing your program to people who are not your staff. A quoting portal, a sub-producer's system, an agency management system. If the API were a second rating path, every one of those would be a place where the wrong number could be produced and defended.
What you can build with it
Quote in your own portal
Post a risk, get a premium and a worksheet link back synchronously. No callbacks to wait for.
Shop a risk across your panel
Leave the program code out and one request rates the risk against every program you are entitled to for that product, state and effective date. Each program is an independent result, so one can rate while another refers or declines.
Give partners bounded access
Entitlement is granted per program, per API client. Scopes gate operations, grants gate programs, and there is no wildcard at request time.
Service the policy from your own system
Endorse, cancel, reinstate and renew through the same API, with a calculate preview before anything is confirmed.
Find any policy
Search policy terms by number, insured, status, carrier, program, line and dates, and read each term's transaction ledger.
Prove a premium later
Every rating run is retrievable and byte-identical, worksheet included.
What it deliberately does not do
Stated here rather than discovered during implementation. Not in this release: short-rate or minimum-earned cancellation (cancellation is pro rata, with an audited factor override), reinstatement after a lapse, rewrite or extension transactions, TRIA, webhooks, asynchronous or batch rating, and a PDF worksheet endpoint (worksheets are JSON). SelectRate is not a policy administration system.
SelectRate rates and records policy transactions. It holds the bound policy term, its current rated state and a ledger of every transaction, each with its calculation and worksheet. It is not a policy administration or accounting system: billing, invoicing, commissions and accounting stay in the systems you already run. Policy documents are produced by SelectDocs, a separate product, from the same policy data.
The shape to hold in your head
A quote is a container for a risk at an effective date. It has many rating runs. A rating run has one result per entitled program. Each result has one worksheet and one immutable execution behind it. The newest run is the current one, and every older run stays retrievable.
A bound quote with an expiration date is a policy term. The term holds its current accepted state and a transaction ledger in sequence: the bind first, then every endorsement, cancellation, reinstatement and renewal.
There is one temporal input in the whole contract: effectiveDate on the quote. It resolves the bureau edition, the base version and any carrier or program override in force for that date. You cannot pin a version, and no version identifiers are accepted anywhere in the public contract. That is what guarantees a quote for a given date resolves the configuration that was genuinely in force, and it is why a premium can be defended years later.
HTTP status describes the call. Result status describes the insurance outcome. A response of nothing but declines is still a 200.
Overview
The Rating API is a channel onto the SelectRate rating platform. It adds no separate rating logic: a quote rated through this API resolves the same effective-dated program configuration and runs the same engine as the same quote rated in Underwriter Workbench. Identical inputs, program and effective date produce an identical premium and an identical worksheet.
All rating is synchronous. You post a quote, request a rating, and the response carries every program result. There are no callbacks to wait for. The same API records the bind as a policy term and rates every later transaction on it.
| Base URL | |
|---|---|
https://api.selectrate.ai | Production |
https://sandbox-api.selectrate.ai | Sandbox |
All paths below are relative to /api/rating/v1. Requests and responses are
JSON; send Content-Type: application/json. Dates are YYYY-MM-DD,
money is decimal, and identifiers are GUIDs.
Authentication
OAuth 2.0 client credentials. Each API client has a Client ID and a Client Secret. A Program Admin creates the sandbox credential on the Program Studio API screen; the secret is shown once and can be rotated or revoked on the same screen. Exchange the credential for a short-lived bearer token, then send it on every request.
POST https://sandbox-api.selectrate.ai/api/rating/v1/token
Content-Type: application/json
{ "client_id": "sbx-yourorg", "client_secret": "<secret>" }
{
"access_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "rating.quote rating.read worksheet.read"
}
The token endpoint also accepts application/x-www-form-urlencoded with
grant_type=client_credentials. In production, tokens come from
your configured identity provider with the same scopes; the Program Studio API screen names
the issuer. Send the token as Authorization: Bearer <access_token> and cache
it until shortly before expiry rather than requesting one per call. Tokens and secrets are
never written to logs.
Scopes
| Scope | Allows |
|---|---|
rating.quote | Create, update, rate, select and bind quotes; endorsement, cancellation, reinstatement and renewal; accept and discard. |
rating.read | Discovery; quote, run, policy, transaction and calculation reads; cancellation and reinstatement calculate previews. |
worksheet.read | Retrieve rating worksheets. |
Scopes gate operations. Grants gate programs. Holding
rating.quote does not entitle you to any particular program. That is decided
by the grants below.
Tenant isolation
A client sees only its own quotes, rating runs, worksheets and policy terms. Another
client's resources are indistinguishable from ones that do not exist: the API answers
404, never a 403 that would reveal ownership.
Credential handling and the controls around rating are covered in the security and rating integrity overview.
Entitlements
Every API client is granted access to specific rating programs by the organization that owns them. Your client can rate against exactly the programs it has been granted, and no others. There is no wildcard at request time.
GET /programs returns the programs your client has been granted, with carrier,
jurisdictions and configuration status. If you reference a program you have not been granted,
the API returns 403 and does not disclose whether that program exists.
Core concepts
| Concept | What it is |
|---|---|
| Quote | A container for a risk at an effective date. Holds the current inputs and the whole rating history for that risk. |
| Revision | Increments when the rating inputs change through PATCH. Rating the same inputs twice does not create a revision. |
| Rating run | One rating request. Identified by ratingRunId. Produces one result per granted program. |
| Result | One program's outcome within a run: status, premium, execution id, worksheet link, provenance. |
| Execution | The immutable record behind a result. Its inputs, result and worksheet never change. |
| Policy term | Created when a selected quote is bound with an expiration date. Identified by policyTermId. Holds the term's status, current accepted state, current annualized premium and transaction ledger. |
| Transaction | One entry in a term's ledger: the bind, an endorsement, a cancellation, a reinstatement. Identified by transactionId. Accepted transactions never change. |
| Working copy | The quote an endorsement is made on (workingQuoteId), seeded from the term's current accepted state. |
The shape to hold in your head: a quote has many rating runs; a rating run has many results; each result has one worksheet. A bound quote becomes a policy term, and the term keeps a ledger of every transaction after it.
Endpoints
Discovery
Quotes and rating
Policy
Transactions
History
Request contract
Every quote is a common envelope plus a product-specific risk document.
POST /api/rating/v1/quotes
Authorization: Bearer <token>
Idempotency-Key: 9f2c41a8-...
{
"product": "GL",
"effectiveDate": "2026-10-01",
"expirationDate": "2027-10-01",
"insuredName": "Sample Insured LLC",
"externalReferences": { "quoteId": "CRM-000123", "accountId": "ACC-9", "policyId": null, "reference": null },
"programCode": null,
"risk": { }
}
Leave programCode null to rate against every program you are entitled to, or
set it to pin one granted program. jurisdiction is required for single-jurisdiction
AAIS products and Accounts Receivable; GL derives its states from the risk. External references
are kept as literal strings, and externalReferences.quoteId is unique per client.
ISO GL risk document
"risk": {
"state": "CA",
"eachOccurrenceLimit": 1000000,
"aggregateLimit": 2000000,
"medicalPaymentsLimit": 10000,
"deductibles": [ { "amount": 500, "coverageType": "BI", "basis": "PerOccurrence" } ],
"locations": [
{
"locationNumber": 1,
"zipCode": "90723",
"city": "Paramount",
"county": "Los Angeles",
"classifications": [
{ "classCode": "EXAMPLE-CLASS", "exposureAmount": 1000000 }
]
}
]
}
Territory is derived from the filed mapping and reported in the worksheet provenance. You do not send a territory. Use the location lookup to get the city and county for a ZIP code, and send them verbatim:
GET /api/platform/location/postal-code?state=CA&postalCode=90723
{ "postalCode": "90723", "state": "CA",
"matches": [ { "city": "Paramount", "county": "Los Angeles" } ], "otherStates": [] }
Schema-driven products
Call GET /products/{productCode}/schema for the risk-document contract. For GL
it is the typed policy schema; for AAIS products it is the filed field and value map, with
options and which fields are required. New lines of business appear through discovery when you
are granted them.
Response contract
POST /api/rating/v1/quotes/{quoteId}/ratings
{
"quoteId": "b1f0...",
"quoteNumber": "Q-10231",
"transactionType": "NewBusiness",
"revisionNumber": 2,
"ratingRunNumber": 1,
"ratingRunId": "7c41...",
"ratedAt": "2026-09-21T14:22:07Z",
"effectiveDate": "2026-10-01",
"status": "Completed",
"corrections": [],
"results": [
{
"programCode": "CARRIER-A-GL",
"programName": "Commercial GL - Standard",
"carrier": { "code": "CARRA", "name": "Carrier A" },
"status": "Rated",
"indicatedPremium": 1310.00,
"finalPremium": 1310.00,
"corrections": [],
"referrals": [],
"executionId": "e88a...",
"worksheet": { "available": true, "href": "/api/rating/v1/quotes/b1f0.../ratings/7c41.../results/e88a.../worksheet" },
"provenance": [ ... ]
},
{
"programCode": "CARRIER-B-GL",
"programName": "Commercial GL - Select",
"carrier": { "code": "CARRB", "name": "Carrier B" },
"status": "Referred",
"referrals": [ "Referral text, passed through verbatim from the rating engine." ],
"executionId": "e88b...",
...
}
]
}
Worksheets are not inlined. A worksheet can run to seventy-plus steps, and a fan-out across several programs would multiply that into every response. Each result carries availability and a link; fetch the full worksheet only when you need it.
Result statuses
Each result carries its own status, and a single run can mix them: one program rated,
another referred. Business outcomes such as referrals and corrections are 200
results, never errors.
| Status | Meaning |
|---|---|
Rated | A premium was produced. |
Referred | The program requires underwriter referral. See referrals. |
Declined | The risk is ineligible for this program. |
Treat status as an open set and handle values you do not recognise without failing. Correction and referral text is the rating engine's own wording, passed through unchanged, and it is written to be shown to an underwriter.
Errors
HTTP status describes the API call. Result status describes the insurance
outcome. Every error body has the same shape; integrate against the error
code, never the text.
{
"error": "TransactionOpen",
"detail": "Text describing the problem.",
"correlationId": "3f9a1c22-..."
}
| HTTP | Codes and meaning |
|---|---|
| 400 | InvalidRequest (contract or validation failure; detail names the field), IdempotencyKeyRequired, ExpirationDateRequired (bind without an expiration date). |
| 401 | Missing, invalid or expired token. |
| 403 | Not an active client of this environment, scope missing, or program not granted. Does not reveal whether the program exists. |
| 404 | NotFound: unknown, or another client's, quote, run, execution, external id, term or transaction. |
| 409 | IdempotencyConflict, IdempotencyInProgress, QuoteRevisionConflict, QuoteBound, QuoteFrozen, ConcurrentUpdate, ExecutionNotSelectable, SelectedExecutionRequired, ExternalQuoteIdConflict, PolicyNumberConflict, PolicyTermRequired, PolicyNotInForce, PolicyNotCancelled, TransactionOpen, TransactionNotComputed, TransactionClosed, TransactionDateOutsideTerm, TransactionDateBeforePrior, UnsupportedCalculationMethod, ReinstatementLapse, EndorsementProgramLocked, EndorsementTermLocked, RenewalRequiresBoundQuote, RenewalContinuityConflict, PriorTermAlreadyRenewed. |
Policy terms & ledger
Select a rated result, then bind it. Binding records the policy number and creates the
policy term: it starts InForce, with transaction 1 (NewBusiness) and the
rating versions locked at the term's effective date.
POST /api/rating/v1/quotes/{quoteId}/select { "executionId": "e88a..." }
POST /api/rating/v1/quotes/{quoteId}/bound { "externalPolicyId": "POL-2026-000123",
"expirationDate": "2027-10-01",
"boundAt": "2026-09-21T15:00:00Z" }
-> status Bound, policyTermId
GET /policies/{policyTermId} returns the term: status, dates, product, program,
carrier, insured, currentAnnualizedPremium, currentQuoteId (the quote
holding the current accepted state), lastTransaction, priorTermId and
renewalTermId. GET /policies searches your own terms by
policyNumber, insured (partial), status, carrier,
program, lob and effective and expiration date ranges, newest bound
first, paged.
GET /policies/{policyTermId}/transactions returns the ledger in sequence.
GET …/transactions/{transactionId} returns one transaction with its
calculation: term and unexpired days, default and applied factor, whether the factor was
overridden (with the reason, who and when), previous and resulting annualized premium, the
transaction premium, earned premium, and a components[] breakdown.
Endorsements
An endorsement starts from the term's current accepted state and is rated on the bound program with the versions locked at the term's effective date. The transaction date drives proration only.
POST /policies/{policyTermId}/endorsement
{ "transactionEffectiveDate": "2027-01-01", "reason": "Payroll increase",
"comment": null, "overrideFactor": null, "overrideReason": null }
-> transactionId, workingQuoteId
PATCH /quotes/{workingQuoteId} { "expectedRevision": 1, "risk": { ... } }
POST /quotes/{workingQuoteId}/ratings (bound program only)
POST /quotes/{workingQuoteId}/select { "executionId": "..." }
GET /policies/{policyTermId}/transactions/{transactionId} -> Computed, components
POST /policies/{policyTermId}/transactions/{transactionId}/accept (or /discard)
Each rating of the working copy recomputes the transaction per component: (revised annualized minus previous annualized) times the applied factor, rounded to the product's governed precision and summed. An override factor needs a reason. There is one open transaction per term at a time; accepting makes it immutable and moves the term to the revised state, and discarding leaves the policy unchanged.
Cancellation & reinstatement
Cancellation has a preview and a confirm with the same body. The preview (rating.read)
has no effect; the confirm (rating.quote) cancels the term. Nothing is re-rated.
POST /policies/{policyTermId}/cancellation/calculate
POST /policies/{policyTermId}/cancellation
{ "cancellationEffectiveDate": "2027-06-01", "reason": "Insured request",
"comment": null, "calculationMethod": "ProRata", "overrideFactor": null, "overrideReason": null }
The response carries the default factor (unexpired days over term days), the applied factor,
the return premium (negative), the earned premium and the components. Pro rata is the only
calculation method in this release; short rate and minimum earned are refused with
UnsupportedCalculationMethod, never approximated.
Reinstatement is the exact reversal of the cancellation, on the cancellation date:
POST /policies/{policyTermId}/reinstatement/calculate
POST /policies/{policyTermId}/reinstatement
{ "reinstatementEffectiveDate": "2027-06-01", "reason": "Payment received", "comment": null }
Any other date returns ReinstatementLapse. The term returns to InForce
with its previous annualized premium.
Renewals
A renewal is a new quote created from the term's current accepted state, after every
accepted endorsement, and verified against what was actually rated. A mismatch fails closed
with RenewalContinuityConflict.
POST /api/rating/v1/quotes/{termQuoteId}/renewal
Idempotency-Key: 4a17...
{ "effectiveDate": "2027-10-01", "expirationDate": "2028-10-01",
"externalReferences": { "quoteId": "CRM-000124" }, "insuredName": null, "risk": null }
Omit risk to copy the current accepted risk, or supply one to replace it. Then
rate, select and bind under the new policy number: the new term carries priorTermId,
and the prior term becomes Renewed.
| Copied forward | Never copied |
|---|---|
| Risk document Insured name Account and policy references Product and jurisdiction |
Results, premiums and worksheets Run and revision numbers Selection and bound status Quote number and external quote id Version pins |
Claims-made continuity (ISO GL). The coverage basis and retroactive date copy unchanged. Changing the retroactive date, or moving from claims-made to occurrence, on a renewal is refused. AAIS products copy the field and value map; if a new edition requires a field that was not captured, rating stops with a correction naming it.
Lifecycle statuses
| Object | Statuses |
|---|---|
| Quote | Draft → Rated → Selected → Bound. An endorsement working copy ends Accepted or Discarded. |
| Policy term | InForce ↔ Cancelled, then Renewed. |
| Transaction | Draft → Computed → Accepted (or Discarded). |
Effective dating
effectiveDate on the quote is the only temporal input the API accepts. It
resolves the bureau edition, the base version and any carrier or program override that are in
force for that date.
You cannot pin a version. No version identifiers are accepted anywhere in the public contract. This is deliberate: it is what guarantees that a quote for a given effective date always resolves the configuration that was genuinely in force, and it is why a premium can be defended years later. Endorsements keep the term's effective date, so they rate on the term's locked content.
Multi-program rating
Leave programCode null and one rating request fans out across every program
your client is granted for that product, state and effective date. Each program is an
independent result. This is the same comparison an underwriter sees in Workbench.
Set programCode to rate a single program.
Where a product permits only one program per quote. Accounts Receivable is written on one carrier program per quote. When several grants apply and no program is pinned, the rating call returns 200 with a selection view listing the allowed programs, so you can choose.
Values in these examples are synthetic. Loss costs, multipliers, basic and adjusted rates, increased limit factors, classification codes and descriptions, territory codes, filed editions and filed source references are licensed bureau content. This documentation publishes the shape of the response, never the content inside it.
Worksheet & provenance
The worksheet is the rating, step by step, as the engine executed it. It is captured at rating time, served back verbatim, and never changes. It is the same document Program Studio and Underwriter Workbench print. For ISO GL it carries the edition, locations, classes, coverages, limits, medical payments, deductible, premium determination and provenance; for AAIS products it carries the filed steps.
Alongside the numbers, each derived value carries its source. A territory assignment, for example, records which filed mapping was selected, why it governed, and the document and page it came from.
GET /api/rating/v1/quotes/{quoteId}/ratings/{ratingRunId}/results/{executionId}/worksheet
{
"policyEffectiveDate": "2026-10-01",
"carrier": { "code": "CARRA", "name": "Carrier A" },
"program": { "code": "CARRIER-A-GL", "name": "Commercial GL - Standard" },
"stateEdition": "EXAMPLE-EDITION",
"locations": [
{
"state": "CA", "territory": "EXAMPLE-TERRITORY",
"classifications": [
{ "classCode": "EXAMPLE-CLASS", "description": "Example classification",
"premiumBasis": "Gross Sales", "exposure": 1000000, "ratingUnits": 1000.000,
"coverages": [
{ "coverage": "Premises / Operations", "lossCost": 0.000, "lcm": 0.000, "basicRate": 0.000, "ilf": 0.000, "adjustedRate": 0.000, "premium": 0.00 },
{ "coverage": "Products / Completed Operations", "lossCost": 0.000, "lcm": 0.000, "basicRate": 0.000, "ilf": 0.000, "adjustedRate": 0.000, "premium": 0.00 }
] } ] } ],
"premiumDetermination": [
{ "step": "Calculated premium", "rule": "EXAMPLE-RULE-A", "amount": 0.00 },
{ "step": "Minimum determination", "rule": "EXAMPLE-RULE-B", "amount": 0.00 },
{ "step": "Policy writing minimum", "rule": "EXAMPLE-RULE-C", "amount": 0.00, "applied": false }
],
"finalPremium": 1310.00,
"provenance": [
{ "scope": "Location 1 (CA)",
"statement": "Territory: the filed mapping that governed, why it governed, and what it matched on.",
"source": "Example filed source reference" }
]
}
Structure abbreviated. Values are shown at the precision the engine used. The filed rating plan rounds at defined steps, so recomputing from displayed intermediates reproduces each premium to that rounding. The premium amounts in the response are authoritative.
Worksheets are JSON. There is no PDF endpoint.
Revisions & history
PATCH the quote with expectedRevision to change inputs. A supplied
risk replaces the document wholesale, and the revision increments once when rating
inputs change; reference fields never bump it. A stale expectedRevision returns
QuoteRevisionConflict. Requesting a rating does not change the revision, so rating
the same inputs twice gives you two runs at the same revision, useful for proving a result is
reproducible.
Every run is stamped with the revision it rated and stays retrievable. Fetching an old run returns exactly what it returned the first time, including its worksheet. Nothing about a completed run is ever rewritten. A selection can be changed until the quote is bound, and the change is audited.
POST /quotes -> Q-2026-001, revision 1
POST /quotes/{id}/ratings -> run R1, $12,420
PATCH /quotes/{id} -> revision 2 (exposure increased)
POST /quotes/{id}/ratings -> run R2, $13,180
POST /quotes/{id}/select -> a result of R2 selected
GET /quotes/{id}/ratings -> run history
GET /quotes/{id}/ratings/R1 -> unchanged, still $12,420
Idempotency
Idempotency-Key is required on every operation that changes
something: creating, rating, selecting and binding quotes, and every lifecycle transaction,
accept and discard. PATCH uses expectedRevision instead. Use a fresh key
per logical operation and reuse it when retrying that same operation. Uniqueness is per client,
operation and key.
- Same key, same request, completed: the stored response is replayed with
Idempotency-Replayed: trueand the same ids. A retried bind does not bind twice. - Same key, different request:
409 IdempotencyConflict. - Same key while the first request is still running:
409 IdempotencyInProgress. - A failed or no-effect attempt releases the key. Key expiry never touches history.
Sandbox testing
The Program Studio API screen shows every operation for a published program with its endpoint, request and response, grouped as Quoting, Policy, Transactions and History. In the sandbox, Run Test performs the full lifecycle (quote, rate, select, bind, endorse, cancel, reinstate, renew) and shows the identifiers as they are produced. The production view is copy-only.
Correlation & support
Send X-Correlation-Id and it is echoed on the response and logged. If you do not
send one, the API generates it, and every error body carries it. Quote it in any support
request. It is the fastest route to the exact execution.
Tokens, secrets, risk documents and worksheet content are never written to logs.
Versioning
- The major version is in the URL.
- New lines of business appear in
GET /productswhen you are granted them. - The API never exposes internal version identifiers; the effective date is the only temporal input.
Write clients that ignore unknown JSON fields and tolerate unknown enum values.
Not in this release
Stated plainly so you can plan around them: short-rate cancellation tables and configurable minimum earned premium (cancellation is pro rata, with an audited manual factor override); reinstatement with a lapse; rewrite or extension transactions; TRIA; webhooks or callbacks (all rating is synchronous request and response); asynchronous or batch rating; a PDF worksheet API (worksheets are JSON).
SelectRate rates and records policy transactions. It holds the bound policy term, its current rated state and a ledger of every transaction, each with its calculation and worksheet. It is not a policy administration or accounting system: billing, invoicing, commissions and accounting stay in the systems you already run.
Common questions
Can the API and the underwriting screen ever return different premiums?
No. They resolve the same effective-dated configuration and run the same engine. Identical inputs, program and effective date produce an identical premium and an identical worksheet.
Can we request a specific configuration version?
No. Effective date resolves the version. No version identifiers are accepted anywhere in the public contract.
What does the API return for a risk that is declined?
HTTP 200 with a result status of Declined. HTTP status describes the API call; result status describes the insurance outcome. A rating request that returns nothing but declines is still a successful call.
Are worksheets included in the rating response?
No. A worksheet can run to seventy-plus steps and a fan-out across several programs would multiply that into every response. Each result carries availability and a link; fetch the worksheet when you need it.
How do new lines of business appear?
Through discovery. GET /products lists the products your client may rate, and the product schema endpoint returns each one's risk-document contract.
Can we endorse or cancel a policy through the API?
Yes. Endorsement, pro rata cancellation, reinstatement and renewal are all available, each rated from the policy's current accepted state and recorded in the term's transaction ledger. Cancellation and reinstatement have a calculate preview before you confirm.
Does the API calculate return premium on cancellation?
Yes. The cancellation preview returns the default and applied factor, the return premium, the earned premium and the per-component breakdown. Cancellation is pro rata in this release.
Talk to us about API access.
Bring a program you already rate and we will stand it up in the sandbox so you can call it.