{"openapi":"3.0.3","info":{"title":"PanelRoute Partner API","version":"1.0.0","description":"REST API for prescribing platforms to provision clinics, manage patients, place lab orders, and pull results. Authenticate with a Bearer API key (prk_live_… or prk_test_…). Test keys run against an isolated sandbox — no real labs, charges, or billing."},"servers":[{"url":"https://api.panelroute.com/v1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Clinics"},{"name":"Patients"},{"name":"Panels"},{"name":"Orders"},{"name":"Usage"},{"name":"Webhooks"}],"paths":{"/providers":{"post":{"tags":["Clinics"],"summary":"Provision a clinic","description":"Creates (or returns, idempotently on external_clinic_id) a clinic under your partner account. Auto-activated. Partner keys only.","operationId":"post_providers","security":[{"bearerAuth":["providers:write"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"provider":{"id":"prov_uuid","org_name":"Downtown Family Clinic","status":"active","external_clinic_id":"clinic_8842"}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"org_name":"Downtown Family Clinic","external_clinic_id":"clinic_8842","contact_email":"ops@downtownclinic.com","npi":"1982749531","licensed_states":["AZ"],"baa_attested":true}}}}},"get":{"tags":["Clinics"],"summary":"List clinics","operationId":"get_providers","security":[{"bearerAuth":["providers:read"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"data":[{"id":"prov_uuid","org_name":"Downtown Family Clinic","status":"active","external_clinic_id":"clinic_8842","npi":"1982749531","licensed_states":["AZ"]}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/providers/{id}":{"get":{"tags":["Clinics"],"summary":"Retrieve a clinic","operationId":"get_providers__id_","security":[{"bearerAuth":["providers:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Clinic (provider) id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"provider":{"id":"prov_uuid","org_name":"Downtown Family Clinic","status":"active","external_clinic_id":"clinic_8842","contact_email":"ops@downtownclinic.com"}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}},"patch":{"tags":["Clinics"],"summary":"Update a clinic","description":"Partial update: org_name, contact_email, npi, licensed_states. Status and billing linkage are platform-managed.","operationId":"patch_providers__id_","security":[{"bearerAuth":["providers:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Clinic (provider) id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"provider":{"id":"prov_uuid","org_name":"Downtown Family Clinic","status":"active","contact_email":"newops@downtownclinic.com"}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"contact_email":"newops@downtownclinic.com"}}}}}},"/patients":{"post":{"tags":["Patients"],"summary":"Create a patient","description":"Email, phone, and full address are required so orders are ready to ship. A test key creates a sandbox patient.","operationId":"post_patients","security":[{"bearerAuth":["patients:write"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"patient":{"id":"pat_uuid","provider_id":"prov_uuid","first_name":"Jane","last_name":"Doe","dob":"1990-04-12","gender":"female"}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"provider_id":"prov_uuid","first_name":"Jane","last_name":"Doe","dob":"1990-04-12","gender":"female","email":"jane@example.com","phone":"6025551234","address_line1":"123 Main St","address_city":"Phoenix","address_state":"AZ","address_zip":"85004"}}}}},"get":{"tags":["Patients"],"summary":"List patients","operationId":"get_patients","security":[{"bearerAuth":["patients:read"]}],"parameters":[{"name":"provider_id","in":"query","required":false,"description":"Filter to one clinic.","schema":{"type":"string"}},{"name":"q","in":"query","required":false,"description":"Search by name.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"1–100 (default 50).","schema":{"type":"string"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: the next_cursor from the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"data":[{"id":"pat_uuid","first_name":"Jane","last_name":"Doe","dob":"1990-04-12"}],"has_more":false,"next_cursor":null}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/patients/{id}":{"get":{"tags":["Patients"],"summary":"Retrieve a patient","operationId":"get_patients__id_","security":[{"bearerAuth":["patients:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Patient id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"patient":{"id":"pat_uuid","first_name":"Jane","last_name":"Doe","dob":"1990-04-12","gender":"female"}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}},"patch":{"tags":["Patients"],"summary":"Update a patient","description":"Partial update of contact, address, and name. dob/gender changes go through support. The patient must remain order-ready (required fields can’t be blanked).","operationId":"patch_patients__id_","security":[{"bearerAuth":["patients:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Patient id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"patient":{"id":"pat_uuid","first_name":"Jane","last_name":"Doe","phone":"6025559999"}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"phone":"6025559999","address_line1":"500 New Address Rd"}}}}}},"/panels":{"get":{"tags":["Panels"],"summary":"List orderable panels","description":"Standard panels, your partner-wide shared panels (shared: true), plus a clinic’s own custom panels with ?provider_id. Every panel lists its biomarkers in `markers` ({ marker_id, name, slug }) so you can show doctors and patients exactly what is being ordered; marker_count is its length. Partner keys see wholesale_price + retail_price; provider keys see price. Prices are per-panel — every order additionally carries a fixed $15.00 Lab Processing Fee.","operationId":"get_panels","security":[{"bearerAuth":["panels:read"]}],"parameters":[{"name":"provider_id","in":"query","required":false,"description":"Include this clinic’s custom panels.","schema":{"type":"string"}},{"name":"include_archived","in":"query","required":false,"description":"true to list EVERY custom panel the owner has — archived, pending (e.g. awaiting re-approval after a restore) and rejected included — instead of only orderable ones. Applies to the clinic named by provider_id (pass your own id with a provider key) and, for partner keys, to shared panels. Every custom panel carries approval_status, archived and orderable; restore archived ones with POST /panels/{id}/restore (only approval_status \"approved\" restores straight to orderable).","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"data":[{"id":"panel_uuid","type":"custom","name":"TRT Panel (Male)","collection_method":"walk_in_test","lab_name":"Labcorp","marker_count":4,"markers":[{"marker_id":123,"name":"Testosterone, Total","slug":"testosterone-total"},{"marker_id":124,"name":"Testosterone, Free","slug":"testosterone-free"},{"marker_id":201,"name":"Estradiol","slug":"estradiol"},{"marker_id":305,"name":"Sex Hormone Binding Globulin","slug":"shbg"}],"approval_status":"approved","archived":false,"orderable":true,"wholesale_price":"54.00","retail_price":"70.20"}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}},"post":{"tags":["Panels"],"summary":"Create a custom panel","description":"Registers a custom panel with the lab network. Pass provider_id for a single-clinic panel, or shared: true (partner keys) for a partner-wide panel orderable by ALL your clinics. Auto-approves when the lab activates it immediately; otherwise it is pending and panel.approved fires once active (usually within a day). Live keys only.","operationId":"post_panels","security":[{"bearerAuth":["panels:write"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"panel":{"id":"panel_uuid","type":"custom","shared":true,"name":"TRT Panel (Male)","approval_status":"pending","orderable":false}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"shared":true,"name":"TRT Panel (Male)","collection_method":"walk_in_test","markers":[{"marker_id":123,"provider_id":"TESTOSTERONE_TOTAL"}]}}}}}},"/panels/{id}":{"get":{"tags":["Panels"],"summary":"Retrieve a panel","description":"One panel with its full biomarker list, priced exactly as GET /panels. Standard panels, your shared panels, and (with ?provider_id for partner keys) a clinic’s own custom panels — archived ones included, with orderable: false. 404 when the panel is not in your scope.","operationId":"get_panels__id_","security":[{"bearerAuth":["panels:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Panel id (standard or custom).","schema":{"type":"string"}},{"name":"provider_id","in":"query","required":false,"description":"Partner keys: the clinic whose custom panel this is.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"panel":{"id":"panel_uuid","type":"custom","name":"TRT Panel (Male)","collection_method":"walk_in_test","lab_name":"Labcorp","marker_count":4,"markers":[{"marker_id":123,"name":"Testosterone, Total","slug":"testosterone-total"},{"marker_id":124,"name":"Testosterone, Free","slug":"testosterone-free"},{"marker_id":201,"name":"Estradiol","slug":"estradiol"},{"marker_id":305,"name":"Sex Hormone Binding Globulin","slug":"shbg"}],"approval_status":"approved","archived":false,"orderable":true,"wholesale_price":"54.00","retail_price":"70.20"}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}},"delete":{"tags":["Panels"],"summary":"Archive a custom panel","description":"Archives (soft-deletes) a custom panel: it disappears from GET /v1/panels, POST /v1/orders rejects it by name, and the lab test is deactivated with the lab network. Orders already placed with it are unaffected. Custom panels only — a provider key may archive its own panels; a partner key its shared panels or any clinic’s panel in scope. Live keys only. 404 for standard-catalog or out-of-scope panels, 409 if already archived.","operationId":"delete_panels__id_","security":[{"bearerAuth":["panels:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Custom panel id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"panel":{"id":"panel_uuid","type":"custom","name":"TRT Panel (Male)","archived":true,"archived_at":"2026-09-07T18:00:00Z","orderable":false}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/biomarkers":{"get":{"tags":["Panels"],"summary":"Search the biomarker catalog","description":"Search orderable biomarkers by name — or omit q and pass lab to page through that lab’s full catalog. Each result’s marker_id + provider_id plug straight into POST /v1/panels’ markers array — build a panel-builder UI on top of this. provider_id codes are lab-specific: pass ?lab= when building a panel (all of a panel’s markers must come from one lab). Every marker carries its own price, markup included: provider keys see price; partner keys see wholesale_price + retail_price. Marker prices are estimates for building panels — the panel’s price is confirmed when it is created (a panel is priced as one lab test, which can differ from the sum of its markers), and every order adds the flat lab_processing_fee once. price_varies: true means the lab may adjust that marker’s price by condition (e.g. patient state).","operationId":"get_biomarkers","security":[{"bearerAuth":["panels:read"]}],"parameters":[{"name":"q","in":"query","required":false,"description":"Search term (min 2 chars). Optional when lab is given — omit to list the lab’s full catalog.","schema":{"type":"string"}},{"name":"lab","in":"query","required":false,"description":"quest | labcorp | bioreference | sonora_quest | ihd — required to get provider_id codes.","schema":{"type":"string"}},{"name":"page","in":"query","required":false,"description":"Page number (default 1). Keep requesting until page equals pages.","schema":{"type":"string"}},{"name":"size","in":"query","required":false,"description":"Results per page, 1–100 (default 50).","schema":{"type":"string"}},{"name":"provider_id","in":"query","required":false,"description":"Partner keys: price as that clinic’s own panel. Omit for shared-panel pricing.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"data":[{"marker_id":123,"name":"Testosterone, Total","slug":"testosterone-total","unit":"ng/dL","provider_id":"TESTOSTERONE_TOTAL","lab":"labcorp","wholesale_price":"34.82","retail_price":"45.27","price_varies":false}],"page":1,"size":50,"pages":21,"lab_processing_fee":"15.00"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/coverage":{"get":{"tags":["Panels"],"summary":"Check lab locations near a zip","description":"How many patient service centers (walk-in draw sites) each lab has within range of a zip — check before ordering so the patient is sent to a lab that is actually nearby. ZIP only (convert a city to a zip first). locations_within_radius is null when that lab is not offered for the area at all (e.g. Quest and Labcorp in NY, NJ and RI); 0 means offered but no locations within the radius. Walk-in only: at-home draws are not offered.","operationId":"get_coverage","security":[{"bearerAuth":["panels:read"]}],"parameters":[{"name":"zip","in":"query","required":false,"description":"5-digit zip (ZIP+4 accepted). Required.","schema":{"type":"string"}},{"name":"radius","in":"query","required":false,"description":"Miles: 10 | 20 | 25 | 50 | 100 (default 25).","schema":{"type":"string"}},{"name":"lab","in":"query","required":false,"description":"quest | labcorp | bioreference | sonora_quest — repeat or comma-separate; default all.","schema":{"type":"string"}},{"name":"provider_id","in":"query","required":false,"description":"Partner keys: check through that clinic’s lab account.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"zip":"85004","radius_miles":25,"data":[{"lab":"quest","lab_name":"Quest Diagnostics","locations_within_radius":36,"available":true},{"lab":"labcorp","lab_name":"LabCorp","locations_within_radius":16,"available":true},{"lab":"bioreference","lab_name":"BioReference","locations_within_radius":null,"available":false}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/panels/{id}/restore":{"post":{"tags":["Panels"],"summary":"Restore an archived custom panel","description":"Un-archives a custom panel (the inverse of DELETE /panels/{id}; same ownership rules, live keys only). The lab test is reactivated with the lab network and the panel re-checked: approval_status is \"approved\" (orderable now) only if it was approved before, is active at the lab, has a price, and the lab’s cost is unchanged; otherwise \"pending\" — a changed cost drops the old price and the panel is repriced automatically at approval — and panel.approved fires once it clears. A panel rejected before archiving stays rejected. 409 if not archived.","operationId":"post_panels__id__restore","security":[{"bearerAuth":["panels:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Custom panel id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"panel":{"id":"panel_uuid","type":"custom","name":"TRT Panel (Male)","archived":false,"approval_status":"approved","orderable":true,"message":"Restored and ready to order."}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/markup":{"get":{"tags":["Panels"],"summary":"Retail markup config","description":"Your default retail markup and any per-panel overrides. Partner keys only.","operationId":"get_markup","security":[{"bearerAuth":["panels:read"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"default_markup_percent":30,"overrides":[{"panel_id":"panel_uuid","markup_percent":45}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}},"put":{"tags":["Panels"],"summary":"Set retail markup","description":"Set the default retail markup and/or per-panel overrides (markup_percent: null removes an override). Applies to future orders. Wholesale pricing is not writable.","operationId":"put_markup","security":[{"bearerAuth":["pricing:write"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"default_markup_percent":30,"overrides":[{"panel_id":"panel_uuid","markup_percent":45}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"default_markup_percent":30,"overrides":[{"panel_id":"panel_uuid","markup_percent":45}]}}}}}},"/orders":{"post":{"tags":["Orders"],"summary":"Place an order","description":"No card is charged — usage is billed monthly at wholesale. Every order includes a fixed $15.00 Lab Processing Fee on top of the panels’ wholesale price (amount_charged reflects it). Requires a consents object. One panel: panel_id (standard) or custom_panel_id. Several panels in ONE order (one requisition, one draw, one fee): panel_ids and/or custom_panel_ids arrays, up to 10 — they must be from the same lab and share a collection method, and every panel must be active; otherwise 400 with the offending panel named, before anything is billed. A test key places a sandbox order (no charge, no lab).","operationId":"post_orders","security":[{"bearerAuth":["orders:write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Optional UUID. Safe-retries the request — the first call runs and its response is replayed for repeats with the same key.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"order":{"id":"order_uuid","status":"processing","amount_charged":"93.00","collection_method":"walk_in_test","items":[{"id":"panel_uuid","type":"custom","name":"TRT Panel (Male)"},{"id":"panel_uuid_2","type":"custom","name":"Thyroid Panel"}]}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"provider_id":"prov_uuid","patient_id":"pat_uuid","custom_panel_ids":["panel_uuid","panel_uuid_2"],"consents":{"telehealth":true,"hipaa_authorization":true}}}}}},"get":{"tags":["Orders"],"summary":"List orders","operationId":"get_orders","security":[{"bearerAuth":["orders:read"]}],"parameters":[{"name":"provider_id","in":"query","required":false,"description":"Filter to one clinic.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"1–100 (default 50).","schema":{"type":"string"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: the next_cursor from the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"data":[{"id":"order_uuid","status":"processing"}],"has_more":false,"next_cursor":null}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/orders/{id}":{"get":{"tags":["Orders"],"summary":"Retrieve an order","description":"`panels` lists every panel on the order in order-set position; `panel` is the first of them (kept for existing integrations). `requisition_available` flips true once the lab has accepted the order (usually within minutes) — then download the form the patient takes to the draw site from /orders/{id}/requisition.pdf.","operationId":"get_orders__id_","security":[{"bearerAuth":["orders:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Order id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"order":{"id":"order_uuid","status":"processing","lab_status":"requisition_created","amount_charged":"54.00","result_available":false,"requisition_available":true,"patient":{"id":"pat_uuid","first_name":"Jane","last_name":"Doe"},"panel":{"id":"panel_uuid","type":"custom","name":"TRT Panel (Male)"},"panels":[{"id":"panel_uuid","type":"custom","name":"TRT Panel (Male)"}]}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/orders/{id}/results":{"get":{"tags":["Orders"],"summary":"Fetch results","description":"Structured results once available. 409 until the lab reports back (watch the order.results_ready webhook).","operationId":"get_orders__id__results","security":[{"bearerAuth":["orders:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Order id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"order_id":"order_uuid","results":{"results":[{"name":"Estradiol","value":46.29,"unit":"pg/mL","is_above_max_range":false}]},"pdf_url":"https://…"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/orders/{id}/cancel":{"post":{"tags":["Orders"],"summary":"Cancel an order","description":"Cancels a submitted/processing order. Live orders only within 1 hour of placement (after that, contact support); test orders any time. Usage/charges are reversed and order.cancelled fires.","operationId":"post_orders__id__cancel","security":[{"bearerAuth":["orders:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Order id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"order_id":"order_uuid","status":"cancelled","cleanup":"complete"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/orders/{id}/requisition.pdf":{"get":{"tags":["Orders"],"summary":"Download the requisition (lab order form)","description":"The lab order form the patient takes to the draw site, as an application/pdf stream. Ready once requisition_available is true on the order (usually within minutes of placement); 409 before that. Give it to the patient — PanelRoute does not contact patients directly. Test orders return a canned sandbox PDF.","operationId":"get_orders__id__requisition.pdf","security":[{"bearerAuth":["orders:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Order id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"note":"binary application/pdf response"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/orders/{id}/results.pdf":{"get":{"tags":["Orders"],"summary":"Download the results PDF","description":"The signed lab report as an application/pdf stream. 409 until results are ready. Test orders return a canned sandbox PDF.","operationId":"get_orders__id__results.pdf","security":[{"bearerAuth":["orders:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Order id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"note":"binary application/pdf response"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/orders/{id}/events":{"get":{"tags":["Orders"],"summary":"Order status timeline","description":"Every status transition the order has been through, in sequence — the authoritative history to reconcile against instead of reconstructing state from webhooks.","operationId":"get_orders__id__events","security":[{"bearerAuth":["orders:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Order id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"order_id":"order_uuid","current_status":"delivered","events":[{"from_status":null,"to_status":"pending_payment","occurred_at":"2026-07-01T10:00:00Z"},{"from_status":"pending_payment","to_status":"processing","occurred_at":"2026-07-01T10:00:04Z"}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/orders/{id}/simulate":{"post":{"tags":["Orders"],"summary":"Advance a test order (sandbox)","description":"SANDBOX ONLY (test key). Advances a test order processing → collected → resulted → delivered, or rehearse the unhappy paths with to: \"failed\" / \"cancelled\". Omit \"to\" to advance one step. interpretation (\"normal\" | \"abnormal\", default abnormal) shapes the canned results. On \"delivered\", results attach and order.results_ready fires.","operationId":"post_orders__id__simulate","security":[{"bearerAuth":["orders:write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Test order id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"order_id":"order_uuid","status":"delivered","result_available":true}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"to":"delivered","interpretation":"abnormal"}}}}}},"/usage":{"get":{"tags":["Usage"],"summary":"Usage ledger","description":"What you owe for the period. Partner keys only. A test key returns an empty ledger (sandbox orders never bill).","operationId":"get_usage","security":[{"bearerAuth":["usage:read"]}],"parameters":[{"name":"period","in":"query","required":false,"description":"YYYY-MM to scope to a billing period.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"period":"2026-07","total_wholesale":"324.00","order_count":6,"events":[{"order_id":"order_uuid","wholesale":"54.00","retail":"70.20","billing_period":"2026-07"}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/invoices":{"get":{"tags":["Usage"],"summary":"List invoices","description":"Monthly wholesale invoices, newest first. invoice.finalized fires when a period closes. Partner keys only.","operationId":"get_invoices","security":[{"bearerAuth":["usage:read"]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"1–100 (default 50).","schema":{"type":"string"}},{"name":"starting_after","in":"query","required":false,"description":"Cursor: the next_cursor from the previous page.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"data":[{"id":"inv_uuid","billing_period":"2026-06","total_wholesale":"324.00","order_count":6,"status":"finalized"}],"has_more":false,"next_cursor":null}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/invoices/{id}":{"get":{"tags":["Usage"],"summary":"Retrieve an invoice","description":"One invoice with its line items (the usage rows it covers).","operationId":"get_invoices__id_","security":[{"bearerAuth":["usage:read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Invoice id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"invoice":{"id":"inv_uuid","billing_period":"2026-06","total_wholesale":"324.00","order_count":6,"status":"finalized","lines":[{"order_id":"order_uuid","wholesale":"54.00","occurred_at":"2026-06-12T15:00:00Z","reversed_after_close":false}]}}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/webhooks/portal":{"get":{"tags":["Webhooks"],"summary":"Webhook portal link","description":"A short-lived link to the hosted portal where you add/verify webhook endpoints and inspect deliveries. Available to partner keys and provider keys alike — each key owner has its own isolated webhook application.","operationId":"get_webhooks_portal","security":[{"bearerAuth":["webhooks:manage"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"portal_url":"https://app.svix.com/…"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/webhooks/endpoints":{"get":{"tags":["Webhooks"],"summary":"List webhook endpoints","operationId":"get_webhooks_endpoints","security":[{"bearerAuth":["webhooks:manage"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"data":[{"id":"ep_2x…","url":"https://example.com/webhooks/panelroute","event_types":null,"disabled":false,"created_at":"2026-07-01T10:00:00Z"}]}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}},"post":{"tags":["Webhooks"],"summary":"Add a webhook endpoint","description":"Registers an HTTPS endpoint. Omit event_types to subscribe to every event type your key can receive (the endpoint is stored with that full list, so event_types comes back populated; event types added to the catalog later are not added automatically). The URL is not contacted at registration — deliveries start when events fire. The signing secret is returned once here (also retrievable via /webhooks/endpoints/{id}/secret). Partner keys can subscribe to every event type; provider keys to their own order.* events and panel.approved.","operationId":"post_webhooks_endpoints","security":[{"bearerAuth":["webhooks:manage"]}],"parameters":[],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"endpoint":{"id":"ep_2x…","url":"https://example.com/webhooks/panelroute","event_types":["order.results_ready","order.failed"],"disabled":false},"secret":"whsec_…"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}},"requestBody":{"required":true,"content":{"application/json":{"example":{"url":"https://example.com/webhooks/panelroute","event_types":["order.results_ready","order.failed"]}}}}}},"/webhooks/endpoints/{id}":{"delete":{"tags":["Webhooks"],"summary":"Remove a webhook endpoint","operationId":"delete_webhooks_endpoints__id_","security":[{"bearerAuth":["webhooks:manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Endpoint id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"deleted":true}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}},"/webhooks/endpoints/{id}/secret":{"get":{"tags":["Webhooks"],"summary":"Endpoint signing secret","operationId":"get_webhooks_endpoints__id__secret","security":[{"bearerAuth":["webhooks:manage"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Endpoint id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"example":{"secret":"whsec_…"}}}},"400":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"type":"invalid_request","message":"A human-readable message.","request_id":"req_a1b2c3d4e5f6a7b8"}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"prk_live_… / prk_test_…","description":"Your API key from the API keys tab of the partner portal."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request","unauthorized","forbidden","not_found","conflict","payment_required","rate_limited","api_error"]},"message":{"type":"string"},"request_id":{"type":"string","description":"Quote this to support — it indexes our server logs."}}}}}}}}