{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/health":{"get":{"summary":"Health","description":"Liveness probe for Render's healthCheckPath and external uptime checks.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/sr_fax_callback":{"post":{"summary":"Sr Fax Callback","description":"Webhook for SRFax send-result callbacks (the per-fax sNotifyURL).\n\nSRFax posts FaxDetailsID and ErrorCode for every send; ErrorCode is empty\non success, so only failed sends do any work.","operationId":"sr_fax_callback_sr_fax_callback_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_sr_fax_callback_sr_fax_callback_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sendgrid/event_webhook/receive":{"post":{"summary":"Sendgrid Event Webhook","description":"Webhook for the SendGrid event webhook, which posts events in batches.\n\nSendGrid retries a batch for 24 hours unless it gets a 2xx, so the batch is\nrecorded before answering; a malformed body gets FastAPI's automatic 422 and\nis retried.\n\nhttps://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features","operationId":"sendgrid_event_webhook_api_sendgrid_event_webhook_receive_post","requestBody":{"content":{"application/json":{"schema":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Events"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ses/event_webhook/receive":{"post":{"summary":"Ses Event Webhook","description":"Webhook for AWS SES events, which are delivered wrapped in SNS messages.\n\nSNS posts with a `text/plain` content type, so the body is taken raw and\nparsed here instead of being declared as a JSON body. The SNS message type\nheader decides the work: `SubscriptionConfirmation` needs its subscribe URL\nfetched to activate the topic, `Notification` carries the SES event itself.\n\nhttps://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html","operationId":"ses_event_webhook_api_ses_event_webhook_receive_post","parameters":[{"name":"x-amz-sns-message-type","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Amz-Sns-Message-Type"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"string","contentMediaType":"application/octet-stream","default":"","title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/speechify_webhook":{"post":{"summary":"Speechify Webhook","description":"Webhook for Speechify Simba agent batch-call events.\n\nThe body is taken raw because the HMAC in the Speechify-Signature header\ncovers the exact bytes sent, which can't be re-derived from a parsed payload.","operationId":"speechify_webhook_speechify_webhook_post","parameters":[{"name":"Speechify-Signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Speechify-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/retell_webhook":{"post":{"summary":"Retell Webhook","description":"Webhook for Retell voice-agent call events (signature-collection pilot).\n\nThe body is taken raw because the HMAC in the X-Retell-Signature header\ncovers the exact bytes sent, which can't be re-derived from a parsed payload.","operationId":"retell_webhook_retell_webhook_post","parameters":[{"name":"X-Retell-Signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Retell-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/stripe_webhook":{"post":{"summary":"Stripe Webhook","description":"Webhook for Stripe events on hand-off invoices and patient payment links.\n\nThe body is read raw so an unparseable payload can be answered with a 200\nrather than a validation error: Stripe retries non-2xx deliveries for days,\nand a body we can't parse now won't parse on a retry either. The raw bytes\nare also what the signature covers.\n\nAn unsigned or badly signed delivery is rejected with a 401 before it is\nparsed.","operationId":"stripe_webhook_stripe_webhook_post","parameters":[{"name":"Stripe-Signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/aftership_webhook":{"post":{"summary":"Aftership Webhook","description":"Webhook for AfterShip tracker updates.\n\nThe body is taken raw because the HMAC in the aftership-hmac-sha256 header\ncovers the exact bytes sent, which can't be re-derived from a parsed payload.\nAn unparseable body raises, answering 500 so AfterShip retries the delivery.","operationId":"aftership_webhook_aftership_webhook_post","parameters":[{"name":"aftership-hmac-sha256","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Aftership-Hmac-Sha256"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/twilio/sms":{"post":{"summary":"Twilio Sms","description":"Webhook for inbound SMS received through Twilio.\n\n`Body` MUST have a default. Twilio sends `Body=` (empty) for a media-only\nMMS — a patient texting a photo of their insurance card with no caption —\nand FastAPI treats an empty form value as absent, so a required `Body`\nfails validation with a 422 and the message is never recorded. Twilio does\nnot retry inbound webhooks on 4xx, so those messages were lost outright\n(VM-10490). The legacy Flask route read `request.values[\"Body\"]`, which\nreturned the empty string, hence the regression on migration.\n\nTo/From/MessageSid stay required: Twilio always sends them, so a request\nwithout them is malformed rather than media-only.","operationId":"twilio_sms_api_twilio_sms_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_twilio_sms_api_twilio_sms_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/twilio/sms/status":{"post":{"summary":"Twilio Sms Status","description":"Webhook for Twilio SMS delivery-status callbacks.","operationId":"twilio_sms_status_api_twilio_sms_status_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_twilio_sms_status_api_twilio_sms_status_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/create_psr":{"post":{"summary":"Create Psr","description":"The InsureCare API: Medline posts a supply request, we create the order.\n\nThe body is taken as a plain dict rather than a typed `CreatePSR` so that\nvalidation stays in `create_psr_common`: Medline's published contract answers\na bad payload with the `CreatePSRResponse` envelope, which FastAPI's own 422\nwould replace, and the raw request is logged before validation runs. A dict\n(not `Any`) because FastAPI hands the parameter raw bytes when the content\ntype isn't JSON, which the logging of the body can't serialize.\n\nThe api-key check is skipped in development so the endpoint can be exercised\nlocally without the shared secret.","operationId":"create_psr_create_psr_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request Json"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sandbox/create_psr":{"post":{"summary":"Create Psr Sandbox","description":"Medline's sandbox route: creates the order against the demo hospital.\n\nValidates against the running environment's sandbox key -- a key of its own,\nseparate from the live create_psr key -- and there is no development bypass.","operationId":"create_psr_sandbox_sandbox_create_psr_post","parameters":[{"name":"x-api-key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Request Json"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/refills-phone-tree/await-pickup":{"post":{"summary":"Refills Await Pickup","description":"TwiML the outbound refill call starts on, holding it open until answered.","operationId":"refills_await_pickup_refills_phone_tree_await_pickup_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/refills-phone-tree/call-status":{"post":{"summary":"Refills Call Status","description":"Twilio status + async-AMD callback that routes the answered call.\n\nAnswers with an empty 200 rather than TwiML: Twilio ignores the body of a\nstatus callback, so the call is redirected through the REST API instead.","operationId":"refills_call_status_refills_phone_tree_call_status_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_refills_call_status_refills_phone_tree_call_status_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/refills-phone-tree/initial":{"post":{"summary":"Refills Initial","description":"The main refill menu: refill (1), no refill (2), representative (3).","operationId":"refills_initial_refills_phone_tree_initial_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/refills-phone-tree/voicemail":{"post":{"summary":"Refills Voicemail","description":"Records that the call reached a machine and plays the voicemail message.","operationId":"refills_voicemail_refills_phone_tree_voicemail_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/refills-phone-tree/menu":{"post":{"summary":"Refills Menu","description":"Acts on the main-menu selection.\n\n`Digits` is required, so a gather that captured nothing gets a 422 where the\nlegacy Flask route's `request.form[\"Digits\"]` raised a 400.","operationId":"refills_menu_refills_phone_tree_menu_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_refills_menu_refills_phone_tree_menu_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/refills-phone-tree/menu-confirmation":{"post":{"summary":"Refills Menu Confirmation","description":"Acts on the refill-scope selection: full refill, representative, or start over.","operationId":"refills_menu_confirmation_refills_phone_tree_menu_confirmation_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_refills_menu_confirmation_refills_phone_tree_menu_confirmation_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/refills-phone-tree/change-confirmation-no":{"post":{"summary":"Refills Change Confirmation No","description":"Cancels the declined refill and tells the patient it is cancelled.","operationId":"refills_change_confirmation_no_refills_phone_tree_change_confirmation_no_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/refills-phone-tree/change-confirmation-yes":{"post":{"summary":"Refills Change Confirmation Yes","description":"Activates the confirmed refill and tells the patient it is on the way.","operationId":"refills_change_confirmation_yes_refills_phone_tree_change_confirmation_yes_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/vive_webhook/qty_updated":{"post":{"summary":"Vive Qty Updated Webhook","description":"Webhook for Vive on-hand quantity changes, fired per variant.","operationId":"vive_qty_updated_webhook_vive_webhook_qty_updated_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/vive_webhook/product_archived":{"post":{"summary":"Vive Product Archived Webhook","description":"Webhook for a Vive product being archived or re-listed.\n\nThe route is named for archiving but the payload carries an `active` flag,\nso Vive fires it on un-archive too.","operationId":"vive_product_archived_webhook_vive_webhook_product_archived_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/vive_webhook/item_shipped":{"post":{"summary":"Vive Item Shipped Webhook","description":"Webhook for a Vive shipment, carrying one tracking number.\n\nAnswers 200 even when the Vive order number matches nothing on file —\nthere's no useful retry from Vive's side, and replaying won't help.","operationId":"vive_item_shipped_webhook_vive_webhook_item_shipped_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"Body_refills_call_status_refills_phone_tree_call_status_post":{"properties":{"CallStatus":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callstatus"},"CallSid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callsid"},"AnsweredBy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Answeredby"}},"type":"object","title":"Body_refills_call_status_refills_phone_tree_call_status_post"},"Body_refills_menu_confirmation_refills_phone_tree_menu_confirmation_post":{"properties":{"Digits":{"type":"string","title":"Digits"}},"type":"object","required":["Digits"],"title":"Body_refills_menu_confirmation_refills_phone_tree_menu_confirmation_post"},"Body_refills_menu_refills_phone_tree_menu_post":{"properties":{"Digits":{"type":"string","title":"Digits"}},"type":"object","required":["Digits"],"title":"Body_refills_menu_refills_phone_tree_menu_post"},"Body_sr_fax_callback_sr_fax_callback_post":{"properties":{"FaxDetailsID":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Faxdetailsid"},"ErrorCode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Errorcode"}},"type":"object","title":"Body_sr_fax_callback_sr_fax_callback_post"},"Body_twilio_sms_api_twilio_sms_post":{"properties":{"To":{"type":"string","title":"To"},"From":{"type":"string","title":"From"},"MessageSid":{"type":"string","title":"Messagesid"},"Body":{"type":"string","title":"Body","default":""}},"type":"object","required":["To","From","MessageSid"],"title":"Body_twilio_sms_api_twilio_sms_post"},"Body_twilio_sms_status_api_twilio_sms_status_post":{"properties":{"MessageSid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Messagesid"},"MessageStatus":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Messagestatus"}},"type":"object","title":"Body_twilio_sms_status_api_twilio_sms_status_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}