PatternIQ API v1
Send a claim (or a batch) and receive the historical benchmark for each submitted field, the sample size behind it, the historical outcome difference and an evidence level. The API is deterministic and has no LLM in the analysis path.
Overview
Base URL: / on the deployed API host. All analysis endpoints are versioned under /api/v1. Interactive OpenAPI docs are served at /docs and the schema at /openapi.json.
| Endpoint | Purpose |
|---|---|
GET /health | Liveness and evidence load state |
GET /api/v1/meta | Evidence version, period, detectors, thresholds |
POST /api/v1/analyze | One claim |
POST /api/v1/analyze/batch | Up to 10,000 claims, with patterns |
POST /api/v1/adapters/fhir/claim | One FHIR Claim resource |
POST /api/v1/adapters/837p | Raw X12 837P text |
POST /api/v1/adapters/csv | CSV text with an optional column map |
Authentication
Every analysis request uses a bearer API key. Create keys with make key-create NAME=billing PREFIX=piq_live_. Keys are shown once and stored only as a salted SHA-256 hash; revocation is a timestamp, not a delete.
Authorization: Bearer piq_live_<key> X-Request-ID: optional-correlation-id
Keys may be scoped to piq_test_ or piq_live_. Requests are rate limited per key (see /api/v1/meta for limits); a limited request returns 429. Keys never appear in browser JavaScript — the web app proxies through a server route.
Quick start
curl -X POST "$PATTERNIQ_API_URL/api/v1/analyze" \
-H "Authorization: Bearer $PATTERNIQ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"claim_id": "ABC123",
"procedure": "99214",
"primary_diagnosis": "I10",
"pos": "11",
"modifiers": ["25"],
"quantity": 1,
"charge": 285.00,
"insurance_type": "HM"
}'Analyze one claim
POST /api/v1/analyze
{
"claim_id": "ABC123",
"review": true,
"flag_count": 1,
"flags": [
{
"detector": "modifier",
"field": "modifier",
"submitted_value": "25",
"classification": "UNUSUAL",
"message": "Verify modifier 25",
"reason": "This modifier (25) appears on 6.2% of 14,821 comparable historical claims. It is 0.11× as common as the most common configuration for the same procedure and billing context. Comparable claims with this configuration had a 12.0 percentage-point higher historical $0-payment rate.",
"evidence": {
"evidence_level": "STRONG",
"historical_frequency": 0.062,
"submitted_count": 920,
"context_count": 14821,
"comparison_count": 13901,
"zero_payment_rate": 0.24,
"comparison_zero_payment_rate": 0.12,
"zero_payment_difference_pp": 12.0,
"alternatives": [{"value": "(none)", "count": 12000, "frequency": 0.81}]
},
"comparison_context": {
"procedure": "99214",
"pos": "11",
"insurance_type": "HM",
"primary_diagnosis": "I10",
"context": "procedure+pos+insurance_type+primary_diagnosis"
}
}
],
"detections": ["..."],
"evidence_version": "v1.0.0",
"data_vintage": {"from_year": 2016, "through_year": 2025, "population_version": "v1", "detector_version": "v1"}
}detections carries the evidence for every evaluated field, including fields classified NORMAL, so an integration can log or display the benchmark even when nothing fired.
Analyze a batch
POST /api/v1/analyze/batch
{
"batch": {
"claims_analyzed": 20000,
"claims_flagged": 327,
"submitted_charges_flagged": 418291.0,
"detector_counts": {"modifier": 141, "charge": 82, "pos": 51, "diagnosis": 37, "quantity": 16},
"classification_counts": {"NORMAL": 85000, "UNUSUAL": 400, "HIGHLY_UNUSUAL": 90},
"evidence_level_counts": {"INSUFFICIENT": 120, "LIMITED": 2100, "GOOD": 30000, "STRONG": 53000},
"errors": 0
},
"patterns": [
{
"pattern_id": "modifier|97110|11|XX",
"detector": "modifier",
"label": "CPT 97110 + modifier XX",
"affected_claims": 73,
"submitted_charges": 94820.0,
"batch_frequency": 0.314,
"historical_frequency": 0.021,
"relative_frequency": 14.95,
"historical_outcome_difference_pp": 14.8,
"evidence_level": "STRONG",
"repeated": true,
"claim_ids": ["..."]
}
],
"claims": ["per-claim results, same shape as /analyze"],
"evidence_version": "v1.0.0",
"data_vintage": {"...": "..."}
}submitted_charges_flagged is the sum of submitted charges on flagged claims. It is not a loss estimate, not recoverable revenue, and does not imply the charge would otherwise have been lost.
CSV, FHIR and 837P adapters
Conversion happens at the edge, never inside the analysis engine. The canonical claim is the only shape the detectors see.
CSV
POST /api/v1/adapters/csv
{
"csv_text": "cpt_code,dx1,place_service,mod1,units,billed,insurance\n99214,I10,11,25,1,285.00,HM",
"column_map": {"procedure": "cpt_code", "primary_diagnosis": "dx1", "pos": "place_service",
"modifiers": "mod1", "quantity": "units", "charge": "billed", "insurance_type": "insurance"}
}FHIR Claim
POST /api/v1/adapters/fhir/claim
{ "resourceType": "Claim", "id": "fhir-1", "item": [{"productOrService": {"coding": [{"code": "99214"}]}, ...}], ... }837P
Send the raw interchange as the request body. Add ?insurance_type=HM: 837P carries no insurance category, only a payer identity.
curl -X POST "$PATTERNIQ_API_URL/api/v1/adapters/837p?insurance_type=HM" \ -H "Authorization: Bearer $PATTERNIQ_API_KEY" \ -H "Content-Type: text/plain" \ --data-binary @batch.837p
Request schema (canonical claim)
| Field | Type | Notes |
|---|---|---|
| procedure | string | CPT/HCPCS, required |
| pos | string | Place of service, required |
| insurance_type | string | Insurance category code, required |
| primary_diagnosis | string? | ICD-10; optional |
| additional_diagnoses | string[] | Accepted and echoed; not used by V1 detectors |
| modifiers | string[] | Only the first is evaluated in V1 |
| quantity | number? | Must be > 0 |
| charge | number? | Must be > 0; submitted billed amount |
| claim_id | string? | Opaque caller identifier, echoed back |
Response schema
| Field | Meaning |
|---|---|
| review | True when at least one field is UNUSUAL or HIGHLY_UNUSUAL |
| flag_count | Number of flagged fields |
| flags[] | One entry per flagged field, with evidence and context |
| detections[] | Every evaluated field, including NORMAL |
| evidence_version | The historical benchmark version used |
| data_vintage | Years, population version, detector version |
| warnings[] | Unknown codes, skipped checks |
Detector definitions
| Detector | Question | Comparison context (field removed) |
|---|---|---|
| modifier | How common is this modifier configuration among comparable claims? | procedure + pos + insurance (+ primary diagnosis), falling back to procedure + pos |
| pos | Given the procedure, insurance and diagnosis, how common is this POS? | procedure + insurance (+ diagnosis), falling back to procedure + insurance |
| quantity | Given the procedure and POS, how common is this billed quantity? | procedure + pos |
| charge | Where does the submitted charge sit in the historical billed-charge distribution? | procedure + pos (99 quantiles) |
| diagnosis | Given the procedure and billing context, how common is this primary diagnosis? | procedure + pos + insurance |
The evaluated field is removed from its own comparison context, so a POS check never asks “among claims with this POS”. That avoids target leakage.
Classifications
| Classification | Meaning |
|---|---|
| NORMAL | Consistent with the benchmark for comparable claims |
| UNUSUAL | Materially less common than the typical configuration, or a charge in the 1st–5th / 95th–99th percentile |
| HIGHLY_UNUSUAL | Far rarer than the typical configuration, or a charge beyond the 1st/99th percentile |
Classification is deterministic and documented, not a score. It uses the relative frequency (this value vs the most common value in the same context) plus absolute frequency, with a large historical outcome difference escalating severity. Sparse cohorts are capped. There is no risk_score field.
Evidence levels
| Level | Comparison context size |
|---|---|
| INSUFFICIENT | below the evidence floor (25) — the detector returns NORMAL with no claim of precision |
| LIMITED | 25–99 |
| GOOD | 100–999 |
| STRONG | 1,000 or more |
A LIMITED cohort cannot produce a HIGHLY_UNUSUAL classification. THRESHOLDS are published in /api/v1/meta and travelled with every response by evidence version.
Error codes
| Status | Meaning |
|---|---|
| 400 | Malformed JSON body |
| 401 | Missing, invalid or revoked API key |
| 413 | Batch exceeds the configured limit, or body too large |
| 422 | Validation failure; detail.errors lists the fields |
| 429 | Rate limit exceeded; retry after a minute |
| 500 | Internal error; response carries only request_id and status |
Code examples
JavaScript
const res = await fetch(`${process.env.PATTERNIQ_API_URL}/api/v1/analyze`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PATTERNIQ_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ procedure: "99214", pos: "11", insurance_type: "HM",
primary_diagnosis: "I10", modifiers: ["25"], charge: 285 }),
});
const result = await res.json();
if (result.review) console.log(result.flags[0].message);Python
import os, httpx
response = httpx.post(
f"{os.environ['PATTERNIQ_API_URL']}/api/v1/analyze",
headers={"Authorization": f"Bearer {os.environ['PATTERNIQ_API_KEY']}"},
json={"procedure": "99214", "pos": "11", "insurance_type": "HM",
"primary_diagnosis": "I10", "modifiers": ["25"], "charge": 285},
timeout=30,
)
response.raise_for_status()
result = response.json()
for flag in result["flags"]:
print(flag["message"], flag["evidence"]["historical_frequency"])Integration architecture
EHR / PM / billing / clearinghouse
|
v
PatternIQ API -> canonical claim -> historical benchmark artifact
|
v
flags + evidence + repeated patternsThe API holds no customer claim warehouse and no source-data access. Every request reads the precomputed evidence artifact, which is versioned and reproducible.
Privacy and retention
Default philosophy: analyze the claim, return the result, forget the claim.
- Raw claim payloads are not persisted and are not written to logs or analytics.
- No claim payload is sent to an LLM, telemetry, or a third-party logging service.
- Operational metadata may be retained: request id, API key id, timestamp, latency, number of claims, number of flags, API version, evidence version and status code.
- Customer-supplied claim IDs are opaque and are echoed back only.
This is an engineering posture, not a legal conclusion: PatternIQ does not claim HIPAA exemption or compliance. A compliance review is required before processing live PHI.
Versioning
The API version is in the path (/api/v1). The historical benchmark carries its own evidence_version, population_version and detector_version, all returned with every response, so an old result can always be tied to the exact benchmark that produced it. Changing the population definition bumps population_version; changing a detector or threshold bumps evidence_version or detector_version.