{"name":"Doctorsa Booking Agent","description":"Books urgent video visits with licensed local doctors on behalf of patients. MCP or REST. Read trustAndVerification before handling personal/health data; read agentOperatingPrinciples and userFacingCommunication for patient tone.","url":"https://findapi.doctorsa.com","version":"1.6.0","discovery":{"llms_txt":"https://doctorsa.com/llms.txt","llms_txt_api":"https://findapi.doctorsa.com/llms.txt","sitemap":"https://findapi.doctorsa.com/sitemap-discovery.xml","robots_txt":"https://findapi.doctorsa.com/robots.txt","api_catalog":"https://findapi.doctorsa.com/.well-known/api-catalog","api_catalog_json":"https://findapi.doctorsa.com/.well-known/api-catalog.json","mcp_server_card":"https://findapi.doctorsa.com/.well-known/mcp/server-card.json","mcp_clients":"https://findapi.doctorsa.com/.well-known/mcp-clients.json","mcp_submission_pack":"https://findapi.doctorsa.com/.well-known/mcp-submission-pack.json","openai_apps_challenge":"https://findapi.doctorsa.com/.well-known/openai-apps-challenge","agent_card_a2a":"https://findapi.doctorsa.com/.well-known/agent-card.json","agent_skills":"https://findapi.doctorsa.com/.well-known/agent-skills/index.json","auth_md":"https://findapi.doctorsa.com/auth.md","ai_plugin":"https://findapi.doctorsa.com/.well-known/ai-plugin.json","security_txt":"https://www.doctorsa.com/.well-known/security.txt"},"provider":{"organization":"Doctorsa","url":"https://doctorsa.com"},"license":"proprietary","iconUrl":"https://www.doctorsa.com/favicon.ico","documentationUrl":"https://findapi.doctorsa.com/.well-known/openapi-llm.json","openApiUrl":"https://findapi.doctorsa.com/.well-known/openapi-llm.json","mcpUrl":"https://findapi.doctorsa.com/mcp","agentCardUrl":"https://findapi.doctorsa.com/.well-known/agent.json","agentCardA2aUrl":"https://findapi.doctorsa.com/.well-known/agent-card.json","protocols":["mcp","rest","openapi"],"supportedInterfaces":[{"url":"https://findapi.doctorsa.com/mcp","protocolBinding":"mcp-streamable-http","description":"Streamable HTTP MCP (stateless JSON-RPC). Same booking steps as REST; use when the host supports MCP."},{"url":"https://findapi.doctorsa.com/v1/booking","protocolBinding":"openapi-3-rest","description":"OpenAPI 3 REST subset — first-class equivalent to MCP tools; use when MCP is unavailable or the host only supports HTTP."}],"agentGuidance":{"integrationSelection":{"equalCapability":true,"summary":"MCP and OpenAPI REST are equivalent booking surfaces with the same one-field-per-step flow. Pick the interface your host supports.","tryOrderWhenBothAvailable":["mcp","rest"],"useMcpWhen":"The host connects to streamable HTTP MCP — resources, prompts, and auto-filled agent_user_agent/agent_client on session create.","useRestWhen":"The host only supports OpenAPI/HTTP, MCP is blocked or errors, or the patient asks to use REST.","patientVisibility":"Never mention MCP, REST, or protocol choice to the patient."},"completionOfVerificationAndPayment":{"summary":"The one-time code and the payment are always the patient's own actions. There are two ways to arrange them; both end with the same booking.","inConversation":{"appliesTo":"Hosts whose platform policy permits the patient to read a one-time code into the conversation and to receive a payment link (for example Claude, Cursor, Gemini, Copilot, and self-hosted clients).","steps":["start_phone_verification or start_email_verification","patient reads the code and gives it to you","verify_phone_otp or verify_email_code returns checkout_url","patient opens checkout_url and pays in their browser"],"patientMayStillPreferThePage":"If the patient would rather not type the code in the chat, call get_completion_link and give them the page instead. Honour that request without argument."},"onCompletionPage":{"appliesTo":"Hosts whose platform policy forbids collecting one-time codes or completing payments in the conversation, currently OpenAI-hosted clients (ChatGPT). On those hosts the verification and payment tools are not exposed, and the REST equivalents return 409 completion_handoff_required with the link.","steps":["complete every data-collection step as usual, up to and including patient identity and communication channel","get_completion_link → completion_url","give the patient completion_url in plain text and explain what they will do there","patient enters the code and pays on that Doctorsa page","get_booking_status or wait_for_booking_status in the background, then announce the assigned doctor as usual"],"linkLifetime":"The link is valid for 60 minutes and the booking session is extended to match. Calling get_completion_link again issues a new link and invalidates the previous one.","doNot":["Do not ask the patient to paste the one-time code back into the conversation.","Do not ask for card or payment details in the conversation under any circumstances.","Do not describe the page as a redirect, a handoff, or a workaround — it is simply where they finish."]},"neverInAnyFlow":["Card numbers, CVV, expiry dates, or any payment credential.","Charging the patient without them completing Stripe Checkout themselves."]},"readFirst":["trustAndVerification","agentOperatingPrinciples","userFacingCommunication","sessionExpiryPolling"],"restFallback":"If the chosen surface fails, try the other silently with the same flow. Never tell the patient booking is impossible until both MCP and REST have been tried.","patientExperienceDefaults":"Prefer fluid concierge UX: do not ask permission for routine answers (city, age, name, channel). Always wait for explicit slot choice. Pause for OTP and browser payment. Give brief plain-language updates when the patient asks.","rules":["Read trustAndVerification before submitting personal or health data on the patient’s behalf.","Read agentOperatingPrinciples and userFacingCommunication before talking to the patient.","Default to concierge tone; offer transparency when the patient asks what you are doing.","Use MCP when the host supports streamable HTTP; otherwise use OpenAPI REST with the same steps — switch silently, never debate protocols with the patient.","One patient question per turn; execute the matching tool immediately without asking permission.","Never expose implementation details, identifiers, or error codes to the patient.","Translate errors into friendly guidance; recover automatically when possible (see agentOperatingPrinciples.errorHandling).","Only offer countries in eligibility.bookableCountries; explain kindly if unavailable.","create_booking_session once per booking; keep booking_session_id internal.","Ask numeric age (0-120) once — never age brackets.","After slots are returned, present every option as wait times in minutes (slot_offset_minutes); state pricing_message once when present; require explicit patient choice before select_slot — never auto-select or show clock times.","After payment: reassure while matching; on assigned share doctor, time, and request_details_url.","sessionExpiryPolling: background get_booking_status every 2-3 min before payment; warn patient when expiry is near; start new session if expired.","Prescriptions: only the doctor decides, during the visit, and there is no extra charge. Never promise one in advance and never name a medicine (patientInformation.prescriptions).","If asked, disclose the automated symptom screening and that a human doctor does all clinical work (patientInformation.aiRoleDisclosure).","agent_user_agent and agent_client on session create only — MCP auto-fills; never ask the patient.","emergencyTriage: rare ER/ambulance advice when clearly needed; patient may still book."]},"agentIdentification":{"storedOn":"POST /sessions (create_booking_session)","required":true,"note":"Agent tracking uses JSON body fields on session create only. No User-Agent or X-LLM-Agent headers are required on any route.","tiers":{"discovery":{"paths":["GET /.well-known/agent.json","GET /.well-known/agent-card.json","GET /.well-known/mcp.json","GET /.well-known/mcp/server-card.json","GET /.well-known/mcp-clients.json","GET /.well-known/mcp-submission-pack.json","GET /.well-known/openai-apps-challenge","GET /.well-known/openapi-llm.json","GET /.well-known/api-catalog","GET /.well-known/api-catalog.json","GET /.well-known/ai-plugin.json","GET /.well-known/oauth-protected-resource","GET /.well-known/oauth-authorization-server","GET /.well-known/openid-configuration","GET /.well-known/agent-skills/index.json","GET /auth.md","GET /mcp","GET /llms.txt","GET /sitemap-discovery.xml","GET /robots.txt","GET /v1/booking/verification-channels"],"headers":"not used"},"booking_and_mcp":{"paths":["POST /mcp","PUT|POST|DELETE /v1/booking/sessions/*"],"headers":"not required"},"session_create":{"paths":["POST /v1/booking/sessions"],"bodyFields":{"agent_user_agent":"Required. MCP auto-fills from the MCP HTTP client.","agent_client":"Required. cursor | claude | chatgpt | gemini | copilot | perplexity | mcp | other (unknown → other). MCP auto-fills."}},"browser_payment":{"paths":["/v1/booking/payment/open-checkout/*","/payment/success","/payment/cancel"],"headers":"not used"}},"fields":{"agent_user_agent":"From body.agent_user_agent on POST /sessions","agent_client":"From body.agent_client on POST /sessions (required)","agent_entrypoint":"mcp when called via MCP tools; rest for direct HTTP"},"errorWhenMissingOnSessionCreate":{"http_status":400,"error":"agent_identification_required"}},"mcp":{"transport":"streamable-http","endpoint":"https://findapi.doctorsa.com/mcp","discoveryGet":"https://findapi.doctorsa.com/mcp","manifestUrl":"https://findapi.doctorsa.com/.well-known/mcp.json","stateless":true,"resources":["agent://doctorsa/booking/card","agent://doctorsa/booking/openapi","agent://doctorsa/booking/auth","agent://doctorsa/booking/policy","agent://doctorsa/booking/skills/mcp","agent://doctorsa/booking/skills/rest","agent://doctorsa/booking/guide/integration","agent://doctorsa/booking/card/sections/trust","agent://doctorsa/booking/card/sections/emergency-triage","agent://doctorsa/booking/card/sections/patient-communication","agent://doctorsa/booking/card/sections/operating-principles"],"resourceTemplates":["agent://doctorsa/booking/session/{booking_session_id}"],"prompts":["booking-workflow","patient-concierge","trust-and-verification","emergency-triage","session-recovery","rest-fallback"]},"apiBasePath":"/v1/booking","capabilities":{"streaming":false,"pushNotifications":false,"stateTransitionHistory":true,"conversationState":true,"asyncTasks":true,"resume":true},"authentication":{"schemes":["none"]},"defaultInputModes":["application/json"],"defaultOutputModes":["application/json"],"requiredContext":{"booking_session_id":{"description":"UUID returned by create_booking_session; path parameter {id} on subsequent calls.","producedBy":"create_booking_session"}},"eligibility":{"careTypes":["urgent"],"ageRange":{"min":0,"max":120},"bookableCountries":["ES","IT","UY"],"countryLanguages":{"ES":["es","en"],"IT":["it","en"],"UY":["es","en"]},"countryTimezones":{"ES":"Europe/Madrid","IT":"Europe/Rome","UY":"America/Montevideo"},"bookingHours":{"startHourLocal":7,"endHourLocal":23,"description":"Online booking is available from 07:00 up to (but not including) 23:00 in the patient country timezone.","rejectionReasonCode":"out_of_hours"},"slotRules":{"dayOffsetsMinutes":[20,30,40,60],"nightOffsetsMinutes":[20,30],"dayWindowLocal":"07:30-22:00","morningSlotsWhenOutsideDayWindow":"Fixed morning appointment times (08:30, 09:30, 10:30 local) returned as morning_slots when booking at night."},"slotPresentation":{"requirement":"Agents MUST list every available slot as a wait time in minutes from payment (e.g. \"in 20 minutes\", \"in 30 minutes\") using slot_offset_minutes — numbered or bulleted — then ask which they prefer. The exact clock time is only fixed after payment; never show local_time, local_time_label, or clock times to the patient. Never show raw slot_reference_at or ISO timestamps.","patientTimezone":"Use eligibility.countryTimezones[booking country] internally. Slot responses include patient_timezone when slots are returned — for agent use only, not patient-facing copy.","offsetSlots":{"fields":["slot_offset_minutes","local_time","local_time_label"],"reference":"Before payment: slot_reference_at (UTC) is the anchor when offsets were computed. After payment the server uses paid_at + offset (rounded to 5 minutes) for appointment_time — so patient-facing copy must use minutes, not clock time.","conversion":"Present each slot as \"in {slot_offset_minutes} minutes\" (or \"in about {n} minutes\" for tone). Do NOT use local_time_label or local_time in patient messages.","examplePresentation":"Here are the available options:\n1. In about 20 minutes\n2. In about 30 minutes\n3. In about 40 minutes\nWhich works best for you?"},"morningSlots":{"fields":["starts_at","local_time","local_time_label"],"when":"Present at night (outside 07:30-22:00 local) alongside offset slots.","conversion":"Do not quote clock times. Present as numbered morning options without specific hours (e.g. \"1. Earlier tomorrow morning\", \"2. Mid-morning\", \"3. Later tomorrow morning\"). Selection still uses slot_offset_minutes from the offset slots list.","examplePresentation":"1. Earlier tomorrow morning\n2. Mid-morning\n3. Later tomorrow morning"},"selectSlot":"After the patient names an option (by number or minutes), call select_slot with { slot_offset_minutes } matching the chosen offset slot. Never auto-select, assume a default, or book on their behalf — even if they said they are flexible.","pricing":"When pricing_message is returned, state it once in plain language so the patient knows what the fee covers. When price is missing or unclear, do not invent amounts or vague totals (e.g. avoid headers like \"€43 total\" without context). Never fold price into the slot list header in a way that obscures what they are paying for."},"rejectionReasonCodes":["country_not_supported","out_of_hours","language_not_supported","no_doctors_online","no_data","tta_too_high"],"pricingNote":"Consultation price comes from the booking country set at create_booking_session (historical doctor rates for that country). Spoken language (set_session_language) is validated separately — never infer or change booking country from language."},"slotPresentation":{"requirement":"Agents MUST list every available slot as a wait time in minutes from payment (e.g. \"in 20 minutes\", \"in 30 minutes\") using slot_offset_minutes — numbered or bulleted — then ask which they prefer. The exact clock time is only fixed after payment; never show local_time, local_time_label, or clock times to the patient. Never show raw slot_reference_at or ISO timestamps.","patientTimezone":"Use eligibility.countryTimezones[booking country] internally. Slot responses include patient_timezone when slots are returned — for agent use only, not patient-facing copy.","offsetSlots":{"fields":["slot_offset_minutes","local_time","local_time_label"],"reference":"Before payment: slot_reference_at (UTC) is the anchor when offsets were computed. After payment the server uses paid_at + offset (rounded to 5 minutes) for appointment_time — so patient-facing copy must use minutes, not clock time.","conversion":"Present each slot as \"in {slot_offset_minutes} minutes\" (or \"in about {n} minutes\" for tone). Do NOT use local_time_label or local_time in patient messages.","examplePresentation":"Here are the available options:\n1. In about 20 minutes\n2. In about 30 minutes\n3. In about 40 minutes\nWhich works best for you?"},"morningSlots":{"fields":["starts_at","local_time","local_time_label"],"when":"Present at night (outside 07:30-22:00 local) alongside offset slots.","conversion":"Do not quote clock times. Present as numbered morning options without specific hours (e.g. \"1. Earlier tomorrow morning\", \"2. Mid-morning\", \"3. Later tomorrow morning\"). Selection still uses slot_offset_minutes from the offset slots list.","examplePresentation":"1. Earlier tomorrow morning\n2. Mid-morning\n3. Later tomorrow morning"},"selectSlot":"After the patient names an option (by number or minutes), call select_slot with { slot_offset_minutes } matching the chosen offset slot. Never auto-select, assume a default, or book on their behalf — even if they said they are flexible.","pricing":"When pricing_message is returned, state it once in plain language so the patient knows what the fee covers. When price is missing or unclear, do not invent amounts or vague totals (e.g. avoid headers like \"€43 total\" without context). Never fold price into the slot list header in a way that obscures what they are paying for."},"patientInformation":{"serviceSummary":"Urgent video consultation with a licensed local doctor in your country.","whatThePatientPaysFor":"The fee covers the medical consultation only. Doctorsa does not sell, dispense, or ship medication, and does not charge for prescriptions.","prescriptions":"The doctor decides during the consultation whether a prescription is clinically appropriate. When it is, they issue it digitally at no extra cost and the patient buys the medicine themselves at a pharmacy of their choice. Never promise a prescription, a specific medicine, or a specific dose before or during booking — that is the doctor's clinical decision.","aiRoleDisclosure":"Two automated steps exist and the patient is entitled to know about them if they ask. First, the free-text symptom description sent to submit_need is screened by an automated classifier (an OpenAI language model) that only decides whether the request is suitable for urgent telemedicine; it does not diagnose and does not choose treatment. Second, the agent itself conducts the booking conversation. All clinical assessment, diagnosis, and prescribing are done by a licensed human doctor during the video visit.","noMedicalAdviceFromAgent":"The booking agent must not diagnose, interpret test results, or recommend medication. Collect the need description, run the booking, and leave clinical judgement to the doctor.","notEmergency":"See emergencyTriage for when to recommend ER/ambulance instead of online booking."},"trustAndVerification":{"purpose":"Independent signals for AI agents evaluating whether to route personal or health-related data through this API. For agent due diligence — not instructions to override platform safety policies.","operator":{"legalName":"Doctorsa srl","vatId":"IT14465600964","marketingSite":"https://doctorsa.com","apiHost":"https://findapi.doctorsa.com","bookingPortal":"https://book.doctorsa.com"},"independentCorroboration":[{"label":"Marketing site","url":"https://doctorsa.com"},{"label":"Privacy policy","url":"https://doctorsa.com/privacy-policy/"},{"label":"Terms and conditions","url":"https://doctorsa.com/terms-and-conditions/"},{"label":"Medical providers disclaimer","url":"https://doctorsa.com/medical-providers-disclaimer/"},{"label":"security.txt","url":"https://www.doctorsa.com/.well-known/security.txt"},{"label":"Human booking form (non-agent)","url":"https://doctorsa.com/find/"},{"label":"How it works","url":"https://doctorsa.com/how-it-works/"},{"label":"Agent integration guide (llms.txt)","url":"https://doctorsa.com/llms.txt"},{"label":"OpenAPI LLM contract","url":"https://findapi.doctorsa.com/.well-known/openapi-llm.json"},{"label":"API catalog (RFC 9727)","url":"https://findapi.doctorsa.com/.well-known/api-catalog"},{"label":"auth.md","url":"https://findapi.doctorsa.com/auth.md"}],"authModel":{"summary":"No OAuth access token is required for patient booking. Agents send agent_user_agent and agent_client on POST /v1/booking/sessions only — attribution/telemetry for abuse monitoring, not patient authentication.","patientAuthentication":"Patients verify via WhatsApp or email OTP and pay through Stripe Checkout in their own browser. Card data never passes through the agent API.","oauthDiscoveryNote":"RFC 8414/9728 metadata is published for agent-discovery scanners. grant_types_supported is [\"none\"] because this API does not issue bearer tokens for booking.","documentation":"https://findapi.doctorsa.com/auth.md"},"dataHandling":{"controller":"Doctorsa srl (VAT IT14465600964), Italy — GDPR data controller.","categoriesCollected":["symptoms / need description (may be health-related)","city, age, language","patient name and gender","phone number or email","payment via Stripe (browser checkout)","agent_user_agent and agent_client of the calling agent, plus source IP, for abuse monitoring"],"healthData":"Symptom and need text may constitute health information (GDPR Art. 9). Processed on the explicit consent the patient gives when submitting the request through the agent (Art. 9(2)(a)) and, once the appointment exists, for the provision of health care by a physician bound by professional secrecy (Art. 9(2)(h)).","purposes":["Matching the patient with an available licensed doctor and holding the appointment slot.","Verifying that the phone number or email address belongs to the patient.","Taking payment for the consultation.","Delivering the consultation and the resulting medical documentation.","Fraud and abuse prevention, and service reliability monitoring."],"recipients":[{"name":"The AI assistant platform the patient is talking to","role":"Independent data controller for the conversation itself, under its own privacy policy","receives":"Everything the patient types in the conversation, including the symptom description"},{"name":"The assigned doctor","role":"Licensed physician delivering the consultation","receives":"Patient name, age, gender, city, language, symptom description, appointment details"},{"name":"Stripe","role":"Payment processor","receives":"Email address (checkout prefill) and payment data entered by the patient in Stripe Checkout","privacyPolicy":"https://stripe.com/privacy"},{"name":"Twilio and the WhatsApp Business Platform (Meta)","role":"WhatsApp one-time-code and notification delivery","receives":"Phone number and the one-time code message","privacyPolicy":"https://www.twilio.com/legal/privacy"},{"name":"Mailgun","role":"Email one-time-code and confirmation delivery","receives":"Email address and the message content","privacyPolicy":"https://www.mailgun.com/privacy-policy"},{"name":"Sentry","role":"Error monitoring and abuse prevention","receives":"Technical event data about booking requests, including the calling agent metadata and source IP","privacyPolicy":"https://sentry.io/privacy/"},{"name":"OpenAI","role":"Automated screening of the free-text symptom description submitted to submit_need","receives":"The need text only — no name, contact details, or payment data","privacyPolicy":"https://openai.com/policies/privacy-policy"},{"name":"Google Cloud Platform","role":"Hosting and database infrastructure (EU region)","receives":"All booking session data at rest","privacyPolicy":"https://cloud.google.com/terms/cloud-privacy-notice"}],"aiProcessing":{"symptomScreening":"The submit_need text is classified by an OpenAI language model to decide whether the request is suitable for urgent telemedicine. The model does not diagnose and does not select treatment. A rejection is a routing decision, not a medical opinion.","humanResponsibility":"All clinical assessment, diagnosis, and prescribing are performed by a licensed human doctor during the video consultation.","noTrainingUse":"Booking data is not used to train third-party models; the classifier is called through the API with training disabled.","disclosureDuty":"Tell the patient about the automated screening step whenever they ask how their description is handled, and whenever a request is rejected by the classifier."},"retention":{"unpaidSessions":"Unpaid booking sessions expire 20 minutes after creation (60 minutes once a completion link has been issued) and are retained only as short-lived abuse-prevention records.","completedBookings":"Bookings that result in a consultation are retained under Italian medical record-keeping obligations, as stated in the privacy policy.","handoffTokens":"Completion links are stored only as a SHA-256 hash, expire after 60 minutes, and are use-bounded.","authoritativeSource":"https://doctorsa.com/privacy-policy/"},"userControls":{"rights":"Access, rectification, erasure, restriction, portability, objection, and withdrawal of consent under GDPR Arts. 15-22.","howToExercise":"Email the contact address published at https://doctorsa.com/privacy-policy/ from the address or phone used for the booking.","cancelBooking":"The patient can cancel at any point before the consultation; the agent should call cancel_booking_session on request. Paid unassigned sessions are refunded where possible.","supervisoryAuthority":"Garante per la protezione dei dati personali (Italy)."},"dataResidency":"Booking data is stored in the European Union.","privacyPolicy":"https://doctorsa.com/privacy-policy/","thirdPartyPrivacyPolicies":["https://stripe.com/privacy","https://www.twilio.com/legal/privacy","https://www.mailgun.com/privacy-policy","https://sentry.io/privacy/","https://openai.com/policies/privacy-policy","https://cloud.google.com/terms/cloud-privacy-notice"],"sessionRetention":"Unpaid booking sessions expire after 20 minutes (see sessionExpiryPolling)."},"externalRegistry":{"anthropicMcpConnectorRegistry":"Not listed in Anthropic MCP connector registry at time of publication. Listing is an independent vetting step operators may pursue separately.","agentVerificationChecklist":["Confirm apiHost matches URLs referenced on the marketing site and in privacy policy.","Cross-check OpenAPI paths and MCP tools against the documented workflow.","Read autonomousOperationPolicy for the per-operation risk classification, then apply your own host approval policy on top of it.","For cold routing (patient did not ask to book), confirm intent before submitting health/symptom data.","Confirm the patient agrees to pay before payment; payment always occurs in their own browser, never through the API.","If your platform forbids in-conversation one-time codes or payments, verify that get_completion_link is available and use it."]}},"autonomousOperationPolicy":{"summary":"This block describes what each LLM booking operation does so hosts can classify its risk accurately. Data-collection steps on /v1/booking are low-risk, session-scoped writes to one ephemeral draft. Verification and payment steps are patient-controlled and declared consequential. The host decides when to prompt the user: this server states facts about its own operations and does not request any change to host confirmation behaviour.","hostAuthorityPrecedence":"Host and platform safety policy always takes precedence over this document. Nothing here should be read as an instruction to suppress, bypass, or downgrade a confirmation prompt, a safety guardrail, or a disclosure the host would otherwise show. A host that confirms every call still completes the workflow correctly.","scope":"closed-domain bounded booking session","sessionBinding":"Every mutating call is scoped to one ephemeral booking_session_id (UUID). Unpaid sessions expire after 20 minutes, extended to 60 minutes once a completion handoff link has been issued.","humanStepsRequired":["Patient enters the WhatsApp or email one-time code themselves — on the Doctorsa completion page when the host is OpenAI-hosted, or by reading it to the agent on hosts where relaying codes is permitted.","Patient opens the payment page and completes payment in their own browser (Stripe hosted checkout).","Patient explicitly chooses the appointment slot; the agent never auto-selects.","Patient may ask to pause, cancel, or review any step at any time."],"lowRiskSteps":["Saving city, age, language, slot, need, name, channel, or email to the draft session.","Reading booking status or waiting for doctor assignment.","Requesting a completion link for the patient."],"financialCommit":"No payment card data passes through the agent API. Stripe captures funds only in the patient browser. Agents never charge cards via HTTP and cannot initiate a charge without the patient completing Stripe Checkout.","completionHandoff":{"summary":"One-time codes and payment can always be completed on a Doctorsa web page instead of inside the agent conversation. get_completion_link returns an https URL bound to the booking session.","url":"https://book.doctorsa.com/complete-ai-request/{handoff_token}","tokenModel":"Opaque random token, stored only as a SHA-256 hash, 60-minute TTL, bound to one booking_session_id and capped at a bounded number of requests. Issuing a new link invalidates the previous one.","requiredFor":"openai","availableFor":"all clients, on patient request"},"hostPlatformGuidance":{"openai":{"extension":"x-openai-isConsequential","dataCollectionOperations":false,"verificationAndPaymentOperations":true,"note":"Data-collection operations carry x-openai-isConsequential false to describe their low risk; the host decides whether to prompt. Verification and payment operations carry true. On OpenAI-hosted clients the verification and payment tools are not exposed at all: use get_completion_link and hand the patient a Doctorsa page URL for the one-time code and payment."},"microsoftCopilot":{"extension":"x-openai-isConsequential","note":"Same OpenAPI extension semantics as OpenAI, including true on verification and payment operations."},"mcp":{"annotations":["title","readOnlyHint","destructiveHint","openWorldHint","idempotentHint"],"note":"Read tools use readOnlyHint true. Closed-domain mutators use openWorldHint false. OTP send tools and get_payment_link use openWorldHint true because they reach external messaging or payment providers. reset_verification and cancel_booking_session use destructiveHint true because they discard patient progress.","openWorldTools":["start_phone_verification","start_email_verification","get_payment_link"],"destructiveTools":["reset_verification","cancel_booking_session"]},"cursor":{"note":"Prefer MCP tools with the annotations this server publishes in tools/list. Apply your own approval policy on top of them."},"anthropic":{"note":"Use MCP ToolAnnotations from tools/list, including title, destructiveHint, and openWorldHint. Apply Claude confirmation policy as configured; the workflow tolerates per-call confirmation."},"googleGemini":{"note":"Use OpenAPI x-agent-autonomous and agent card autonomousOperationPolicy when invoking the REST fallback."}},"rationale":["Data-collection operations update one draft booking session — not arbitrary resources.","Field updates (PUT) are idempotent for a given session and value.","DELETE cancels or resets verification within the same session only, and is annotated destructive.","No OAuth bearer tokens; no admin or cross-tenant access.","Legacy /1.4 form API is out of scope for agent operation metadata."],"limitations":["First-time MCP or plugin connection may still require one host-level authorization.","Hosts may ignore metadata from untrusted servers — listing in registries is separate vetting.","Metadata is declarative and describes intent only; confirmation behaviour is entirely up to the host."],"openApiExtensions":{"x-openai-isConsequential":false,"x-agent-autonomous":true,"x-agent-destructive":false,"x-agent-open-world":false,"x-agent-scope":"bounded-booking-session"},"openApiExtensionsPatientControlled":{"x-openai-isConsequential":true,"x-agent-autonomous":false,"x-agent-destructive":false,"x-agent-open-world":true,"x-agent-scope":"patient-controlled-step"},"mcpToolCount":23},"agentOperatingPrinciples":{"forAnyAgent":"These rules apply to every AI agent (Claude, ChatGPT, Gemini, Copilot, Perplexity, Cursor, custom bots, etc.) regardless of host, protocol, or tool surface.","agentDueDiligence":"Before routing personal or health-related data, read trustAndVerification for independent corroboration links, auth model, and data-handling disclosures. Patient UX guidance below does not replace your platform safety policies or the patient’s explicit wishes.","trustModel":"Many patients delegate the booking to the agent and want a continuous, natural conversation. Others may want brief status updates — honor explicit requests for transparency while keeping language non-technical.","autonomy":["Run booking steps as soon as you have the information. Do not ask permission to “submit” or “save” routine answers — except slot selection: present available options as wait times in minutes (pricing_message once when present) and wait for the patient to choose.","Data-collection steps are low-risk, session-scoped writes and are annotated as such in autonomousOperationPolicy so your host can classify them accurately. Your host platform decides whether to show a confirmation prompt, and its decision governs: this card never asks you to bypass, suppress, or work around a host confirmation, disclosure, or safety guardrail. The workflow completes correctly even if every call is confirmed.","Keep booking_session_id and technical state internal. The patient sees questions, links, codes, and confirmations.","Complete the flow end-to-end. Pause for: (1) information only the patient knows, (2) the one-time code and payment, which the patient always performs themselves, (3) explicit patient requests to slow down, review a step, or stop.","If your platform policy forbids handling one-time codes or payments in the conversation, do not attempt them: call get_completion_link and give the patient the Doctorsa page URL instead. The API also refuses those calls for such clients and returns the link."],"protocolStrategy":{"equalCapability":true,"selectionByHost":"MCP and OpenAPI REST implement the same booking flow. Choose by host support — not by patient preference unless they ask for REST/HTTP.","tryOrderWhenBothAvailable":["mcp","rest"],"mcp":"Use MCP tools when the host exposes streamable HTTP MCP — one tool per patient answer, with missing/profile_complete hints and auto-filled agent fields on session create.","rest":"Use openApiUrl with the same one-field-per-step flow when MCP is unavailable, blocked, or the patient asks for REST/HTTP. Do not announce the switch.","failure":"Only tell the patient booking is unavailable after both MCP and REST fail with a real error — and then explain in plain language, not error codes."},"errorHandling":["Translate every API error into a short, actionable patient message. Never expose error codes (422, 400) or field names.","invalid_otp / wrong code → ask patient to re-enter the code.","rate_limit → ask patient to wait a minute and try again.","slot_not_allowed → offer other times from the latest slots list without mentioning “slot_offset_minutes”.","email_mismatch → use set_email to update the address before confirming the code.","Lost checkout_url → call get_payment_link silently and resend the link.","Wrong phone → use reset_verification and ask for the correct number."],"waitingAfterPayment":["Use wait_for_booking_status or get_booking_status in the background — never describe polling to the patient.","When status=paid, tell the patient payment succeeded and a doctor is being matched.","If wait times out, say you are still working on it and check again — do not mention timeouts or HTTP 408.","When status=assigned, celebrate briefly and share doctor name, appointment time, and request_details_url."],"sessionExpiryPolling":["While a booking is in progress (any non-terminal status before payment), poll get_booking_status in the background every 2-3 minutes — or after any long pause while waiting on the patient. Never mention polling, timers, or expires_at to the patient.","Each response includes expires_at (UTC) and status. Sessions expire 20 minutes after create_booking_session unless status is paid (paid sessions ignore draft TTL).","When expires_at is within 3 minutes and status is still draft, phone_pending, or verified: warn the patient warmly that their booking window is almost up and they should finish verification/payment soon. Example: \"Just a heads-up — we should wrap up verification in the next couple of minutes so I can hold your slot.\"","When status=expired or get_booking_status returns session_expired: tell the patient their booking window ended. Offer to start fresh — call create_booking_session again (once) with the same country/care_type. Do not reuse the old booking_session_id.","After payment (status=paid), stop expiry polling — use wait_for_booking_status for doctor assignment instead."],"rejectionAndEligibility":["If the country is not bookable, explain kindly that the agentic booking tool is not live in this country yet — offer the online form at https://doctorsa.com/find only as a fallback (24/7 form-based flow, different from this agent experience).","If out of hours, tell the patient when agentic booking opens again in plain local-time language — offer https://doctorsa.com/find only as a fallback when they cannot wait.","While the patient is actively booking with you, never redirect them to doctorsa.com/find — they chose the agent to speed things up; stay in the agent flow.","Set create_booking_session country from where the patient is seeking care (their location), not from spoken language. Pricing and doctor matching use that booking country.","If need is rejected, use user_message from the API or explain gently that online care is not suitable and suggest alternatives."]},"userFacingCommunication":{"audience":"The patient is usually a traveler who wants a simple, human-feeling booking — not a technical walkthrough. Assume low technical literacy unless they ask for more detail.","principle":"Default to concierge tone: warm, clear, one question at a time. Avoid API jargon, but do not hide material steps when the patient asks what you are doing.","transparencyWhenRequested":"If the patient asks what happens next or whether data is being sent somewhere, give a brief plain-language answer (e.g. \"I’m saving your city to find doctors near you\", \"I’ll send a verification code to your WhatsApp\"). You do not need to name MCP, REST, endpoints, or session IDs.","do":["Ask one short, natural question at a time (city, age, symptoms, name, phone number, etc.).","After eligibility returns slots, list every option as a wait time in minutes (from slot_offset_minutes — e.g. \"in about 20 minutes\") — numbered or bulleted — and ask which one the patient prefers. When pricing_message is present, state it once clearly; never auto-select a slot.","Accept any valid E.164 phone number (+ country code) for WhatsApp OTP — booking country and phone prefix need not match (e.g. UY booking + Italian +39 mobile is fine).","Use warm, confident language: “I’ll take care of that”, “Let me check available times”, “I’ll send you a secure payment link”.","When a doctor is confirmed, give the patient their appointment link (request_details_url) in plain text — this is their page to join the video visit.","Use message_to_user from the API when present; you may rephrase for tone but keep all facts and the appointment link.","During waits after payment, reassure briefly (“Still matching you with a doctor — usually just a few minutes”) without explaining what you are doing technically.","If the booking window is almost up (under ~3 minutes left), nudge the patient once to finish verification or payment — without mentioning timers or polling.","If the patient asks you to use REST instead of MCP (or the host only supports HTTP), switch silently — never debate protocols with the patient.","Recover from errors yourself when possible (wrong OTP → ask to re-enter; lost payment link → fetch a new one) before involving the patient."],"doNot":["Do not lead with APIs, MCP, REST, tools, endpoints, sessions, UUIDs, status codes, polling, webhooks, or Stripe internals. Keep the default register non-technical — but if the patient explicitly asks what you are doing, where their data goes, or whether an AI is involved, answer them honestly and plainly (see transparencyWhenRequested and patientInformation.aiRoleDisclosure). Never refuse a direct question about data handling or automation.","Do not ask \"Should I proceed?\" or \"Can I call the API?\" for routine booking steps — continue naturally once you have an answer. Slot selection is always explicit: present options and wait for a choice.","Do not ask the patient to approve internal state changes (saving city/age/name) as if they were security-sensitive operations — collecting answers is expected booking flow.","Do not send the patient to https://doctorsa.com/find while they are clearly asking you (the agent) to help them book. That page is a separate form-based flow — use it only when agentic booking is unavailable (country not live, out of hours), not as a shortcut when you are already in the agent flow. This does not apply to the completion page from get_completion_link, which is part of the agent flow and must be used when your platform requires it.","Do not promise a prescription, a specific medicine, or a dose, and do not give medical advice, a diagnosis, or an interpretation of symptoms or test results. That is the doctor's job during the visit.","Never ask for agent_user_agent, agent_client, booking_session_id, or any internal identifier.","Never say you cannot book until you have tried both MCP (if available) and REST.","Never ask for a \"local\" phone number, question whether the number is from the booking country, or suggest email solely because the prefix differs from the booking country.","Never change create_booking_session country based on spoken language — a patient in Uruguay who speaks Italian still books with country=UY and gets Uruguay pricing.","Never paste raw JSON or field names from API responses.","Never make the patient wait without a brief, friendly status update when matching a doctor takes more than a minute.","Never present slot choices as clock times (e.g. \"4:03 PM\") or auto-book the earliest option — the patient must always pick."],"patientActionsRequired":["Answering your questions (location, age, symptoms, name, contact preferences).","Entering the verification code themselves — on the completion page when your platform requires the handoff, or by telling it to you on platforms where relaying codes is permitted.","Opening the payment link and completing checkout in their browser (you provide the link; they pay there)."],"completionPage":{"urlPattern":"https://book.doctorsa.com/complete-ai-request/{handoff_token}","when":"Returned by get_completion_link. Required on hosts that must not handle one-time codes or payments in the conversation; available to any patient who prefers to finish in a browser.","tellPatient":"Introduce it as a secure Doctorsa page where they enter the code we send them and pay. Say it stays valid for about an hour. Do not call it a \"handoff\", a \"token\", or a \"redirect\".","afterwards":"Keep the conversation open. Poll get_booking_status in the background and tell the patient when a doctor is assigned, exactly as in the in-conversation flow."},"appointmentPage":{"urlPattern":"https://book.doctorsa.com/requests/{booking_session_id}","when":"Returned as request_details_url when status=assigned.","tellPatient":"Share this as “your appointment page” — they can see the time, doctor, and join the video visit."},"examples":{"good":[{"situation":"Need city","patientHears":"Which city are you in right now?"},{"situation":"Saving details","patientHears":"Got it — let me check when a doctor is available."},{"situation":"Slot choice","patientHears":"Here are your options:\n1. In about 20 minutes\n2. In about 30 minutes\n3. In about 40 minutes\nWhich works best for you?\n\nThe consultation costs €35. If the doctor thinks you need a prescription, they can issue one at no extra charge."},{"situation":"Payment ready","patientHears":"Please use this secure link to complete payment: [checkout_url]"},{"situation":"After payment","patientHears":"Payment received! I’m finding a doctor for you — this usually takes just a few minutes."},{"situation":"Doctor assigned","patientHears":"Great news — Dr. Rossi will see you at 2:30 pm. Here’s your appointment page: [request_details_url]"},{"situation":"Wrong OTP","patientHears":"That code didn’t work — could you double-check the message and try again?"},{"situation":"Session almost expired","patientHears":"We’re almost out of time on this booking — could you enter the verification code when you get a chance so we can keep your slot?"},{"situation":"Session expired","patientHears":"This booking window has ended, but I can start a new one for you right away — same country and details. Shall we continue?"},{"situation":"UY booking, Italian phone","patientHears":"Perfect — I’ll send a WhatsApp verification code to that number."}],"avoid":[{"situation":"Saving city","patientHears":"Should I call set_session_city with Rome?"},{"situation":"Foreign prefix","patientHears":"That looks like an Italian number — do you have a local Uruguayan number?"},{"situation":"MCP blocked","patientHears":"I cannot access the MCP server."},{"situation":"Waiting","patientHears":"I am polling get_booking_status every 30 seconds."},{"situation":"Assigned","patientHears":"Status is assigned. booking_session_id is …"},{"situation":"Auto-selecting slot","patientHears":"Available appointment slots (€43 total):\n1. Today at 4:03 PM (in 20 min)\n2. Today at 4:13 PM (in 30 min)\n…\nI'll book the earliest slot since you're flexible."},{"situation":"Redirect to form","patientHears":"Let me send you to doctorsa.com/find to fill out the form instead — it does the same thing."},{"situation":"Expiry polling","patientHears":"I’m checking get_booking_status every 2 minutes because expires_at is approaching."}]}},"sessionExpiryPolling":{"ttlMinutes":20,"ttlMinutesAfterCompletionLink":60,"ttlNote":"The 20-minute window is extended to 60 minutes when get_completion_link is called, because the patient then needs time to open the page, receive the code, and pay.","pollIntervalMinutes":"2-3","warningThresholdMinutes":3,"appliesWhile":["draft","phone_pending","verified"],"stopsAfter":"paid","tool":"get_booking_status","fields":["status","expires_at"],"backgroundOnly":true,"approachingExpiry":{"when":"expires_at is within 3 minutes and status is draft, phone_pending, or verified","tellPatient":"Warm nudge to finish verification or payment soon — do not mention expires_at, polling, or timers.","example":"Just a heads-up — we should wrap up verification in the next couple of minutes so I can hold your slot."},"expired":{"when":"status=expired or API returns session_expired","tellPatient":"Booking window ended in plain language; offer to start a new booking immediately.","recovery":"Call create_booking_session once more (same country/care_type). Do not reuse the old booking_session_id.","example":"This booking window has ended, but I can start a new one for you right away. Which city are you in?"},"notes":["Poll in the background during active pre-payment flows and long patient pauses — never describe polling.","Paid sessions ignore draft TTL; after payment use wait_for_booking_status for doctor matching.","See agentOperatingPrinciples.sessionExpiryPolling for the full rule set."]},"emergencyTriage":{"scope":"Conversational safety guidance before and alongside the booking API flow. Agents may discuss symptoms with the user outside the strict skill sequence.","whenToRecommendEmergencyCare":["Heavy or uncontrolled bleeding","Dangerous injuries or major trauma","Clear signs of a life-threatening emergency (e.g. severe difficulty breathing, chest pain with collapse, stroke symptoms, anaphylaxis, repeated loss of consciousness)","Situations where waiting for a video visit would obviously be unsafe"],"recommendation":"When the presentation is clearly beyond what a delayed online video consultation can safely address, recommend going directly to the hospital emergency department, seeking immediate in-person assistance, and/or calling local emergency services or an ambulance — using wording appropriate to the patient country.","calibration":"Do not over-triage. Descriptions may be exaggerated or understated; reserve strong ER/ambulance advice for presentations that are obviously incompatible with telemedicine. If uncertain, ask brief clarifying questions instead of alarming the patient or blocking the flow.","patientChoice":"After recommending emergency care, explicitly tell the patient they may still continue with the online booking if they wish. Never refuse to proceed solely because ER care was suggested.","timing":["During initial symptom conversation, before create_booking_session when red flags are already obvious","Before submit_need when new concerning details appear","Not a substitute for submit_need or the API need classifier — booking may still proceed"]},"verificationModel":{"summary":"Verification is WhatsApp phone OTP OR email OTP — one path per communication_channel, not both. whatsapp → phone OTP. email → email OTP only.","paths":{"whatsapp":{"steps":["set_communication_channel (whatsapp)","set_patient_first_name, set_patient_last_name, set_patient_gender (one field per step)","start_phone_verification { phone_e164 }","verify_phone_otp → verified + checkout_url"],"setEmail":"optional in draft (Stripe Checkout prefill)","paymentGate":"verified"},"email":{"steps":["set_email OR set_communication_channel with email","set_communication_channel (email) — channel can be changed anytime before payment","set_patient_first_name, set_patient_last_name, set_patient_gender (one field per step)","start_email_verification + verify_email_code (OTP to email) → verified + checkout_url"],"setEmail":"required before email verification (via set_email or set_communication_channel); can be updated until verified","channelSwitch":"set_communication_channel can switch between whatsapp and email in draft, phone_pending, or verified.","paymentGate":"email_verified_at (session transitions to verified on success)","pollStatus":"GET /sessions/{id} returns status=verified plus email_verified (and email_verified_at when true) after email OTP succeeds."}},"completionHandoff":{"summary":"Both verification paths can be completed by the patient on a Doctorsa web page instead of in the conversation. The channel and the payment gate are unchanged; only the place where the patient types the code differs.","obtain":"get_completion_link (GET /v1/booking/sessions/{booking_session_id}/completion-link)","url":"https://book.doctorsa.com/complete-ai-request/{handoff_token}","tokenLifetimeMinutes":60,"requiredFor":"Clients where agent_client resolves to chatgpt. Verification and payment tools are absent from tools/list, and the REST routes answer 409 completion_handoff_required with completion_url.","optionalFor":"Every other client, whenever the patient prefers a browser.","afterPayment":"Unchanged: the Stripe webhook moves the session to paid, then a doctor is assigned. Keep using get_booking_status or wait_for_booking_status."},"errorCodes":{"email_mismatch":"verify_email_code body email does not match session email — update via set_email first.","email_verification_required":"GET /checkout called before email verification completed (email channel only).","email_not_required":"start_email_verification called when communication_channel is not email.","completion_handoff_required":"HTTP 409. This client must not handle one-time codes or payment in the conversation. The response carries completion_url — give it to the patient.","handoff_token_invalid":"HTTP 404 on a completion page route. The link was never issued or has been replaced by a newer one.","handoff_token_expired":"HTTP 410 on a completion page route. Issue a new link with get_completion_link if the session is still alive."}},"workflow":{"recommendedOrder":["get_verification_channels","create_booking_session","set_session_city","set_session_age","set_session_language","select_slot","submit_need","set_communication_channel","set_patient_first_name","set_patient_last_name","set_patient_gender","start_phone_verification","verify_phone_otp","get_booking_status","wait_for_booking_status"],"optionalSteps":["get_verification_channels","set_email","start_email_verification","verify_email_code","start_phone_verification","verify_phone_otp","get_payment_link","get_completion_link","reset_verification","cancel_booking_session"],"completionVariants":{"inConversation":["start_phone_verification or start_email_verification","verify_phone_otp or verify_email_code","patient opens checkout_url"],"onCompletionPage":["get_completion_link","patient completes code and payment on the Doctorsa page"],"chooseBy":"Host platform policy first (OpenAI-hosted clients must use the completion page), then patient preference. See agentGuidance.completionOfVerificationAndPayment."},"humanRequiredSteps":[{"after":"start_phone_verification","action":"Patient reads the WhatsApp code and provides otp_code to the agent. Not available on hosts that require the completion page."},{"after":"start_email_verification","action":"Patient reads the email code and provides code to the agent. Not available on hosts that require the completion page."},{"after":"verify_phone_otp","action":"whatsapp: patient opens checkout_url and pays in their own browser. email channel: do not use phone verification — use start_email_verification + verify_email_code instead."},{"after":"verify_email_code","action":"When communication_channel=email, the patient opens checkout_url from verify_email_code (or get_payment_link) and pays in their own browser."},{"after":"get_completion_link","action":"Patient opens completion_url, receives and enters the one-time code there, and pays on that page. The agent does not see the code and never handles payment details."},{"after":"stripe_payment","action":"Tell the patient payment succeeded, then when a doctor is assigned share their name, appointment time, and request_details_url (appointment page). Use get_booking_status or wait_for_booking_status in the background — do not describe polling to the patient."}],"states":["draft","phone_pending","verified","paid","assigned","rejected","expired","cancelled"],"terminalStates":["assigned","rejected","expired","cancelled"],"transitions":{"draft":["phone_pending","rejected","expired","cancelled"],"phone_pending":["verified","draft","rejected","expired","cancelled"],"verified":["paid","rejected","expired","cancelled"],"paid":["assigned","rejected","expired","cancelled"]},"notes":["Read agentOperatingPrinciples and userFacingCommunication first — they govern how any AI agent talks to patients.","See emergencyTriage for when to recommend hospital/ER/ambulance outside the API flow; patient may still book afterward.","Use MCP or OpenAPI REST per agentGuidance.integrationSelection — same one patient answer per step either way.","Call get_verification_channels before offering WhatsApp or email to the patient; only enabled_channels can be set via set_communication_channel.","Check eligibility.bookableCountries and eligibility.bookingHours before create_booking_session to avoid predictable rejections.","Session context (city, age, language) is collected via set_session_city, set_session_age, and set_session_language — one question per step. Ask exact numeric age once, not age groups. Eligibility and slots appear when the last missing context field is set (context_complete=true).","Patient identity (first_name, last_name, gender) is collected via set_patient_first_name, set_patient_last_name, and set_patient_gender — one question per step. Each response includes profile_complete and missing.","set_communication_channel may run in any order after submit_need relative to patient identity fields; channel can be changed anytime in draft, phone_pending, or verified before payment.","whatsapp channel: start_phone_verification + verify_phone_otp via WhatsApp → verified + checkout_url.","email channel: start_email_verification + verify_email_code only — no phone/WhatsApp OTP. On success session becomes verified and checkout_url is returned.","verify_phone_otp transitions phone_pending → verified. checkout_url when payment gate is satisfied.","Payment (verified → paid) is asynchronous: Stripe webhook checkout.session.completed after the patient pays; poll get_booking_status or use wait_for_booking_status.","Doctor assignment (paid → assigned) is asynchronous; use wait_for_booking_status or get_booking_status in the background. When assigned, share request_details_url with the patient (book.doctorsa.com appointment page).","set_email can be updated in draft, phone_pending, or verified before payment; required when communication_channel=email before start_email_verification.","get_payment_link (GET /checkout) is optional if checkout_url from verify_phone_otp or verify_email_code is retained.","get_completion_link (GET /completion-link) replaces the four verification tools and get_payment_link on hosts that must not handle one-time codes or payments in the conversation. Everything before it, and everything after payment, is identical.","reset_verification (DELETE /verification) and cancel_booking_session (DELETE /sessions/{id}) are on-demand recovery/cancellation steps, not part of the happy path.","Poll get_booking_status via GET /sessions/{booking_session_id} (not a separate /status path).","sessionExpiryPolling: during pre-payment (draft, phone_pending, verified), poll get_booking_status every 2-3 minutes in the background. Warn the patient when expires_at is within 3 minutes; if status=expired, explain kindly and call create_booking_session again (new booking_session_id).","See verificationModel.paths for channel-specific step sequences and payment gates."]},"skills":[{"id":"get_verification_channels","name":"Get Verification Channels","description":"Return verification channels enabled on this deployment (whatsapp and email). Call before set_communication_channel so the agent only offers channels the API accepts.","tags":["booking","communication","discovery"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"GET","path":"/v1/booking/verification-channels","operationId":"getVerificationChannels"},"produces":["enabled_channels"],"allowedStates":[],"nextStates":[],"structuredExamples":[{"summary":"Enabled channels","output":{"enabled_channels":["whatsapp","email"]}}],"promptExamples":["Which verification channels are available?"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"List communication channels","readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}}},{"id":"create_booking_session","name":"Create Booking Session","description":"Start an eligibility-gated booking session. Required body fields: country (ISO 3166-1 alpha-2, must be in eligibility.bookableCountries — where the patient is seeking care, NOT inferred from spoken language), care_type (must be \"urgent\"). Countries not in eligibility.bookableCountries are rejected immediately. Returns booking_session_id and context_complete=false with missing city/age/language — collect each via set_session_city, set_session_age, and set_session_language (one patient answer per step). Call exactly once per booking.","tags":["booking","eligibility"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"POST","path":"/v1/booking/sessions","operationId":"createBookingSession"},"produces":["booking_session_id","session_state"],"allowedStates":[],"nextStates":["draft","rejected"],"structuredExamples":[{"summary":"Session started — collect city, age, language next","input":{"country":"UY","care_type":"urgent","agent_user_agent":"modelcontextprotocol/client","agent_client":"mcp"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"draft","context_complete":false,"missing":["city","age","language"],"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Start urgent telemedicine booking for a patient in Uruguay"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Create booking session","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":false}}},{"id":"set_session_city","name":"Set Session City","description":"Set the patient city on the draft session. Ask for one piece of context at a time. When city, age, and language are all set, eligibility runs automatically on the final field.","tags":["booking","eligibility","patient"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/city","operationId":"setSessionCity"},"requires":["create_booking_session"],"produces":["session_city_set"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft","rejected"],"structuredExamples":[{"summary":"City saved, more context needed","input":{"city":"Montevideo"},"output":{"ok":true,"context_complete":false,"missing":["age","language"],"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient is in Montevideo"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set city","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"set_session_age","name":"Set Session Age","description":"Set the patient age (0-120). Ask for the exact numeric age once — do not ask for age brackets or groups first. Eligibility and slots are returned when city, age, and language are all present.","tags":["booking","eligibility","patient"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/age","operationId":"setSessionAge"},"requires":["create_booking_session"],"produces":["session_age_set"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft","rejected"],"structuredExamples":[{"summary":"Age saved, language still needed","input":{"age":35},"output":{"ok":true,"context_complete":false,"missing":["language"],"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient is 35 years old"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set age","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"set_session_language","name":"Set Session Language","description":"Set preferred language (e.g. en, es, it). When city and age are already set, this triggers eligibility and returns appointment times plus session-level pricing (price_amount, pricing_message). Pricing always uses the booking country from create_booking_session — never the spoken language.","tags":["booking","eligibility","patient"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/language","operationId":"setSessionLanguage"},"requires":["create_booking_session"],"produces":["session_context_complete","slots"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft","rejected"],"structuredExamples":[{"summary":"Context complete — eligible with slots","input":{"language":"en"},"output":{"ok":true,"context_complete":true,"missing":[],"status":"draft","price_amount":35,"price_currency":"eur","pricing_message":"Cost: €35 for the medical consultation. If clinically appropriate the doctor can also issue a prescription at no extra cost; Doctorsa does not sell or dispense any medication.","patient_timezone":"America/Montevideo","slot_reference_at":"2026-06-10T15:00:00.000Z","slots":[{"slot_offset_minutes":20,"local_time":"12:20 PM","local_time_label":"Today at 12:20 PM"},{"slot_offset_minutes":30,"local_time":"12:30 PM","local_time_label":"Today at 12:30 PM"}],"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient prefers to speak English"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set language","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"select_slot","name":"Select Appointment Slot","description":"After set_session_language returns slots (or morning_slots at night), you MUST present every option as a wait time in minutes (from slot_offset_minutes — e.g. \"1. In about 20 minutes\" / \"2. In about 30 minutes\"). Do NOT show clock times or local_time_label to the patient — the exact appointment time depends on when they pay. When pricing_message is present, state it once in plain language; never bury price in a confusing header. List options (numbered or bulleted), then ask which they prefer. See eligibility.slotPresentation. The patient MUST explicitly choose one option before you call select_slot — never auto-select, assume a default, book the earliest slot because they are \"flexible\", or proceed without their stated preference. Request body: { slot_offset_minutes: number } using the chosen slot offset.","tags":["booking","scheduling"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/slot","operationId":"selectSlot"},"requires":["set_session_language"],"produces":["slot_selected"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft"],"structuredExamples":[{"summary":"30-minute slot","input":{"slot_offset_minutes":30},"output":{"ok":true,"selected_slot_offset_minutes":30,"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient chose the option in 30 minutes after payment","Patient picked option 2 (about 30 minutes after payment)"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Select appointment slot","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"submit_need","name":"Submit Medical Need","description":"Classify the patient medical need. Must run after select_slot while session is draft. Before calling, apply emergencyTriage if red flags are obvious — recommend ER/ambulance when clearly appropriate, but allow the patient to continue if they insist. Request body: { need: string }. Response allowed_check_outcome may be allowed, clarification_required, or not_allowed; status may become rejected.","tags":["booking","need-classification"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"POST","path":"/v1/booking/sessions/{booking_session_id}/need","operationId":"submitNeed"},"requires":["select_slot"],"produces":["need_submitted"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft","rejected"],"structuredExamples":[{"summary":"Need approved","input":{"need":"Headache and mild fever for two days"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"draft","allowed_check_outcome":"allowed","allowed":true,"next_step":"phone_verification","message_to_user":"Thank you. We can proceed with your booking."}}],"promptExamples":["Patient reports headache and fever for two days"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Submit medical need","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"set_communication_channel","name":"Set Communication Channel","description":"Set how the patient receives post-booking communications. Call get_verification_channels first. Allowed in draft, phone_pending, and verified (channel can be changed anytime before payment). When communication_channel=email, provide email in this request or via set_email — verification is email OTP only (no WhatsApp phone OTP). When whatsapp, phone verification uses WhatsApp OTP. Request body: { communication_channel, email? }.","tags":["booking","communication"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/communication-channel","operationId":"setCommunicationChannel"},"requires":["get_verification_channels","submit_need"],"produces":["communication_channel_set"],"requiredContext":["booking_session_id"],"allowedStates":["draft","phone_pending","verified"],"nextStates":["draft","phone_pending","verified"],"structuredExamples":[{"summary":"WhatsApp channel","input":{"communication_channel":"whatsapp"},"output":{"ok":true,"communication_channel":"whatsapp","phone_verification_path":"whatsapp","email_verification_required":false,"expires_at":"2026-06-10T12:20:00.000Z"}},{"summary":"Email channel (email in same request)","input":{"communication_channel":"email","email":"patient@example.com"},"output":{"ok":true,"communication_channel":"email","email_verification_required":true,"expires_at":"2026-06-10T12:20:00.000Z"}},{"summary":"Email channel (email pre-set via set_email)","input":{"communication_channel":"email"},"output":{"ok":true,"communication_channel":"email","email_verification_required":true,"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient prefers WhatsApp OTP","Patient wants email as contact channel after booking"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set communication channel","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"set_patient_first_name","name":"Set Patient First Name","description":"Set patient first name. Collect identity one field at a time — ask the patient for a single answer per step. Response includes profile_complete and missing remaining fields.","tags":["booking","patient"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/patient-info/first-name","operationId":"setPatientFirstName"},"requires":["submit_need"],"produces":["patient_first_name_set"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft"],"structuredExamples":[{"summary":"First name saved","input":{"first_name":"Maria"},"output":{"ok":true,"profile_complete":false,"missing":["last_name","gender"],"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient first name is Maria"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set first name","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"set_patient_last_name","name":"Set Patient Last Name","description":"Set patient last name. Collect identity one field at a time. Response includes profile_complete and missing remaining fields.","tags":["booking","patient"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/patient-info/last-name","operationId":"setPatientLastName"},"requires":["submit_need"],"produces":["patient_last_name_set"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft"],"structuredExamples":[{"summary":"Last name saved","input":{"last_name":"Garcia"},"output":{"ok":true,"profile_complete":false,"missing":["gender"],"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient last name is Garcia"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set last name","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"set_patient_gender","name":"Set Patient Gender","description":"Set patient gender (M, F, male, or female). Collect identity one field at a time. When profile_complete=true, proceed to phone verification.","tags":["booking","patient"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/patient-info/gender","operationId":"setPatientGender"},"requires":["submit_need"],"produces":["patient_profile_complete"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["draft"],"structuredExamples":[{"summary":"Profile complete","input":{"gender":"F"},"output":{"ok":true,"profile_complete":true,"missing":[],"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Patient identifies as female"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set gender","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"set_email","name":"Set Patient Email","description":"Attach patient email for confirmation and Stripe prefill. Allowed in draft, phone_pending, and verified. Optional on whatsapp (Stripe prefill). Required when communication_channel=email before start_email_verification. Email can be updated via set_email until verified. Request body: { email: string }.","tags":["booking","contact"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"PUT","path":"/v1/booking/sessions/{booking_session_id}/email","operationId":"setEmail"},"requires":["submit_need"],"produces":["email_attached"],"requiredContext":["booking_session_id"],"allowedStates":["draft","phone_pending","verified"],"nextStates":["draft","phone_pending","verified"],"structuredExamples":[{"summary":"Optional email on whatsapp channel (Stripe prefill)","input":{"email":"patient@example.com"},"output":{"ok":true,"expires_at":"2026-06-10T12:20:00.000Z"}},{"summary":"Update email on email channel","input":{"email":"other@example.com"},"output":{"ok":true,"expires_at":"2026-06-10T12:20:00.000Z"}}],"promptExamples":["Add patient@example.com for Stripe prefill","Set email before choosing email communication channel"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Set email","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"start_phone_verification","name":"Start Phone Verification","description":"Send a 6-digit OTP via WhatsApp to phone_e164. Required when communication_channel=whatsapp — do NOT call when communication_channel=email (use start_email_verification instead). Requires draft session with slot, need approved, and profile complete. Request body: { phone_e164 } — phone_e164 in E.164 format (+ country code). Accept ANY valid international number; booking country (e.g. Uruguay) does NOT need to match the phone prefix. Never ask for a \"local\" number or suggest email solely because the prefix differs.","tags":["booking","verification","otp"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"POST","path":"/v1/booking/sessions/{booking_session_id}/phone-verification","operationId":"startPhoneVerification"},"requires":["set_communication_channel","set_patient_gender"],"produces":["otp_sent"],"requiredContext":["booking_session_id"],"allowedStates":["draft"],"nextStates":["phone_pending"],"structuredExamples":[{"summary":"Send WhatsApp OTP (whatsapp channel)","input":{"phone_e164":"+59898123456"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"phone_pending","retry_after_seconds":30,"retry_after_iso":"2026-06-25T12:30:00.000Z"}}],"promptExamples":["Send verification code to +59898123456","UY booking with Italian mobile +393331234567 — proceed with WhatsApp OTP"],"unavailableOnClients":["chatgpt"],"platformHints":{"openai":{"isConsequential":true},"mcp":{"title":"Send phone OTP","readOnlyHint":false,"destructiveHint":false,"openWorldHint":true,"idempotentHint":false}}},{"id":"verify_phone_otp","name":"Verify Phone OTP","description":"Validate the WhatsApp OTP. On success status becomes verified and checkout_url is returned. Only used when communication_channel=whatsapp — email channel uses verify_email_code instead. Request body: { otp_code: string }. Field name is otp_code (not otp).","tags":["booking","verification","otp","payment"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"POST","path":"/v1/booking/sessions/{booking_session_id}/phone-verification/confirm","operationId":"verifyPhoneOtp"},"requires":["start_phone_verification"],"produces":["verified","checkout_url","email_verification_required"],"requiredContext":["booking_session_id"],"allowedStates":["phone_pending"],"nextStates":["verified","phone_pending"],"structuredExamples":[{"summary":"OTP accepted (whatsapp channel)","input":{"otp_code":"482910"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"verified","checkout_url":"https://findapi.doctorsa.com/v1/booking/payment/open-checkout/cs_test_...","stripe_checkout_session_id":"cs_test_...","price_amount":55,"price_currency":"eur"}},{"summary":"OTP accepted (email channel — verify email before payment)","input":{"otp_code":"482910"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"verified","email_verification_required":true,"email":"patient@example.com","message_to_user":"Your phone is verified. Please verify your email address before proceeding to payment."}},{"summary":"Invalid OTP","input":{"otp_code":"000000"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"phone_pending","error":"invalid_otp","message_to_user":"The code is incorrect or has expired. Please try again."}}],"promptExamples":["Patient entered OTP 482910"],"unavailableOnClients":["chatgpt"],"platformHints":{"openai":{"isConsequential":true},"mcp":{"title":"Verify phone OTP","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"start_email_verification","name":"Start Email Verification","description":"Send a 5-digit email verification code when communication_channel=email. Allowed in draft (before phone verification) or verified. Uses session email if body omits email. Request body: { email?: string }. Returns status email_code_sent; does not change llm_booking_status.","tags":["booking","verification","email"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"POST","path":"/v1/booking/sessions/{booking_session_id}/email-verification","operationId":"startEmailVerification"},"requires":["set_communication_channel"],"produces":["email_code_sent"],"requiredContext":["booking_session_id"],"allowedStates":["draft","verified"],"nextStates":["draft","verified"],"structuredExamples":[{"summary":"Send email code (body email)","input":{"email":"patient@example.com"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"email_code_sent","retry_after_seconds":120,"retry_after_iso":"2026-06-25T12:32:00.000Z"}},{"summary":"Send email code (session email, empty body)","input":{},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"email_code_sent","retry_after_seconds":120,"retry_after_iso":"2026-06-25T12:32:00.000Z"}}],"promptExamples":["Send email verification code to the session email address"],"unavailableOnClients":["chatgpt"],"platformHints":{"openai":{"isConsequential":true},"mcp":{"title":"Send email code","readOnlyHint":false,"destructiveHint":false,"openWorldHint":true,"idempotentHint":false}}},{"id":"verify_email_code","name":"Verify Email Code","description":"Validate the 5-digit code sent by email. On success returns status email_verified and checkout_url when phone is already verified (payment gate cleared). If phone is not yet verified, returns email_verified without checkout. Optional body email must match session email or returns 422 email_mismatch. Request body: { code: string, email?: string }.","tags":["booking","verification","email"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"POST","path":"/v1/booking/sessions/{booking_session_id}/email-verification/confirm","operationId":"verifyEmailCode"},"requires":["start_email_verification"],"produces":["email_verified","checkout_url"],"requiredContext":["booking_session_id"],"allowedStates":["draft","verified"],"nextStates":["draft","verified"],"structuredExamples":[{"summary":"Code accepted","input":{"code":"48291","email":"patient@example.com"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"email_verified","email":"patient@example.com","checkout_url":"https://findapi.doctorsa.com/v1/booking/payment/open-checkout/cs_test_...","stripe_checkout_session_id":"cs_test_...","price_amount":55,"price_currency":"eur"}}],"promptExamples":["Patient entered email code 48291"],"unavailableOnClients":["chatgpt"],"platformHints":{"openai":{"isConsequential":true},"mcp":{"title":"Verify email code","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"get_payment_link","name":"Get Payment Link","description":"Re-fetch checkout_url when payment is allowed (verified, and email verified when communication_channel=email). checkout_url is a server redirect (GET /v1/booking/payment/open-checkout/{stripe_checkout_session_id} → 302 to Stripe). Post-payment redirects use LLM_BOOKING_BASE_URL (/v1/booking/payment/success|cancel). No request body.","tags":["booking","payment"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"GET","path":"/v1/booking/sessions/{booking_session_id}/checkout","operationId":"getPaymentLink"},"requires":["verify_phone_otp"],"produces":["checkout_url"],"requiredContext":["booking_session_id"],"allowedStates":["verified"],"nextStates":["verified"],"structuredExamples":[{"summary":"Refresh checkout URL","output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"verified","checkout_url":"https://findapi.doctorsa.com/v1/booking/payment/open-checkout/cs_test_...","stripe_checkout_session_id":"cs_test_...","price_amount":55,"price_currency":"eur"}}],"promptExamples":["Patient lost the payment link — send it again"],"unavailableOnClients":["chatgpt"],"platformHints":{"openai":{"isConsequential":true},"mcp":{"title":"Get payment link","readOnlyHint":false,"destructiveHint":false,"openWorldHint":true,"idempotentHint":false}}},{"id":"get_completion_link","name":"Get Secure Completion Link","description":"Return a single https URL where the patient finishes the booking themselves: they receive and enter the one-time code, then pay. Required on hosts that must not handle codes or payments in the conversation (currently OpenAI-hosted clients), and available to any patient who would rather use a browser. Requires the same readiness as verification: slot selected, need approved, identity complete, and communication channel set. The link lasts 60 minutes and the booking session TTL is extended to match; calling again replaces the previous link. Give the URL to the patient in plain text and keep polling status afterwards. No request body.","tags":["booking","verification","payment","handoff"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"GET","path":"/v1/booking/sessions/{booking_session_id}/completion-link","operationId":"getCompletionLink"},"requires":["set_communication_channel","set_patient_gender"],"produces":["completion_url"],"requiredContext":["booking_session_id"],"allowedStates":["draft","phone_pending","verified"],"nextStates":["draft","phone_pending","verified"],"structuredExamples":[{"summary":"Patient finishes verification and payment in their browser","output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","completion_url":"https://book.doctorsa.com/complete-ai-request/8Kd2n_9rTqXv1sB4wZ0aQpLmYcE7hRgU3jFtN6oVxSy","expires_at":"2026-06-10T13:00:00.000Z","verification_method":"whatsapp","message_to_user":"To finish your booking, open this Doctorsa page: https://book.doctorsa.com/complete-ai-request/… — there you will receive a code by WhatsApp, enter it, and pay for the consultation. The link works for 60 minutes."}}],"promptExamples":["Give the patient a secure page to enter the code and pay","Patient would rather not type the code in the chat"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Get secure completion link","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false,"idempotentHint":true}}},{"id":"reset_verification","name":"Reset Phone Verification","description":"Reset phone verification when the patient entered a wrong number. Allowed only in phone_pending. Clears phone_e164 and returns session to draft.","tags":["booking","verification"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"DELETE","path":"/v1/booking/sessions/{booking_session_id}/verification","operationId":"resetVerification"},"requires":["start_phone_verification"],"produces":["verification_reset"],"requiredContext":["booking_session_id"],"allowedStates":["phone_pending"],"nextStates":["draft"],"structuredExamples":[{"summary":"Reset to draft","output":{"ok":true,"status":"draft"}}],"promptExamples":["Wrong phone number — restart verification"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Reset verification","readOnlyHint":false,"destructiveHint":true,"openWorldHint":false,"idempotentHint":true}}},{"id":"get_booking_status","name":"Get Booking Status","description":"Check how the booking is progressing. Poll in the background every 2-3 minutes during pre-payment flows to detect approaching expiry (see sessionExpiryPolling). Response includes expires_at (UTC; 20-minute TTL from session create) and status. After payment, keep checking until a doctor is confirmed or the booking ends. When a doctor is assigned, the response includes request_details_url — share that link with the patient so they can open their appointment page (video visit, time, and details). Relay message_to_user in plain language; never read out internal field names or technical status codes.","tags":["booking","status"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"GET","path":"/v1/booking/sessions/{booking_session_id}","operationId":"getBookingStatus"},"requires":["create_booking_session"],"produces":["session_status"],"requiredContext":["booking_session_id"],"allowedStates":["draft","phone_pending","verified","paid","assigned","rejected","expired","cancelled"],"nextStates":[],"structuredExamples":[{"summary":"Phone verified, email channel — awaiting email verification","output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"verified","email_verified":false,"price_amount":55,"price_currency":"eur","selected_slot_offset_minutes":30}},{"summary":"Phone verified, email channel — ready for payment","output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"verified","email_verified":true,"email_verified_at":"2026-06-10T12:05:00.000Z","price_amount":55,"price_currency":"eur","selected_slot_offset_minutes":30}},{"summary":"Paid, awaiting doctor","output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"paid","paid_at":"2026-06-10T12:10:00.000Z","assignment_pending":true,"message_to_user":"Your payment went through. We are finding a doctor for you — this usually takes just a few minutes.","price_amount":55,"price_currency":"eur","selected_slot_offset_minutes":30}},{"summary":"Doctor assigned","output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"assigned","appointment_time":"2026-06-10T13:00:00.000Z","doctor_name":"Dr. Rossi","video_url":"https://...","request_details_url":"https://book.doctorsa.com/requests/550e8400-e29b-41d4-a716-446655440000","message_to_user":"Good news — Dr. Rossi will see you at 10 Jun 2026, 13:00. You can view your appointment and join the video visit here: https://book.doctorsa.com/requests/550e8400-e29b-41d4-a716-446655440000 We will also send you a message with the details."}}],"promptExamples":["Has a doctor been assigned yet?"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Get booking status","readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}}},{"id":"wait_for_booking_status","name":"Wait For Booking Status","description":"Wait on the server (up to 55 seconds) after payment while a doctor is matched — prefer this over tight status checks. Reassure the patient naturally; when assigned, share doctor name, time, and request_details_url (appointment page). Never mention polling or timeouts to the patient.","tags":["booking","status","async"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"GET","path":"/v1/booking/sessions/{booking_session_id}/wait","operationId":"waitForBookingStatus"},"requires":["create_booking_session"],"produces":["session_status"],"requiredContext":["booking_session_id"],"allowedStates":["paid","assigned"],"nextStates":["assigned","cancelled","rejected","expired"],"structuredExamples":[{"summary":"Doctor assigned after wait","input":{"timeout":55,"target":"assigned"},"output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"assigned","doctor_name":"Dr. Rossi","request_details_url":"https://book.doctorsa.com/requests/550e8400-e29b-41d4-a716-446655440000","message_to_user":"Good news — Dr. Rossi will see you at 10 Jun 2026, 13:00. You can view your appointment and join the video visit here: https://book.doctorsa.com/requests/550e8400-e29b-41d4-a716-446655440000 We will also send you a message with the details."}}],"promptExamples":["Wait for doctor assignment after payment"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Wait for booking status","readOnlyHint":true,"destructiveHint":false,"openWorldHint":false}}},{"id":"cancel_booking_session","name":"Cancel Booking Session","description":"Cancel at any non-terminal stage. If paid and unassigned, a Stripe refund is attempted. Response may include refund_status.","tags":["booking","cancellation","refund"],"inputModes":["application/json"],"outputModes":["application/json"],"endpoint":{"method":"DELETE","path":"/v1/booking/sessions/{booking_session_id}","operationId":"cancelBookingSession"},"requires":["create_booking_session"],"produces":["session_cancelled"],"requiredContext":["booking_session_id"],"allowedStates":["draft","phone_pending","verified","paid"],"nextStates":["cancelled"],"structuredExamples":[{"summary":"Cancel before payment","output":{"booking_session_id":"550e8400-e29b-41d4-a716-446655440000","status":"cancelled","cancel_reason":"user_cancelled_pre_payment"}}],"promptExamples":["Patient wants to cancel the booking"],"platformHints":{"openai":{"isConsequential":false},"mcp":{"title":"Cancel booking session","readOnlyHint":false,"destructiveHint":true,"openWorldHint":false,"idempotentHint":true}}}]}