Glossary & Reference
This page is the canonical reference for terms, identifiers, statuses, and webhook events used across every Stealth Health partner integration. Each tier guide links here rather than duplicating these tables.
ID Prefixes
All partner-facing identifiers carry a stable prefix so you can route them in your own systems without parsing.
| Prefix | Object | Issued by |
|---|---|---|
ptr_ | Partner | Stealth Health (at onboarding) |
sk_live_ / sk_test_ | API key (production / sandbox) | Stealth Health |
whsec_ | Webhook signing secret | Stealth Health |
ref_ | Referral | Stealth Health (POST /partner/referrals) |
partner_intake_ | Patient profile (created via POST /partner/appointments) | Stealth Health — deterministic: partner_intake_{partner_id}_{sanitized_email} |
partner_inline_ | Patient profile (created via POST /partner/prescriptions with inline_patient) | Stealth Health — deterministic: partner_inline_{partner_id}_{sanitized_email} |
pres_ | Prescriber (clinical-tier registry) | Stealth Health (POST /partner/prescribers) |
appt_ | Appointment | Stealth Health |
appt_partner_ | Partner-submitted appointment | Stealth Health (POST /partner/prescriptions) |
PRX- | Partner-submitted prescription (rx_id) | Stealth Health |
lab_ | Lab order | Stealth Health (clinician-placed) |
psc_apt_ | Patient Service Center appointment | Stealth Health (booked via our lab partner's PSC network) |
evt_ | Webhook event | Stealth Health |
Partners are encouraged to set their own partner_reference on every create call. We treat it as the idempotency key and echo it back on every webhook so your downstream joins are stable.
:::note Patient ID stability
The patient_id returned from POST /partner/appointments (and POST /partner/prescriptions) is deterministic and canonical: it is derived from (partner_id, lowercased patient email), so the same patient always resolves to the same patient_id. It is safe to persist and anchor refills on. For follow-up/refill cases, pass it back as patient_id rather than re-sending inline_patient — re-sending inline_patient reuses the same patient profile but opens a new referral and appointment. Some older examples in the tier guides show a pat_… placeholder; the real format is partner_intake_… / partner_inline_… as shown above.
:::
Authentication headers
| Header | Purpose | Required on |
|---|---|---|
X-Partner-ID | Identifies the partner (public, safe to log). | All /partner/* calls. |
X-Api-Key | Authenticates the request (secret). Compared in constant time against a stored SHA-256 hash of the key. | All /partner/* calls. |
X-Access-Tier | Must be clinical for clinical-tier endpoints. Referral-tier keys hitting clinical endpoints are rejected with 403 CLINICAL_ACCESS_REQUIRED. | Clinical-tier endpoints. |
Key rotation supports a dual-hash window: the old key remains valid until previous_key_expires_at (default 7 days) so partners can deploy the new key with zero downtime.
Webhook signature header
Every outbound webhook delivery includes:
X-Stealth-Signature: sha256=<hex-digest>
The digest is HMAC-SHA256(webhook_secret, raw_request_body). Verify on every request, against the raw body bytes, using a constant-time comparison. A reference implementation lives in Clinical Tier § 17.2.
Delivery contract:
- Stealth Health expects a
2xxresponse within 10 seconds. - Failed deliveries are retried with backoff
[30s, 5m, 30m, 2h, 12h](max 6 attempts) and persisted in our internal event store for replay. - Each event has a unique
event_id; treat it as the idempotency key.
Webhook event catalog
Referral-tier events
Available on all tiers. Delivered to the webhook URL configured on your partner account.
| Event | Trigger | Payload extension |
|---|---|---|
referral.enrolled | Patient completes intake form | product_category |
referral.pending_review | Intake submitted for doctor review | — |
referral.approved | Doctor approves prescription | — |
referral.denied | Doctor denies prescription | denial_category (medical_contraindication, incomplete_information, not_a_candidate, ...) |
referral.payment_due | Payment required from patient | amount_cents, currency |
referral.payment_complete | Patient payment received | amount_cents, currency |
referral.awaiting_shipment | Prescription sent to pharmacy | — |
referral.shipped | Order shipped by pharmacy | carrier, estimated_delivery |
referral.in_transit | Carrier scan | carrier |
referral.out_for_delivery | Shipment out for delivery | carrier |
referral.delivered | Shipment delivered | carrier, delivered_at |
referral.delivery_exception | Delivery issue (failed attempt etc.) | carrier, exception_type |
referral.cancelled | Referral cancelled | cancelled_by (partner or patient) |
referral.expired | Enrollment URL expired without completion | — |
Clinical-tier events
Sent only to clinical-tier partners. PHI-bearing payloads.
| Event | Trigger | Payload extension |
|---|---|---|
patient.created | Patient profile created after enrollment | patient (full profile) |
patient.updated | Patient updates their profile | patient (changed fields) |
appointment.intake_completed | Intake questionnaire submitted | appointment_id, intake_summary |
appointment.prescription_signed | A Stealth or partner physician signed an Rx for a partner-owned appointment. Emitted from all sign paths (doctor portal, standalone, JotForm, POST /partner/prescriptions) | appointment_id, prescription: { rx_id, medications[] } (slim shape — see Clinical Tier § 13.2.1) |
transaction.succeeded | Payment successfully processed | transaction (full detail) |
transaction.refunded | Payment refunded | transaction, refund_amount_cents, reason |
prescription.received | Partner-submitted Rx accepted by POST /partner/prescriptions | appointment_id, prescription_id, rx_id, prescriber_id, medications[] |
prescription.rejected | Either (a) partner-submitted Rx rejected after async re-validation, or (b) Stealth physician declined to sign for a partner-owned appointment. Paired with referral.denied in case (b). | appointment_id?, prescription_id?, error_code, error_message (see Clinical Tier § 13.2.2 for the enum) |
messageThread.updated | New message landed in a partner-visible thread; body is not included — re-fetch via GET /partner/patients/:id/messages | thread_id, appointment_id, patient_id, last_message_at, sender_type, sender_role |
lab_order.created | Clinician placed a lab order on a partner-owned appointment | lab_order_id, appointment_id, patient_id, collection_method, provider, tests[] (summary) |
lab_order.requisition_ready | Lab published the requisition PDF | lab_order_id, requisition_pdf_url_expires_at |
lab_order.sample_collected | Sample collected (walk-in, at-home phleb, or testkit registration) | lab_order_id, collected_at, collection_method |
lab_order.results_updated | Lab released results (partial or final); biomarker values NOT included — re-fetch via GET /partner/lab-orders/:id/results | lab_order_id, result_status, resulted_at |
lab_order.completed | All ordered markers final | lab_order_id, resulted_at |
lab_order.cancelled | Order cancelled before sample collection | lab_order_id, cancellation_reason |
lab_order.exception | Lab rejected the sample or order entered unrecoverable error state | lab_order_id, detailed_status, exception_note |
Prescriber-Partner events
| Event | Trigger | Payload extension |
|---|---|---|
appointment.submitted | Appointment created and queued for review | appointment_id, patient_id, status, estimated_review_hours |
appointment.in_review | Physician has opened the case | appointment_id |
appointment.prescription_signed | Physician approved and signed a prescription | appointment_id, prescription, prescriber |
appointment.denied | Physician denied the case | appointment_id, denial_category, denial_reason |
prescription.routed | Prescription sent to pharmacy (when we route) | appointment_id, pharmacy_name, pharmacy_npi |
prescription.filled | Pharmacy confirmed fill and shipped | appointment_id, tracking |
Status lifecycles
Referral status
Appointment status (clinical / prescriber-partner)
doctor_reviewed is also the entry state for partner-submitted prescriptions (the appointment is created already in this state by POST /partner/prescriptions).
Common error codes
The complete per-endpoint error catalog lives in each tier guide. The codes below appear across multiple endpoints.
| Code | HTTP | Meaning |
|---|---|---|
MISSING_CREDENTIALS | 401 | X-Partner-ID or X-Api-Key header missing. |
INVALID_CREDENTIALS | 401 | Partner not found, inactive, or key hash mismatch. |
CLINICAL_ACCESS_REQUIRED | 403 | Endpoint requires clinical-tier access but a referral-tier key was used. |
RATE_LIMIT_EXCEEDED | 429 | Per-minute rate limit hit. Honor Retry-After. |
INVALID_BODY | 400 | Request body failed schema validation. details contains specifics. |
INTERNAL_ERROR | 500 | Unexpected server error. Safe to retry with exponential backoff. |
Rate limits
| Environment | Limit (per partner, per minute) |
|---|---|
Production (api.stealth.health) | 300 |
Sandbox (sandbox.stealth.health) | 60 |
429 responses include standard Retry-After, X-RateLimit-Remaining, and X-RateLimit-Reset headers.
Environments
| Environment | Base URL | Notes |
|---|---|---|
| Production | https://api.stealth.health | Real PHI. Requires production API key (sk_live_*). |
| Sandbox | https://sandbox.stealth.health | Synthetic data only. Requires sandbox key (sk_test_*). Functionally identical surface. |