SmarterRisk Order Intake API
Overview
The SmarterRisk Order Intake API allows Insurance Partners to programmatically submit risk assessment orders on behalf of their clients. Orders placed via the API follow the same workflow as orders placed through the partner dashboard — your client receives an invitation email, completes their risk assessment, and results appear in your partner dashboard.
Base URL: https://app.smarterrisk.com
V1 Scope
Version 1 is an order intake API.
- Supported: Submit a new order, retrieve the saved order record by
orderId - Not supported: Validation-only endpoints, quote/estimate endpoints, webhook callbacks, or API retrieval of risk reports, scores, recommendations, forms, or assessment results
Stability: V1 is stable. Breaking changes will only ship in a new version (v2). Non-breaking additions (new response fields, new optional request fields) may be added to v1 without notice. We recommend your integration ignore unknown response fields gracefully.
Quick Start
Get your first sandbox order submitted in under 5 minutes.
Step 1 — Get your sandbox API key:
- Log in to your Partner Dashboard at
https://app.smarterrisk.com - Go to My Account
- Click the API Integration tab
- Under Sandbox, click Generate Sandbox Key
- Copy the key immediately — it is shown only once
Step 2 — Submit a test order:
curl -X POST https://app.smarterrisk.com/api/v1/order-intake \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_SANDBOX_KEY" \
-d '{
"clientEmail": "test@example.com",
"businessName": "Test Company",
"contactName": "Jane Smith",
"lines": ["WC"],
"policyNumber": "TEST-001"
}'
Step 3 — Check the response:
{
"message": "Order received successfully",
"orderId": "ORD-1234567890-ABCDE1234",
"status": "pending",
"plan": "bright",
"orderCostCents": 0,
"orderCost": "$0.00",
"sandbox": true
}
Sandbox orders are saved and displayed in the Partner Dashboard GUI under “Current Client Orders” when the sandbox mode is toggled on. But no email is sent, no charge is made, and no client account is created. When you’re ready, generate a live key and swap it in.
Requirements
- An active SmarterRisk Partner subscription (Legacy Pay as You Go Accounts are not supported)
- An API key generated from your Partner Account page
Authentication
All requests must include your API key as a Bearer token in the Authorization header.
Authorization: Bearer YOUR_API_KEY
Managing API Keys
- Log in to your Partner Dashboard
- Go to My Account → API Integration
- Click Generate Key (live) or Generate Sandbox Key (sandbox)
- Give the key a friendly name for identification
- Copy the key immediately — it is shown only once
Key limits:
| Type | Max Active Keys | Notes |
|---|---|---|
| Live | 3 | Each key tracks its own usage and quota |
| Sandbox | 1 | Generating a new sandbox key replaces the previous one |
You can revoke and regenerate keys at any time from the same page.
Security: Never expose your API key in client-side code (browser JavaScript, mobile apps). If you are building a public-facing intake form, keep your API key on your server and proxy requests through your own backend.
Endpoints
| Method | Path | Description |
|---|---|---|
POST | /api/v1/order-intake | Submit a new order |
GET | /api/v1/orders/:orderId | Retrieve the saved order record |
Submit an Order
POST /api/v1/order-intake
Headers
| Header | Value | Required |
|---|---|---|
Authorization | Bearer YOUR_API_KEY | Yes |
Content-Type | application/json | Yes |
Idempotency-Key | Unique string (UUID v4 recommended, max 128 characters) | No |
Idempotency
To safely retry a request without creating a duplicate order, include an Idempotency-Key header.
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
- Scope is per API key
- Only 2xx responses are cached and replayed
- Replayed responses include the header
X-Idempotency-Replayed: true - The response echoes back your
Idempotency-Keyheader - Reuse the same key only for retries of the same request body. Reusing a key with a different body is not supported
- Idempotency keys are valid for 24 hours. After that, the same key will be treated as a new request
- Idempotency is a best-effort safeguard for retry protection
Request Body
{
"clientEmail": "jane@acmecorp.com",
"businessName": "Acme Corp",
"contactName": "Jane Smith",
"lines": ["GL", "PR"],
"policyNumber": "POL-12345",
"numLocations": 2,
"photosRequired": false,
"upgradeSmart": false,
"upgradeIntelligent": false,
"thirdPartyEmail": "broker@example.com"
}
Request Rules
- The request body must be a single JSON object using the exact field names shown below
- Field names and enum values are case-sensitive
- Unknown fields are rejected with
400 VALIDATION_ERROR - Required string fields reject empty strings
Fields
| Field | Type | Required | Description |
|---|---|---|---|
clientEmail | string | Yes | Client’s email address. Used to create the client account. |
businessName | string | Yes | Client’s business name (max 200 characters). |
contactName | string | Yes | Full name of the primary contact (max 100 characters). |
lines | array | Yes | Array of uppercase coverage codes to assess. See Coverage Lines. |
policyNumber | string | Yes | Policy or reference number for your records (max 50 characters). |
numLocations | integer | No | Number of additional locations beyond the 1 included. Send 0 for a single-location order. Range: 0–10. Default: 0. |
photosRequired | boolean | No | Whether photo uploads are required. Requires Smart or Intelligent plan — include upgradeSmart: true or upgradeIntelligent: true. Not available for FL/CL-only orders. Default: false. |
upgradeSmart | boolean | No | Upgrade to Smart plan. Default: false. |
upgradeIntelligent | boolean | No | Upgrade to Intelligent plan. Default: false. See Plan Levels for account-type rules. |
thirdPartyEmail | string | No | A single email address to receive an “order placed” notification (e.g. a broker). This person does not receive the assessment or results — only a notification that an order was submitted. |
Location Counting
Every order includes 1 primary location automatically. The numLocations field specifies additional locations on top of that.
numLocations value | Total locations assessed |
|---|---|
0 (default) | 1 |
1 | 2 |
5 | 6 |
10 | 11 (maximum) |
Common mistake: If your client has 3 locations, send
numLocations: 2(not3).
Assessment by Insurance Line
| Code | Description |
|---|---|
WC | Workers Compensation |
PR | Property |
GL | General Liability |
FL | Fleet |
CL | Contractors Liability |
Rules:
- At least one line is required
- Codes must be uppercase:
WC,PR,GL,FL,CL GL(General Liability) requiresPR(Property) to also be includedphotosRequiredis not available for orders that include onlyFLand/orCL
Plan Levels
The plan determines what your client can access after completing their assessment.
| Plan | How to request | What the client gets |
|---|---|---|
| Bright | Default (no upgrade flags) | Risk assessment + recommendations |
| Smart | upgradeSmart: true | Bright + risk score + full risk report + recommendation updates |
| Intelligent | upgradeSmart: true + upgradeIntelligent: true | Smart + all features (forms, policy builder, training director) |
Account-type behavior:
- MGA / Agency accounts: To request Intelligent, you must send both
upgradeSmart: trueandupgradeIntelligent: true. - Insurer accounts: Start at Smart by default. Send
upgradeIntelligent: truealone to go straight to Intelligent —upgradeSmartis not required.
Tip: If you’re unsure of your account type, always send both
upgradeSmart: trueandupgradeIntelligent: truewhen you want Intelligent. This works correctly for all account types.
Success Response — 201 Created
{
"message": "Order received successfully",
"orderId": "ORD-1234567890-ABCDE1234",
"status": "pending",
"plan": "smart",
"orderCostCents": 5000,
"orderCost": "$50.00"
}
| Field | Description |
|---|---|
orderId | Unique order identifier. Use this to check status via GET /api/v1/orders/:orderId. |
status | Always "pending" on creation. |
plan | The plan assigned to this order ("bright", "smart", or "intelligent"). |
orderCostCents | Order cost in cents as an integer (e.g. 5000 = $50.00). 0 for Bright plan orders. |
orderCost | Order cost as a formatted string (e.g. "$50.00"). "$0.00" for Bright plan orders. |
Sandbox orders include an additional "sandbox": true field.
What Happens After a Successful Order
- Your client receives an invitation email with a link to complete their risk assessment
- The order appears in your Partner Dashboard under Current Client Orders
- Once your client completes the assessment, results are available in your dashboard
Downstream artifacts (reports, scores, recommendations) are not returned by the v1 API. Use your Partner Dashboard to view results.
Degraded Response — 202 Accepted
In rare cases, the order is accepted but a downstream step fails. The API returns 202 instead of 201. If Stripe already charged your account, the response includes invoiceID.
Do not retry 202 responses. The order may already be partially processed. Contact support with the orderId and invoiceID (if present). Support is automatically alerted when this occurs.
| Status | Meaning |
|---|---|
payment_captured_client_failed | Charged, but client account creation failed |
payment_captured_partner_save_failed | Charged, but partner record could not be saved |
payment_captured_queue_failed | Charged, but background processing could not start |
queue_dispatch_failed | No charge, but background processing could not start |
Example:
{
"message": "Payment was captured but client account setup failed. Your account has been charged and support has been notified. Please contact support referencing your order ID.",
"orderId": "ORD-1234567890-ABCDE1234",
"status": "payment_captured_client_failed",
"invoiceID": "in_abc123"
}
Pricing
- Bright plan orders are always no charge:
orderCostCents: 0 - Smart and Intelligent pricing is calculated server-side using the same logic as the partner dashboard, including subscriber discounts, line surcharges, and plan add-ons
- The authoritative billed amount is the
orderCostCents/orderCostreturned in the201response - There is no quote or estimate endpoint in v1 — contact your account manager for your rate card
Check Order Status
GET /api/v1/orders/:orderId
Retrieve the saved order record using the orderId returned by POST /api/v1/order-intake.
curl https://app.smarterrisk.com/api/v1/orders/ORD-1234567890-ABCDE1234 \
-H "Authorization: Bearer YOUR_API_KEY"
Success Response — 200 OK
{
"orderId": "ORD-1234567890-ABCDE1234",
"status": "pending",
"plan": "smart",
"orderType": "assessment",
"createdAt": "2026-03-05T18:00:00.000Z",
"clientInfo": {
"email": "jane@acmecorp.com",
"businessName": "Acme Corp"
},
"serviceDetails": {
"lines": ["GL", "PR"],
"numLocations": 2
}
}
Sandbox orders include "sandbox": true.
V1 Limitation
This endpoint returns the saved order record from the intake service. In normal flows, status remains pending. It does not reflect assessment completion, report availability, or any downstream progress. Degraded statuses are returned if the order encountered a processing issue.
Note:
orderCostCentsandorderCostare only returned in thePOSTresponse. TheGETendpoint returns the saved order record without pricing.
Order Status Values
| Status | Meaning | Action |
|---|---|---|
pending | Order saved successfully | No action needed |
payment_captured_client_failed | Charged, client creation failed | Contact support — do not retry |
payment_captured_partner_save_failed | Charged, partner record save failed | Contact support — do not retry |
payment_captured_queue_failed | Charged, background processing failed | Contact support — do not retry |
queue_dispatch_failed | No charge, background processing failed | Contact support — do not retry |
payment_captured_persistence_failed | Emergency: charged, order record could not be saved | Contact support immediately |
Error — 404
{ "message": "Order not found", "error": "ORDER_NOT_FOUND" }
Returns 404 if the order doesn’t exist or belongs to a different company.
Usage & Quota
Each API key tracks monthly and total order counts. Your account administrator can set a monthly quota per key from the Partner Account page.
Usage is visible in the API Integration section, shown per key (e.g. “42 / 500 orders this month”). Counts reset automatically on the first of each calendar month. All successful live orders count toward the quota.
If the quota is exceeded:
{
"message": "Monthly order quota of 500 reached. Please contact support to increase your limit.",
"error": "MONTHLY_QUOTA_EXCEEDED"
}
API Budget
Your account administrator can set a monthly dollar budget for API orders from the Partner Account page. Each order is costed using the same pricing as the partner dashboard. If an order would cause your account to exceed the budget, it is rejected before any processing or charge occurs.
Budget spend and remaining balance are visible on the Partner Account page. Spend resets on the first of each calendar month. Changing the budget limit does not reset current-month spend.
If the budget would be exceeded:
{
"message": "This order will exceed your monthly budget. Current spend: $450.00, Order cost: $75.00, Budget limit: $500.00. Please contact your admin to update the budget.",
"error": "BUDGET_LIMIT_REACHED"
}
Rate Limits
Each API key is limited to 60 requests per minute (per key, not per account).
Every response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per minute for this key |
X-RateLimit-Remaining | Requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp (seconds) when the window resets |
When the limit is exceeded, the API returns 429 with a Retry-After header indicating seconds to wait.
Sandbox Mode
Use a sandbox API key to test your integration without triggering real processing.
How to get a sandbox key: Partner Dashboard → My Account → API Integration → Sandbox → Generate Sandbox Key.
What sandbox mode does:
- Uses the same base URL and endpoints as live
- Returns a real
201response with"sandbox": true - Order is saved and visible in the Partner Dashboard when Sandbox View is toggled on
- No invitation email is sent
- No third-party notification is sent
- No order processing, Stripe charge, SQS dispatch, or client account creation occurs
- Sandbox orders do not count against live monthly quota or live API budget
- Sandbox orders are automatically deleted after 30 days
What works identically to live: Authentication, validation, error handling, rate limiting, and all request/response schemas.
Use sandbox to verify your integration end-to-end before switching to a live key.
Error Responses
All errors return a JSON body with message and error fields. All responses use Content-Type: application/json.
400 — Validation Error
{
"message": "Business Name — Business Name is required",
"error": "VALIDATION_ERROR",
"errors": [
{
"field": "businessName",
"label": "Business Name",
"message": "Business Name is required",
"detail": "Business Name — Business Name is required"
}
]
}
The top-level message is always the first error as a human-readable string. The errors array contains all validation issues — useful for mapping errors to form fields.
Common validation errors:
| Error | Cause |
|---|---|
Contact Email is required | clientEmail missing or empty |
Contact Email must be a valid email address | Invalid email format |
Business Name is required | businessName missing or empty |
Contact Name is required | contactName missing or empty |
Policy / Reference # is required | policyNumber missing or empty |
At least one Line of Assesment is required | lines array is empty |
General Liability (GL) requires Property (PR) to also be selected | GL without PR |
Photo uploads are not available for Fleet (FL) or Contractors Liability (CL) only orders | photosRequired: true with only FL/CL |
Photo uploads require at least the Smart plan | photosRequired: true without upgradeSmart or upgradeIntelligent |
Third-Party Notification Email must be a valid email address | thirdPartyEmail is not a valid email format |
Unrecognized key(s) in object: '...' | Request body included unsupported fields |
Other 400 errors:
| Error Code | Message |
|---|---|
SMART_REQUIRED_FOR_INTELLIGENT | Smart plan required before Intelligent upgrade (MGA/Agency only) |
INVALID_IDEMPOTENCY_KEY | Idempotency-Key must be a string of 128 characters or fewer |
401 — Authentication Error
{ "message": "API key required", "error": "MISSING_API_KEY" }
{ "message": "Invalid or inactive API key", "error": "INVALID_API_KEY" }
403 — Forbidden
| Error Code | Message |
|---|---|
SUBSCRIPTION_REQUIRED | API access requires an active partner subscription with current status. Check your billing status on the Partner Account page. |
MONTHLY_QUOTA_EXCEEDED | Monthly order quota reached for this API key |
BUDGET_LIMIT_REACHED | Order would exceed the account’s monthly budget |
404 — Not Found
{ "message": "Order not found", "error": "ORDER_NOT_FOUND" }
{ "message": "Error: Company not found", "error": "COMPANY_NOT_FOUND" }
409 — Conflict
Client already exists:
{
"message": "A client account already exists for jane@acmecorp.com. Please contact support if you need to re-order.",
"error": "EXISTING_RISK_PROFILE"
}
Re-orders for existing clients are not supported via the API in v1. To re-order, use the Partner Dashboard or contact support@smarterrisk.com.
Concurrent order:
{
"message": "Your account is currently processing an order. Please try again in a moment.",
"error": "ORDER_PROCESSING_IN_PROGRESS"
}
Another order for your account is being processed — either via the API or the Partner Dashboard. Retry with exponential backoff starting at 5 seconds (e.g. 5s, 10s, 20s).
429 — Rate Limited
{
"message": "Rate limit exceeded. Please slow down.",
"error": "RATE_LIMIT_EXCEEDED",
"retryAfter": 12
}
Wait the number of seconds indicated by retryAfter (also sent as the Retry-After header) before retrying.
500 — Server Error
{
"message": "Error: There was a problem processing the order",
"error": "ORDER_PROCESSING_ERROR"
}
| Error Code | Retryable | Notes |
|---|---|---|
CLIENT_SETUP_FAILED | Yes | No payment was taken. Retry may succeed, but if partial account creation occurred, the retry may return 409 EXISTING_RISK_PROFILE — contact support in that case. |
ORDER_PROCESSING_ERROR | Maybe | Generic unexpected failure. Contact support if it persists after one retry. |
503 — Dependency Unavailable
{
"message": "Pricing service unavailable — please try again",
"error": "PRICING_UNAVAILABLE"
}
SmarterRisk cannot retrieve live pricing. Retry after 10–30 seconds. Contact support if it persists.
Integration Recommendations
| Setting | Recommendation |
|---|---|
| Request timeout | 30 seconds for POST /api/v1/order-intake, 10 seconds for GET /api/v1/orders/:orderId |
| Retry strategy | Retry on 429, 500 CLIENT_SETUP_FAILED, 503. Do not retry 202, 409, or other 500 errors. |
| Backoff | Exponential backoff starting at 5 seconds, max 3 retries |
| Idempotency | Always send an Idempotency-Key on POST requests to protect against network retries |
| Unknown fields | Ignore unknown fields in responses — new fields may be added without a version bump |
Code Examples
cURL
curl -X POST https://app.smarterrisk.com/api/v1/order-intake \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"clientEmail": "jane@acmecorp.com",
"businessName": "Acme Corp",
"contactName": "Jane Smith",
"lines": ["GL", "PR"],
"policyNumber": "POL-12345",
"numLocations": 1
}'
JavaScript (Node.js)
const response = await fetch('https://app.smarterrisk.com/api/v1/order-intake', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.SMARTERRISK_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
clientEmail: 'jane@acmecorp.com',
businessName: 'Acme Corp',
contactName: 'Jane Smith',
lines: ['GL', 'PR'],
policyNumber: 'POL-12345',
numLocations: 1,
}),
});
const data = await response.json();
if (response.status === 201) {
console.log('Order created:', data.orderId, data.plan, data.orderCost);
} else if (response.status === 202) {
// Order accepted but a downstream step failed — do not retry
console.warn('Order degraded:', data.orderId, data.status);
console.warn('Contact support with orderId:', data.orderId);
if (data.invoiceID) console.warn('Invoice:', data.invoiceID);
} else {
console.error('Order failed:', response.status, data.error, data.message);
if (data.errors) {
data.errors.forEach(e => console.error(` ${e.label}: ${e.message}`));
}
}
Python
import os
import uuid
import requests
api_key = os.environ["SMARTERRISK_API_KEY"]
base_url = "https://app.smarterrisk.com"
response = requests.post(
f"{base_url}/api/v1/order-intake",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"clientEmail": "jane@acmecorp.com",
"businessName": "Acme Corp",
"contactName": "Jane Smith",
"lines": ["GL", "PR"],
"policyNumber": "POL-12345",
"numLocations": 1,
},
timeout=30,
)
data = response.json()
if response.status_code == 201:
print(f"Order created: {data['orderId']} ({data['plan']}) — {data['orderCost']}")
elif response.status_code == 202:
# Order accepted but degraded — do not retry
print(f"Order degraded: {data['orderId']} ({data['status']})")
print(f"Contact support with orderId: {data['orderId']}")
else:
print(f"Order failed ({response.status_code}): {data['message']}")
PHP
$apiKey = getenv('SMARTERRISK_API_KEY');
$baseUrl = 'https://app.smarterrisk.com';
$ch = curl_init("$baseUrl/api/v1/order-intake");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
"Authorization: Bearer $apiKey",
'Idempotency-Key: ' . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode([
'clientEmail' => 'jane@acmecorp.com',
'businessName' => 'Acme Corp',
'contactName' => 'Jane Smith',
'lines' => ['GL', 'PR'],
'policyNumber' => 'POL-12345',
'numLocations' => 1,
]),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($body, true);
if ($status === 201) {
echo "Order created: {$data['orderId']} ({$data['plan']}) — {$data['orderCost']}";
} elseif ($status === 202) {
// Order accepted but degraded — do not retry
echo "Order degraded: {$data['orderId']} ({$data['status']}) — contact support";
} else {
echo "Order failed ($status): {$data['message']}";
}
Troubleshooting
| Problem | Likely Cause | Fix |
|---|---|---|
401 MISSING_API_KEY | No Authorization header | Add Authorization: Bearer YOUR_KEY header |
401 INVALID_API_KEY | Key revoked, expired, or wrong environment | Check the key is active in your API Integration dashboard |
403 SUBSCRIPTION_REQUIRED | Subscription lapsed or not active | Check your billing status on the Partner Account page — subscription must be current |
400 VALIDATION_ERROR with “Unrecognized key(s)” | Extra fields in request body | Remove any fields not listed in the schema |
400 with “GL requires PR” | Sent GL without PR | Add "PR" to your lines array |
409 EXISTING_RISK_PROFILE | Client email already has an account | Use the Partner Dashboard to manage existing clients |
409 ORDER_PROCESSING_IN_PROGRESS | Another order is being processed | Wait 5–10 seconds and retry |
403 MONTHLY_QUOTA_EXCEEDED | Hit your key’s monthly limit | Contact your admin to increase the quota |
403 BUDGET_LIMIT_REACHED | Order would exceed monthly budget | Contact your admin to increase the budget |
503 PRICING_UNAVAILABLE | Temporary pricing service issue | Retry after 10–30 seconds |
| Order created but client didn’t get email | Normal processing delay | Allow up to 5 minutes. Check your Partner Dashboard for order status. |
Current V1 Limitations
- No validation-only or quote/estimate endpoint
- No webhook callbacks
- No API endpoints for reports, scores, recommendations, forms, or assessment results
- No bulk order endpoint
- No re-order support for existing clients
Use the Partner Dashboard for downstream risk artifacts until a future API version introduces retrieval endpoints.
Support
For API access, key management, or integration questions contact support@smarterrisk.com.