{"auth":{"derive":[{"attr":"content","from":"meta","name":"csrf","selector":"meta[name=\"csrf-token\"]"}]},"confirm":{"summary":"UNTESTED recipe. Reserve the Doctolib slot {{params.start_date}} (visit type {{params.visit_motive_id}}, practice {{params.practice_id}}, {{params.insurance_sector}} insurance) for your logged-in Doctolib account. You finish the booking yourself in Doctolib."},"description":"UNTESTED until recorded logged in. Selects an exact slot (from doctolib.de/get-slots) in Doctolib's booking funnel for the user's own logged-in account, after an explicit Allow. Returns the practice's notices, which the user must read, and the next booking page to finish in the browser. It does not accept practice notices or confirm the appointment on its own.","effect":"write","id":"doctolib.de/book-slot","keywords":["doctolib","termin buchen","arzttermin buchen","book appointment","booking"],"login_check":{"description":"Logged out: 401 {meta:{reason:'User not logged in'}} (verified 2026-11-07).","expect":{"content_type":"json","login_required_if":{"assert":"body.meta.reason == 'User not logged in' || contains(body.redirection || '', '/auth/')","status":[401,403]},"status":[200]},"id":"login_check","request":{"headers":{"Accept":"application/json"},"method":"GET","url":"/account/appointments.json"}},"output":{"schema":{"properties":{"continue_url":{"description":"Doctolib page to finish the booking","type":["string","null"]},"practice_notices":{"description":"Practice notices (HTML) the user must read and accept in Doctolib before the booking is final","items":{"type":"string"},"type":"array"},"status":{"const":"slot_selected_unconfirmed"}},"type":"object"},"value":"{practice_notices: steps.select_slot.notices, continue_url: steps.select_slot.redirection && (starts_with(steps.select_slot.redirection, 'http') && steps.select_slot.redirection || join('', ['https://www.doctolib.de', steps.select_slot.redirection])) || `null`, status: 'slot_selected_unconfirmed'}"},"params":{"additionalProperties":false,"properties":{"agenda_ids":{"items":{"type":"integer"},"minItems":1,"type":"array"},"insurance_sector":{"enum":["public","private"],"type":"string"},"practice_id":{"type":"integer"},"profile_id":{"description":"From search-practitioners","type":"integer"},"start_date":{"description":"Exact slot timestamp from get-slots, e.g. 2026-11-09T15:45:00.000+01:00","pattern":"^\\d{4}-\\d{2}-\\d{2}T","type":"string"},"visit_motive_id":{"type":"integer"}},"required":["profile_id","visit_motive_id","agenda_ids","practice_id","insurance_sector","start_date"],"type":"object"},"provenance":{"method":"browser-inspect","notes":"UNTESTED. Only select_slot was seen, anonymously, in the UI (spikes/doctolib.md); the anonymous response carried draft_rules (a practice notice to accept) and redirection to /auth/patient_de. CSRF: the UI sends x-csrf-token from meta[name=csrf-token] (Rails). Unknown until Victor records the logged-in flow (tools/har2recipe): the appointment-creation and confirm calls, whether accepting the notice is its own request, patient selection (self or relative), 2FA, and whether select_slot holds the slot. The final confirm step must be added as another write:true step.","recorded_at":"2026-11-07T12:13:00+01:00","recorded_by":"C4"},"requires_login":true,"schema":"https://wield.run/schema/recipe/v1.json","site":{"country":"DE","id":"doctolib.de","language":"de","login_url":"https://www.doctolib.de/auth/patient_de","name":"Doctolib","origin":"https://www.doctolib.de","origins":["https://www.doctolib.de"]},"steps":[{"description":"OBSERVED anonymously only (spikes/doctolib.md). Logged out, Doctolib answers with redirection to /auth/patient_de, which is reported as login_required. Logged in, the response and the following funnel calls are unknown: record them with tools/har2recipe before shipping.","expect":{"content_type":"json","login_required_if":{"assert":"contains(body.redirection || '', '/auth/')","status":[401,403]},"status":[200,201]},"extract":{"json":{"notices":"draft_rules[].body || `[]`","redirection":"redirection || `null`","visit_motive_id":"visit_motive_id || `null`"}},"id":"select_slot","request":{"body":{"type":"json","value":{"agenda_ids":"{{params.agenda_ids}}","insurance_sector":"{{params.insurance_sector}}","practice_ids":["{{params.practice_id}}"],"profile_id":"{{params.profile_id}}","source_action":"profile","start_date":"{{params.start_date}}","visit_motive_ids":["{{params.visit_motive_id}}"]}},"headers":{"Accept":"application/json","Content-Type":"application/json; charset=utf-8","X-CSRF-Token":"{{vars.csrf}}"},"method":"POST","url":"/online_booking/api/slot_selection_funnel/v1/select_slot"},"write":true}],"task":"Reserve a Doctolib appointment slot for the logged-in user (UNTESTED draft)","tos":{"notes":"Booking acts on the user's own Doctolib account and only after an explicit Allow per run. Never auto-accept practice notices; the user reads them. One booking per user request, no retries that could double-book.","reviewed":false},"verify":{"checks":["output.status == 'slot_selected_unconfirmed'"],"example_params":{"agenda_ids":[2359956],"insurance_sector":"public","practice_id":723928,"profile_id":1534568,"start_date":"2026-11-09T15:45:00.000+01:00","visit_motive_id":14928354}},"version":1}