{"openapi":"3.0.3","info":{"title":"Praxisherz API","version":"1.0.0","description":"The public API of Praxisherz for your own practice frontends and integrations: the practice's pages, treatments, team, booking and enquiries as JSON. Resource paths are German (`/api/v1/leistungen`, `/api/v1/buchung/termine`), fields and codes English. Authenticate with the header `Authorization: Bearer <key>` (the practice's key from the panel, Einstellungen). Limit: 120 requests per minute per key; every response to a valid key carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset."},"servers":[{"url":"https://api.praxisherz.ch"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"The practice's secret key (pp_live_…) from the panel, Einstellungen. Server-side only."}},"headers":{"RateLimit-Limit":{"description":"Requests allowed in the window, per key.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window ends (a fixed window, the clock minute). The headers can be absent when the counter is briefly unavailable.","schema":{"type":"integer"}}},"responses":{"Unauthorized":{"description":"The key is missing or not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing":{"summary":"No Authorization header","value":{"error":"Authorization: Bearer <Schlüssel aus dem Panel, Einstellungen> fehlt."}},"invalid":{"summary":"Unknown or rotated key","value":{"error":"Ungültiger Schlüssel."}}}}}},"NotFound":{"description":"Nothing published under this slug.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing":{"summary":"No published treatment with this slug","value":{"error":"Nicht gefunden."}}}}}},"RateLimited":{"description":"More than 120 requests in this minute. Retry after the `Retry-After` seconds.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Seconds to wait before retrying. 60 for the per-minute limit (a full window; RateLimit-Reset has the exact seconds left)."},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Over 120 requests in this minute","value":{"error":"Limit von 120 Aufrufen pro Minute überschritten."}}}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message (German). Do not match on it; use the status and `code`."},"code":{"type":"string","description":"Machine-readable code, where the endpoint sends one: invalid_json, invalid_body, missing_contact, invalid_time_request, basic_insurance_phone_only, feedback_rate_limited.","x-ph-since":"1.0.0"}},"required":["error"],"description":"An error. `error` is a human-readable message in German; branch on the HTTP status and, where the endpoint sends one, on `code`.","x-ph-since":"1.0.0"},"LeadError":{"type":"object","description":"A rejected lead. Nothing was stored.","properties":{"error":{"type":"string","description":"Human-readable message (German)."},"code":{"type":"string","enum":["invalid_json","invalid_body","missing_contact","invalid_time_request"],"description":"invalid_json: the body is not JSON. invalid_body: the JSON is not an object. missing_contact: only with ?strict=1, neither a phone (at least 6 digits) nor an e-mail. invalid_time_request: only with ?strict=1, `timeRequest` failed validation (for example every window is in the past)."}},"required":["error","code"],"x-ph-since":"1.0.0"},"MenuItem":{"type":"object","properties":{"label":{"type":"string","description":"Text of the menu item."},"path":{"type":"string","description":"Path on the practice's site, e.g. /behandlungen."}},"required":["label","path"],"description":"One item of the site's main menu.","x-ph-since":"1.0.0"},"ThemeTokens":{"type":"object","additionalProperties":true,"description":"The site's theme tokens (colours, type, radii, shadows, motion): an object ready to map onto the CSS variables of your frontend.","x-ph-since":"1.0.0"},"Site":{"type":"object","properties":{"name":{"type":"string","description":"Name of the practice as shown on the site."},"primaryDomain":{"type":"string","nullable":true,"description":"The site's main domain; null before a domain is connected."},"menu":{"type":"array","items":{"$ref":"#/components/schemas/MenuItem"},"description":"The main menu, in order."},"theme":{"allOf":[{"$ref":"#/components/schemas/ThemeTokens"}],"description":"Theme tokens of the site."},"profile":{"allOf":[{"$ref":"#/components/schemas/SiteProfile"}],"nullable":true,"description":"The practice profile; null until the practice fills it in.","x-ph-since":"1.0.0"},"noindex":{"type":"boolean","description":"true: the site stays out of search engines (a demo, or before launch). Add meta robots noindex to every page (ideally also the header X-Robots-Tag: noindex) and publish no sitemap. Keep robots.txt open (`Allow: /`, no Sitemap line): a crawler has to fetch the pages to see the noindex and drop URLs it indexed before.","x-ph-since":"1.0.0"}},"required":["name","menu","theme","profile"],"description":"The practice's site: name, domain, menu, theme and the practice profile.","x-ph-since":"1.0.0"},"SiteProfile":{"type":"object","description":"The practice's standing facts for the header, footer, contact page, JSON-LD and metadata. Every field is optional: show what is there and hide the rest (an empty `socials` list means no icons).","properties":{"specialty":{"type":"string","enum":["dental","aesthetic-medicine","physiotherapy","gynecology","dermatology","ophthalmology","veterinary","psychotherapy","medical-center","orthopedics","pediatrics","nutrition"],"description":"The practice's specialty: a key of the platform's list, chosen in the setup assistant or by the site migration. Absent when none (or \"other\") was chosen.","x-ph-since":"1.0.0"},"legalName":{"type":"string","description":"Registered name of the entity that runs the practice."},"phone":{"type":"string","description":"Phone number for display, e.g. +41 44 123 45 67. For a tel: link keep the digits and the +."},"email":{"type":"string","format":"email","description":"The practice's contact e-mail."},"address":{"allOf":[{"$ref":"#/components/schemas/SiteAddress"}],"description":"The practice's address."},"geo":{"type":"object","properties":{"lat":{"type":"number","description":"Latitude."},"lng":{"type":"number","description":"Longitude."}},"required":["lat","lng"],"description":"Map point of the practice."},"openingHours":{"allOf":[{"$ref":"#/components/schemas/OpeningHours"}],"description":"Opening hours per weekday."},"openingHoursNote":{"type":"string","description":"Note shown next to the hours, e.g. until when the phone is answered."},"basicInsurance":{"type":"object","description":"Basic insurance (KVG): whether the practice bills services to the basic insurance.","properties":{"contract":{"type":"boolean","description":"true when the practice bills services to the basic insurance (KVG)."},"note":{"type":"string","description":"Note about the basic insurance, e.g. which services it covers."}},"required":["contract"]},"payments":{"type":"object","properties":{"methods":{"type":"array","items":{"type":"string"},"description":"Payment methods as the practice names them, e.g. Bar, Karte, TWINT."},"note":{"type":"string","description":"Note about payments, e.g. invoice terms."}},"required":["methods"],"description":"Accepted payment methods."},"socials":{"type":"array","items":{"type":"object","properties":{"network":{"type":"string","enum":["facebook","instagram","youtube","tiktok","linkedin","x","google","znanylekarz","other"],"description":"The network."},"url":{"type":"string","description":"Profile address (https://)."}},"required":["network","url"]},"description":"Links to the practice's profiles elsewhere."},"logo":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Logo; null when the file was removed from the media library."},"logoInverse":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Logo for dark backgrounds."},"favicon":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Favicon file."},"defaultOgImage":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"og:image for pages that have none of their own."},"registry":{"type":"object","description":"Public register numbers. Show only the ones that are set.","properties":{"gln":{"type":"string","description":"Number in the register of healthcare providers."},"uid":{"type":"string","description":"Business identification number (UID, CHE-…)."},"vatNumber":{"type":"string","description":"Statistical register number."},"commercialRegister":{"type":"string","description":"Commercial register number."}}},"privacy":{"type":"object","description":"Data for the privacy notice and the information notice of forms. The controller is `legalName` with the address and the `registry` numbers. Full text: GET /datenschutz.","properties":{"contactEmail":{"type":"string","format":"email","description":"E-mail for personal-data requests; the practice's `email` when unset.","x-ph-since":"1.0.0"},"registeredOffice":{"type":"string","description":"The controller's registered office, when it differs from the practice address.","x-ph-since":"1.0.0"},"dpoName":{"type":"string","description":"Data protection advisor, when one is appointed.","x-ph-since":"1.0.0"},"dpoEmail":{"type":"string","format":"email","description":"The data protection advisor's e-mail.","x-ph-since":"1.0.0"}},"x-ph-since":"1.0.0"},"reviewsPolicy":{"type":"string","description":"How the practice collects and checks the reviews it shows on its site."},"demoNotice":{"type":"string","description":"A notice for every page (e.g. a demo site). Show it when present."}},"x-ph-since":"1.0.0"},"SiteAddress":{"type":"object","properties":{"street":{"type":"string","description":"Street and number."},"postalCode":{"type":"string","pattern":"^\\d{4}$","description":"Postal code (PLZ), four digits."},"city":{"type":"string","description":"City."},"district":{"type":"string","description":"District or quarter, e.g. Seefeld."},"accessNote":{"type":"string","description":"How to get in: entrance, floor, parking, lift."}},"required":["street","postalCode","city"],"description":"A postal address in Switzerland.","x-ph-since":"1.0.0"},"OpeningHours":{"type":"object","description":"Opening hours per weekday (keys mon to sun, local time Europe/Zurich). A missing day or an empty list means closed; two intervals mean a break.","properties":{"mon":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"description":"Monday's intervals."},"tue":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"description":"Tuesday's intervals."},"wed":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"description":"Wednesday's intervals."},"thu":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"description":"Thursday's intervals."},"fri":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"description":"Friday's intervals."},"sat":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"description":"Saturday's intervals."},"sun":{"type":"array","items":{"$ref":"#/components/schemas/OpeningInterval"},"description":"Sunday's intervals."}},"x-ph-since":"1.0.0"},"Location":{"type":"object","description":"A location of the practice: one brand and one site, each location with its own address, phone, hours and basic insurance status. The primary location carries the practice profile's values.","properties":{"id":{"type":"string","description":"Location id."},"slug":{"type":"string","description":"The location's address part, unique within the practice."},"name":{"type":"string","description":"Name of the location, e.g. the town or the district."},"primary":{"type":"boolean","description":"true for the primary location (its values are the practice profile's)."},"phone":{"type":"string","description":"Phone number for display. For a tel: link keep the digits and the +."},"email":{"type":"string","format":"email","description":"The location's contact e-mail."},"address":{"allOf":[{"$ref":"#/components/schemas/SiteAddress"}],"description":"The location's address."},"geo":{"type":"object","properties":{"lat":{"type":"number","description":"Latitude."},"lng":{"type":"number","description":"Longitude."}},"required":["lat","lng"],"description":"Map point of the location."},"openingHours":{"allOf":[{"$ref":"#/components/schemas/OpeningHours"}],"description":"Opening hours per weekday."},"openingHoursNote":{"type":"string","description":"Note shown next to the hours."},"basicInsurance":{"type":"object","properties":{"contract":{"type":"boolean","description":"true when the location bills services to the basic insurance."},"note":{"type":"string","description":"Note about the basic insurance."}},"required":["contract"],"description":"Basic insurance (KVG) at this location."}},"required":["id","slug","name","primary"],"x-ph-since":"1.0.0"},"OpeningInterval":{"type":"object","properties":{"opens":{"type":"string","pattern":"^\\d{2}:\\d{2}$","example":"08:00","description":"Opening time, HH:MM."},"closes":{"type":"string","pattern":"^\\d{2}:\\d{2}$","example":"20:00","description":"Closing time, HH:MM, later than `opens`."}},"required":["opens","closes"],"description":"One opening interval.","x-ph-since":"1.0.0"},"Block":{"type":"object","description":"A validated content block. Render the types you support by `type` and skip the rest: new block types never break your frontend.","properties":{"id":{"type":"string","description":"Block id, stable within the page."},"type":{"type":"string","description":"Block type with its version, e.g. hero.v1, faq.v1."},"props":{"type":"object","additionalProperties":true,"description":"The block's fields; their shape depends on `type`."}},"required":["id","type","props"],"additionalProperties":true,"x-ph-since":"1.0.0"},"MediaRef":{"type":"object","description":"An image file. Files from the media library (`/api/media-file/…`) have variants: `?w=` from the ladder 320, 480, 640, 960, 1280, 1600, 2048 px (never upscaled past the original) and `f=` auto | avif | webp (auto picks AVIF or WebP from the Accept header and answers with Vary: Accept), fixed quality 78. Any other width or format is a 400. `f` alone gives a width up to 2048 px; no parameters give the original. Variants are cached (Cache-Control: immutable).","properties":{"url":{"type":"string","description":"Absolute address of the file."},"focalX":{"type":"integer","nullable":true,"description":"Horizontal focal point in percent (0 to 100) to keep when cropping, e.g. as object-position."},"focalY":{"type":"integer","nullable":true,"description":"Vertical focal point in percent (0 to 100)."},"alt":{"type":"string","nullable":true,"description":"Alternative text; null when the practice left it empty."},"width":{"type":"integer","description":"Width of the original in px (library files, when known), for width/height attributes without layout shift.","x-ph-since":"1.0.0"},"height":{"type":"integer","description":"Height of the original in px.","x-ph-since":"1.0.0"},"aiGenerated":{"type":"boolean","description":"The image was generated by AI: label it next to the image (e.g. in a caption).","x-ph-since":"1.0.0"},"caption":{"type":"string","description":"Caption to show under the image.","x-ph-since":"1.0.0"}},"required":["url"],"x-ph-since":"1.0.0"},"Crumb":{"type":"object","properties":{"label":{"type":"string","description":"Text of the step."},"href":{"type":"string","nullable":true,"description":"Path of the step; null for the current page."}},"required":["label"],"description":"One step of a breadcrumb trail.","x-ph-since":"1.0.0"},"Cta":{"type":"object","description":"The page's one call to action: the practice's phone (a tel: link) or its contact page.","properties":{"label":{"type":"string","description":"Button text."},"href":{"type":"string","description":"Link target: tel:… or a path on the practice's site."}},"required":["label","href"],"x-ph-since":"1.0.0"},"RelatedService":{"type":"object","properties":{"id":{"type":"string","description":"Treatment id."},"slug":{"type":"string","description":"Treatment slug."},"path":{"type":"string","description":"Path of the treatment page."},"name":{"type":"string","description":"Treatment name."},"priceFromLabel":{"type":"string","nullable":true,"description":"Price label, e.g. ab CHF 120; null when the practice shows no price."}},"required":["id","slug","path","name"],"description":"A short reference to a published treatment.","x-ph-since":"1.0.0"},"ServiceSummary":{"type":"object","properties":{"id":{"type":"string","description":"Treatment id."},"slug":{"type":"string","description":"Slug from the treatment's address."},"path":{"type":"string","description":"Path on the practice's site, e.g. /behandlungen/dentalhygiene."},"name":{"type":"string","description":"Treatment name."},"category":{"type":"string","nullable":true,"description":"Category the practice groups the treatment under; null when none."},"shortDesc":{"type":"string","nullable":true,"description":"One-sentence description for lists; null when empty."},"priceFromLabel":{"type":"string","nullable":true,"description":"Price label, e.g. ab CHF 120; null when the practice shows no price."},"durationLabel":{"type":"string","nullable":true,"description":"Duration label, e.g. 60 Min.; null when empty."},"bookingServiceId":{"type":"string","nullable":true,"description":"Id of the active booking service this treatment is booked as: an entry of GET /buchung/katalog and the `service` parameter of /buchung/zeitfenster. null when the treatment is not linked to booking or the linked service is inactive.","x-ph-since":"1.0.0"}},"required":["id","slug","path","name","bookingServiceId"],"description":"A published treatment in a list.","x-ph-since":"1.0.0"},"ServiceDetail":{"allOf":[{"$ref":"#/components/schemas/ServiceSummary"},{"type":"object","properties":{"hero":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Main image of the treatment page; null when none."},"bodyBlocks":{"type":"array","items":{"$ref":"#/components/schemas/Block"},"description":"Content blocks of the treatment page."},"faq":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string","description":"The question."},"answer":{"type":"string","description":"The answer, plain text."}},"required":["question","answer"]},"description":"Questions and answers about the treatment."},"related":{"type":"array","items":{"$ref":"#/components/schemas/RelatedService"},"description":"Other published treatments the practice links from this one."},"updatedAt":{"type":"string","format":"date-time","description":"Last change, ISO 8601."}},"required":["bodyBlocks","faq","related","updatedAt"]}],"description":"A published treatment with its page content.","x-ph-since":"1.0.0"},"DoctorSummary":{"type":"object","properties":{"id":{"type":"string","description":"Team member id."},"slug":{"type":"string","description":"Slug from the profile's address."},"path":{"type":"string","description":"Path of the profile page."},"name":{"type":"string","description":"Full name."},"title":{"type":"string","nullable":true,"description":"Professional title, e.g. Dr. med. dent.; null when none."},"specializations":{"type":"array","items":{"type":"string"},"description":"Specialisations, in the practice's order."},"photo":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Portrait; null when none."},"bookingResourceId":{"type":"string","nullable":true,"description":"Id of the active booking specialist for this profile: an entry of GET /buchung/katalog and the `resource` parameter of /buchung/zeitfenster. null when the profile is not linked to booking or the specialist is inactive.","x-ph-since":"1.0.0"}},"required":["id","slug","path","name","specializations","bookingResourceId"],"description":"A published team member in a list.","x-ph-since":"1.0.0"},"DoctorDetail":{"allOf":[{"$ref":"#/components/schemas/DoctorSummary"},{"type":"object","properties":{"bio":{"type":"string","nullable":true,"description":"Biography, plain text with blank lines between paragraphs; null when empty."},"updatedAt":{"type":"string","format":"date-time","description":"Last change, ISO 8601."}},"required":["updatedAt"]}],"description":"A published team member with the full profile.","x-ph-since":"1.0.0"},"CaseStudySummary":{"type":"object","properties":{"id":{"type":"string","description":"Case study id."},"slug":{"type":"string","description":"Slug from the case study's address."},"path":{"type":"string","description":"Path of the case study page."},"title":{"type":"string","description":"Title."},"summary":{"type":"string","nullable":true,"description":"Short summary; null when empty."},"image":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"List image; null when none."},"publishedAt":{"type":"string","format":"date-time","nullable":true,"description":"Publication date, ISO 8601; null when unknown."}},"required":["id","slug","path","title"],"description":"A published case study in a list.","x-ph-since":"1.0.0"},"CaseStudyDetail":{"type":"object","properties":{"id":{"type":"string","description":"Case study id."},"slug":{"type":"string","description":"Slug from the case study's address."},"path":{"type":"string","description":"Path of the case study page."},"title":{"type":"string","description":"Title."},"summary":{"type":"string","nullable":true,"description":"Short summary; null when empty."},"before":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Image before the treatment; null when none."},"after":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Image after the treatment; null when none."},"bodyBlocks":{"type":"array","items":{"$ref":"#/components/schemas/Block"},"description":"Content blocks of the case study."},"services":{"type":"array","items":{"$ref":"#/components/schemas/RelatedService"},"description":"Treatments linked to the case study (published ones only)."},"publishedAt":{"type":"string","format":"date-time","nullable":true,"description":"Publication date, ISO 8601; null when unknown."},"updatedAt":{"type":"string","format":"date-time","description":"Last change, ISO 8601."}},"required":["id","slug","path","title","bodyBlocks","services","updatedAt"],"description":"A published case study with before and after images and its content.","x-ph-since":"1.0.0"},"PostSummary":{"type":"object","properties":{"id":{"type":"string","description":"Post id."},"slug":{"type":"string","description":"Slug from the post's address."},"path":{"type":"string","description":"Path of the post page."},"title":{"type":"string","description":"Title."},"excerpt":{"type":"string","nullable":true,"description":"Excerpt for lists; null when empty."},"cover":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Cover image; null when none."},"publishedAt":{"type":"string","format":"date-time","nullable":true,"description":"Publication date, ISO 8601; null when unknown."}},"required":["id","slug","path","title"],"description":"A published post in a list.","x-ph-since":"1.0.0"},"PostDetail":{"allOf":[{"$ref":"#/components/schemas/PostSummary"},{"type":"object","properties":{"bodyBlocks":{"type":"array","items":{"$ref":"#/components/schemas/Block"},"description":"Content blocks of the post."},"seoTitle":{"type":"string","nullable":true,"description":"Title for <title> when it differs from `title`; null otherwise."},"seoDescription":{"type":"string","nullable":true,"description":"Meta description; null when empty."},"ogImage":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"og:image of the post; null when none."},"updatedAt":{"type":"string","format":"date-time","description":"Last change, ISO 8601."}},"required":["bodyBlocks","updatedAt"]}],"description":"A published post with its content and SEO fields.","x-ph-since":"1.0.0"},"PageContent":{"type":"object","description":"A page built from blocks in the practice's page editor.","properties":{"kind":{"type":"string","enum":["page"],"description":"Always `page`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"seoTitle":{"type":"string","nullable":true,"description":"Title for <title> when it differs from `title`; null otherwise."},"seoDescription":{"type":"string","nullable":true,"description":"Meta description; null when empty."},"blocks":{"type":"array","items":{"$ref":"#/components/schemas/Block"},"description":"The page's content blocks, in order."}},"required":["kind","path","title","blocks"],"x-ph-since":"1.0.0"},"ServicesIndexContent":{"type":"object","description":"The treatments index (/behandlungen).","properties":{"kind":{"type":"string","enum":["services"],"description":"Always `services`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"eyebrow":{"type":"string","description":"Short label above the title."},"lead":{"type":"string","nullable":true,"description":"Paragraph under the title; null when the practice left it empty."},"services":{"type":"array","items":{"$ref":"#/components/schemas/ServiceSummary"},"description":"Published treatments, in the practice's order."}},"required":["kind","path","title","breadcrumbs","cta","services"],"x-ph-since":"1.0.0"},"ServiceContent":{"type":"object","description":"A treatment page (/behandlungen/{slug}).","properties":{"kind":{"type":"string","enum":["service"],"description":"Always `service`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"service":{"allOf":[{"$ref":"#/components/schemas/ServiceDetail"}],"description":"The treatment."}},"required":["kind","path","title","breadcrumbs","cta","service"],"x-ph-since":"1.0.0"},"TeamContent":{"type":"object","description":"The team page (/team).","properties":{"kind":{"type":"string","enum":["team"],"description":"Always `team`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"eyebrow":{"type":"string","description":"Short label above the title."},"lead":{"type":"string","nullable":true,"description":"Paragraph under the title; null when the practice left it empty."},"doctors":{"type":"array","items":{"$ref":"#/components/schemas/DoctorSummary"},"description":"Published team members, in the practice's order."}},"required":["kind","path","title","breadcrumbs","cta","doctors"],"x-ph-since":"1.0.0"},"DoctorContent":{"type":"object","description":"A team member's profile page (/team/{slug}).","properties":{"kind":{"type":"string","enum":["doctor"],"description":"Always `doctor`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"doctor":{"allOf":[{"$ref":"#/components/schemas/DoctorDetail"}],"description":"The team member."},"services":{"type":"array","items":{"$ref":"#/components/schemas/RelatedService"},"description":"Up to 6 of the practice's treatments shown on the profile."}},"required":["kind","path","title","breadcrumbs","cta","doctor","services"],"x-ph-since":"1.0.0"},"CaseStudiesIndexContent":{"type":"object","description":"The case studies index (/fallbeispiele).","properties":{"kind":{"type":"string","enum":["case-studies"],"description":"Always `case-studies`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"eyebrow":{"type":"string","description":"Short label above the title."},"lead":{"type":"string","nullable":true,"description":"Paragraph under the title; null when the practice left it empty."},"caseStudies":{"type":"array","items":{"$ref":"#/components/schemas/CaseStudySummary"},"description":"Published case studies, newest first."}},"required":["kind","path","title","breadcrumbs","cta","caseStudies"],"x-ph-since":"1.0.0"},"CaseStudyContent":{"type":"object","description":"A case study page (/fallbeispiele/{slug}).","properties":{"kind":{"type":"string","enum":["case-study"],"description":"Always `case-study`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"caseStudy":{"allOf":[{"$ref":"#/components/schemas/CaseStudyDetail"}],"description":"The case study."}},"required":["kind","path","title","breadcrumbs","cta","caseStudy"],"x-ph-since":"1.0.0"},"PostsIndexContent":{"type":"object","description":"The posts index (/aktuelles).","properties":{"kind":{"type":"string","enum":["posts"],"description":"Always `posts`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"eyebrow":{"type":"string","description":"Short label above the title."},"posts":{"type":"array","items":{"$ref":"#/components/schemas/PostSummary"},"description":"Published posts, newest first."}},"required":["kind","path","title","breadcrumbs","cta","posts"],"x-ph-since":"1.0.0"},"PostContent":{"type":"object","description":"A post page (/aktuelles/{slug}).","properties":{"kind":{"type":"string","enum":["post"],"description":"Always `post`."},"path":{"type":"string","description":"Path of the page on the practice's site."},"title":{"type":"string","description":"Page title (the h1 and the <title>)."},"breadcrumbs":{"type":"array","items":{"$ref":"#/components/schemas/Crumb"},"description":"Breadcrumb trail from the home page to this page."},"cta":{"allOf":[{"$ref":"#/components/schemas/Cta"}],"description":"The page's one call to action."},"post":{"allOf":[{"$ref":"#/components/schemas/PostDetail"}],"description":"The post."}},"required":["kind","path","title","breadcrumbs","cta","post"],"x-ph-since":"1.0.0"},"ContentPage":{"description":"The page at a path, told apart by `kind`: a page built from blocks, or one of eight pages the platform builds from the practice's treatments, team, case studies and posts.","oneOf":[{"$ref":"#/components/schemas/PageContent"},{"$ref":"#/components/schemas/ServicesIndexContent"},{"$ref":"#/components/schemas/ServiceContent"},{"$ref":"#/components/schemas/TeamContent"},{"$ref":"#/components/schemas/DoctorContent"},{"$ref":"#/components/schemas/CaseStudiesIndexContent"},{"$ref":"#/components/schemas/CaseStudyContent"},{"$ref":"#/components/schemas/PostsIndexContent"},{"$ref":"#/components/schemas/PostContent"}],"discriminator":{"propertyName":"kind","mapping":{"page":"#/components/schemas/PageContent","services":"#/components/schemas/ServicesIndexContent","service":"#/components/schemas/ServiceContent","team":"#/components/schemas/TeamContent","doctor":"#/components/schemas/DoctorContent","case-studies":"#/components/schemas/CaseStudiesIndexContent","case-study":"#/components/schemas/CaseStudyContent","posts":"#/components/schemas/PostsIndexContent","post":"#/components/schemas/PostContent"}},"x-ph-since":"1.0.0"},"PageListItem":{"type":"object","properties":{"path":{"type":"string","description":"Path on the practice's site; pass it to GET /inhalte."},"title":{"type":"string","description":"Page title."},"type":{"type":"string","enum":["page","services","service","team","doctor","case-studies","case-study","posts","post"],"description":"The `kind` GET /inhalte returns for this path."},"updatedAt":{"type":"string","format":"date-time","description":"Last change, ISO 8601."},"kind":{"type":"string","enum":["page","landing","thanks"],"description":"`page` for a page of the site (block pages and entity pages); `landing` for an ad landing page and `thanks` for its thank-you page. Both are kept out of search engines, the sitemap and the menu, and are listed only when you ask with `?kind=`.","x-ph-since":"1.0.0"}},"required":["path","title","type","updatedAt"],"description":"A published path of the site.","x-ph-since":"1.0.0"},"LeadInput":{"type":"object","description":"A patient enquiry. Every field is optional. Common field-name aliases are accepted (listed per field); unknown fields are kept in the lead's extra fields. The server looks for a contact (a phone with at least 6 digits, or an e-mail) under the aliases, then in the other fields (e.g. phoneNumber, a value that looks like an e-mail). A lead without a contact is stored and the 200 carries warnings: [\"missing_contact\"]; with ?strict=1 it is rejected (400 missing_contact).","properties":{"name":{"type":"string","description":"Patient's name. Aliases: full_name, fullname."},"phone":{"type":"string","description":"Phone number. Aliases: tel, telefon, phone_number."},"email":{"type":"string","description":"E-mail address. Aliases: e-mail, mail."},"message":{"type":"string","description":"The patient's message. Aliases: msg, comment."},"utm_source":{"type":"string","description":"Campaign source, stored with the lead's tracking data."},"utm_medium":{"type":"string","description":"Campaign medium.","x-ph-since":"1.0.0"},"utm_campaign":{"type":"string","description":"Campaign name.","x-ph-since":"1.0.0"},"gclid":{"type":"string","description":"Google Ads click id."},"fbclid":{"type":"string","description":"Meta click id."},"privacyNoticeVersion":{"type":"string","description":"The `version` of the information clause your form showed (from GET /datenschutz). Stored with the submission and its time; without it the submission is stored without a clause version.","x-ph-since":"1.0.0"},"marketingConsent":{"type":"boolean","description":"true when the patient ticked the optional marketing box (label `marketingText` from GET /datenschutz). Recorded for SMS (when there is a phone) and e-mail (when there is an address) together with the clause version. It counts only with a valid `privacyNoticeVersion`: without it no consent is recorded and an earlier opt-out stays. For a returning patient booking an appointment, the e-mail consent covers only the address stored with their lead (or the given address when the lead had none). Omit it, or send false, when the box was not ticked.","x-ph-since":"1.0.0"},"timeRequest":{"allOf":[{"$ref":"#/components/schemas/TimeRequestInput"}],"description":"A request for a time: the patient's preferred windows, for practices that keep their calendar in their own system.","x-ph-since":"1.0.0"}},"additionalProperties":true,"x-ph-since":"1.0.0"},"TimeWindow":{"type":"object","description":"A preferred window in Swiss time (Europe/Zurich): a day and hours from and to. The practice site's form offers parts of the day: morning 08:00–12:00, midday 12:00–15:00, afternoon 15:00–18:00, evening 18:00–20:00 (only those the practice is open). `date` has to be a real calendar day (2026-02-31 is not).","properties":{"date":{"type":"string","format":"date","example":"2026-10-05","description":"Day, YYYY-MM-DD."},"from":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","example":"08:00","description":"Start, HH:MM."},"to":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","example":"12:00","description":"End, HH:MM."}},"required":["date","from","to"],"additionalProperties":false,"x-ph-since":"1.0.0"},"TimeRequestInput":{"type":"object","description":"A request for a time: the patient picks a treatment, optionally a team member, and 1 to 3 preferred windows. It is stored with the lead and the front desk gets an appointment request e-mail (Terminanfrage); in the panel reception confirms one time or proposes another, and the patient hears back by e-mail when the lead has an address. Nothing is synced with the practice's calendar. Windows in the past or more than 180 days ahead are dropped. An invalid value never loses the lead: it is stored without the request and gets warnings: [\"invalid_time_request\"] (with ?strict=1: 400).","properties":{"windows":{"type":"array","minItems":1,"maxItems":3,"items":{"$ref":"#/components/schemas/TimeWindow"},"description":"1 to 3 preferred windows."},"serviceSlug":{"type":"string","description":"Slug of a published treatment (GET /leistungen)."},"doctorSlug":{"type":"string","description":"Slug of a published team member (GET /fachpersonen); omit for any doctor."},"serviceName":{"type":"string","description":"Treatment name as text, when your frontend has no slug."},"doctorName":{"type":"string","description":"Doctor's name as text, when your frontend has no slug."}},"required":["windows"],"x-ph-since":"1.0.0"},"LeadTimeRequest":{"type":"object","description":"A stored request for a time.","properties":{"id":{"type":"string","description":"Id of the request."},"status":{"type":"string","enum":["pending","confirmed","proposed","cancelled"],"description":"pending until reception acts; confirmed (a time was confirmed), proposed (reception proposed another time) or cancelled."},"windows":{"type":"array","items":{"$ref":"#/components/schemas/TimeWindow"},"description":"The windows that were stored."},"service":{"type":"string","nullable":true,"description":"Name of the matched treatment, or the text you sent."},"doctor":{"type":"string","nullable":true,"description":"The doctor (matched or as sent); null for any doctor."}},"required":["id","status","windows","service","doctor"],"x-ph-since":"1.0.0"},"LeadCreated":{"type":"object","properties":{"ok":{"type":"boolean","description":"Always true."},"leadId":{"type":"string","description":"Id of the lead in the practice's CRM."},"warnings":{"type":"array","items":{"type":"string","enum":["missing_contact","invalid_time_request"]},"description":"Only when there is a warning. missing_contact: stored, but without a phone or e-mail, so nobody can call back. invalid_time_request: stored without the request for a time, because `timeRequest` failed validation.","x-ph-since":"1.0.0"},"timeRequest":{"allOf":[{"$ref":"#/components/schemas/LeadTimeRequest"}],"description":"The stored request for a time, when you sent a valid one.","x-ph-since":"1.0.0"}},"required":["ok","leadId"],"description":"The stored lead.","x-ph-since":"1.0.0"},"FeedbackInput":{"type":"object","description":"A remark about the practice's site or about the platform, from your frontend's backend or a browser error report it forwards. Remarks are about the site, never about patients: e-mail addresses, phone numbers and AHV numbers are removed before anything is stored (`redacted` in the response says what went). The page address is stored without its query and #fragment. Unknown fields are ignored.","properties":{"message":{"type":"string","minLength":3,"maxLength":4000,"description":"The remark, 3 to 4000 characters."},"kind":{"type":"string","enum":["remark","api_need","bug"],"default":"remark","description":"remark: content or look. api_need: something your frontend needs from the API. bug: something does not work."},"category":{"type":"string","enum":["content","design","feature","backend","other"],"default":"other","description":"Area: content, design (the look), feature, backend, other."},"source":{"type":"string","enum":["site","factory","client_error"],"default":"site","description":"site: the practice site's backend. client_error: a browser error report your backend forwards. One more value is reserved for the platform team's own tooling.","x-ph-reserved-enum":["factory"]},"pageUrl":{"type":"string","maxLength":2000,"description":"The page the remark is about: an absolute address or a path from /."},"viewport":{"type":"string","pattern":"^\\d{2,5}\\s*[x×]\\s*\\d{2,5}$","example":"1440x900","description":"Viewport of the report, e.g. 1440x900."},"selector":{"type":"string","maxLength":500,"description":"CSS selector of the element the remark is about."},"reporter":{"type":"string","maxLength":120,"description":"Who reports it (e.g. a staff member's name). Kept with the remark, left out of what the platform team sees."},"meta":{"type":"object","description":"Flat object, up to 30 keys and 4000 characters of JSON: values are text (up to 500 characters), numbers, true/false or null. E.g. userAgent, appVersion.","additionalProperties":{"oneOf":[{"type":"string","maxLength":500},{"type":"number"},{"type":"boolean"}],"nullable":true}}},"required":["message"],"x-ph-since":"1.0.0"},"FeedbackCreated":{"type":"object","properties":{"id":{"type":"string","description":"Id of the remark, fb_…"},"status":{"type":"string","enum":["new","synced","ignored"],"description":"new: accepted; it is passed on to the platform team."},"redacted":{"type":"array","items":{"type":"string","enum":["email","phone","ahv"]},"description":"Only when something was removed before storing: email, phone, ahv (an AHV number)."}},"required":["id","status"],"description":"The accepted remark.","x-ph-since":"1.0.0"},"FeedbackError":{"type":"object","description":"A rejected remark. Nothing was stored.","properties":{"error":{"type":"string","description":"Human-readable message; for invalid_body it names the field."},"code":{"type":"string","enum":["invalid_json","invalid_body","feedback_rate_limited"],"description":"invalid_json, invalid_body or feedback_rate_limited."}},"required":["error","code"],"x-ph-since":"1.0.0"},"MediaItem":{"type":"object","description":"An item of the media library. `url` is a path relative to the API: prefix it with the origin you call the API on.","properties":{"id":{"type":"string","description":"Media id."},"r2Key":{"type":"string","description":"Internal storage key. Deprecated: do not use it or build addresses from it; use `url`.","deprecated":true},"url":{"type":"string","description":"Path of the file, e.g. /api/media-file/…"},"filename":{"type":"string","description":"Original file name."},"contentType":{"type":"string","description":"MIME type, e.g. image/webp. Listed by GET /medien."},"size":{"type":"integer","description":"Size in bytes. Listed by GET /medien."},"alt":{"type":"string","nullable":true,"description":"Alternative text; null when empty."},"width":{"type":"integer","nullable":true,"description":"Width in px; null when it could not be read from the file.","x-ph-since":"1.0.0"},"height":{"type":"integer","nullable":true,"description":"Height in px; null when it could not be read.","x-ph-since":"1.0.0"},"aiGenerated":{"type":"boolean","description":"The image was generated by AI.","x-ph-since":"1.0.0"},"caption":{"type":"string","nullable":true,"description":"Caption; null when empty.","x-ph-since":"1.0.0"},"createdAt":{"type":"string","format":"date-time","description":"Upload time, ISO 8601. Sent by GET /medien, not by POST /medien.","x-ph-since":"1.0.0"}},"required":["id","r2Key","url","filename"],"x-ph-since":"1.0.0"},"Slot":{"type":"object","description":"A free slot: UTC times (epoch seconds) plus the local date and minute for display.","properties":{"resourceId":{"type":"string","description":"The specialist, an id from the catalog's `resources`."},"startAt":{"type":"integer","description":"Start, epoch seconds (UTC). Send it as `startAt` to book."},"endAt":{"type":"integer","description":"End, epoch seconds (UTC)."},"localDate":{"type":"string","description":"Local date, YYYY-MM-DD."},"localStartMin":{"type":"integer","description":"Local minute of the day (e.g. 570 = 9:30)."},"locationId":{"type":"string","nullable":true,"description":"The slot's location, an id from the catalog's `locations`; null when the practice has no locations recorded.","x-ph-since":"1.0.0"}},"required":["resourceId","startAt","endAt","localDate","localStartMin"],"x-ph-since":"1.0.0"},"AppointmentInput":{"type":"object","properties":{"serviceId":{"type":"string","description":"The booking service, an id from the catalog's `services`."},"resourceId":{"type":"string","description":"The specialist, the `resourceId` of the slot."},"startAt":{"type":"integer","description":"Start, epoch seconds (UTC): the `startAt` of a free slot."},"patient":{"type":"object","properties":{"name":{"type":"string","description":"Full name."},"phone":{"type":"string","description":"Phone number; the lead is matched by it, however it is written."},"email":{"type":"string","description":"E-mail address, optional."}},"required":["name","phone"],"description":"The patient."},"note":{"type":"string","description":"Note for reception, e.g. the reason for the visit."},"privacyNoticeVersion":{"type":"string","description":"The `version` of the information clause your form showed (from GET /datenschutz). Stored with the submission and its time; without it the submission is stored without a clause version.","x-ph-since":"1.0.0"},"marketingConsent":{"type":"boolean","description":"true when the patient ticked the optional marketing box (label `marketingText` from GET /datenschutz). Recorded for SMS (when there is a phone) and e-mail (when there is an address) together with the clause version. It counts only with a valid `privacyNoticeVersion`: without it no consent is recorded and an earlier opt-out stays. For a returning patient booking an appointment, the e-mail consent covers only the address stored with their lead (or the given address when the lead had none). Omit it, or send false, when the box was not ticked.","x-ph-since":"1.0.0"},"locationId":{"type":"string","description":"The location picked; the slot must be there, otherwise 409.","x-ph-since":"1.0.0"}},"required":["serviceId","resourceId","startAt","patient"],"description":"An appointment to book at a slot from GET /buchung/zeitfenster.","x-ph-since":"1.0.0"},"PrivacyPolicy":{"type":"object","description":"The practice's privacy notice and the information notice for its forms (the information duty of Art. 19 DSG), generated from the practice profile. Bookings and enquiries need no consent: the form shows `clause` with a link to the notice and one optional, unticked marketing box labelled `marketingText`. Send `version` back as `privacyNoticeVersion` with POST /anfragen and POST /buchung/termine.","properties":{"version":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}\\.[0-9a-f]{8}$","description":"Template date plus a digest of the text; it changes with every word of the policy and the clause."},"path":{"type":"string","description":"Path of the policy page on the practice's site (the clause and the footer link to it)."},"title":{"type":"string","description":"Title of the policy page."},"lead":{"type":"string","description":"Sentence under the title."},"clause":{"type":"object","description":"The clause under a form: `text`, then the link `linkLabel` to `href`.","properties":{"text":{"type":"string","description":"Text of the clause."},"linkLabel":{"type":"string","description":"Text of the link to the policy."},"href":{"type":"string","description":"Target of the link to the policy."}},"required":["text","linkLabel","href"]},"marketingText":{"type":"string","description":"Label of the optional marketing box, unticked by default."},"sections":{"type":"array","description":"Sections of the policy. `body` is plain text: a blank line starts a paragraph, lines starting with \"- \" form a list.","items":{"type":"object","properties":{"id":{"type":"string","description":"Stable id of the section, usable as an anchor."},"heading":{"type":"string","description":"Heading of the section."},"body":{"type":"string","description":"Text of the section."}},"required":["id","heading","body"]}},"missing":{"type":"array","items":{"type":"string","enum":["legalName","address","uid","gln","contactEmail"]},"description":"Profile fields the policy needs that the practice has not filled in yet (the policy is served anyway)."}},"required":["version","path","title","lead","clause","marketingText","sections","missing"],"x-ph-since":"1.0.0"},"AppointmentCreated":{"type":"object","properties":{"appointment":{"type":"object","properties":{"id":{"type":"string","description":"Appointment id."},"startAt":{"type":"integer","description":"Start, epoch seconds (UTC)."},"endAt":{"type":"integer","description":"End, epoch seconds (UTC)."},"leadId":{"type":"string","description":"The patient's lead in the practice's CRM."},"status":{"type":"string","enum":["confirmed","pending"],"description":"confirmed: the appointment is booked. pending: the slot is held while the practice confirms it or, with `paymentUrl`, until the patient pays. Tell the patient plainly that the practice will confirm. Absent means confirmed.","x-ph-since":"1.0.0"},"pendingUntil":{"type":"integer","description":"With pending: until when the slot is held, epoch seconds (UTC). Without a decision or the payment by then the visit expires and the patient gets an e-mail.","x-ph-since":"1.0.0"},"paymentUrl":{"type":"string","format":"uri","description":"The practice's Stripe Checkout page for the prepayment or deposit. Send the patient there; after paying they come back to the practice's site (/buchung/zahlung/…). Absent when there is nothing to pay.","x-ph-since":"1.0.0"}},"required":["id","startAt","endAt","leadId"],"description":"The appointment."}},"required":["appointment"],"description":"The booked appointment.","x-ph-since":"1.0.0"},"Appointment":{"type":"object","properties":{"id":{"type":"string","description":"Appointment id."},"startAt":{"type":"integer","description":"Start, epoch seconds (UTC)."},"endAt":{"type":"integer","description":"End, epoch seconds (UTC)."},"status":{"type":"string","enum":["confirmed","cancelled","completed","no_show","pending"],"description":"confirmed, cancelled, completed, no_show or pending (waits for the practice's confirmation or the online payment)."},"pendingUntil":{"type":"integer","nullable":true,"description":"With pending: until when the slot is held, epoch seconds (UTC).","x-ph-since":"1.0.0"},"patientName":{"type":"string","description":"Patient's full name."},"patientPhone":{"type":"string","description":"Patient's phone, digits and +."},"patientEmail":{"type":"string","nullable":true,"description":"Patient's e-mail; null when none."},"note":{"type":"string","nullable":true,"description":"Note from the booking; null when none."},"leadId":{"type":"string","description":"The patient's lead in the practice's CRM."},"serviceId":{"type":"string","description":"The booking service."},"serviceName":{"type":"string","description":"Name of the booking service."},"resourceId":{"type":"string","description":"The specialist."},"resourceName":{"type":"string","description":"Name of the specialist."},"source":{"type":"string","enum":["site","widget","panel","api"],"description":"Where it was booked: site, widget, panel or api."}},"required":["id","startAt","endAt","status","patientName","patientPhone","leadId","serviceId","serviceName","resourceId","resourceName","source"],"description":"An appointment in the practice's calendar. Contains patient data: keep it server-side.","x-ph-since":"1.0.0"},"SlotTaken":{"type":"object","description":"The 409 of POST /buchung/termine: the slot was taken in the meantime, nothing was stored. `slots` is a fresh list of that day's free slots (the shape of /buchung/zeitfenster), so you can offer another one straight away.","properties":{"error":{"type":"string","enum":["slot_taken"],"description":"Always slot_taken."},"slots":{"type":"array","items":{"$ref":"#/components/schemas/Slot"},"description":"That day's free slots right now."}},"required":["error","slots"],"x-ph-since":"1.0.0"},"BookingCatalogService":{"type":"object","description":"An active booking service.","properties":{"id":{"type":"string","description":"Service id: the `service` parameter of /buchung/zeitfenster."},"name":{"type":"string","description":"Name of the service."},"durationMin":{"type":"integer","description":"Length of the visit in minutes."},"priceLabel":{"type":"string","nullable":true,"description":"Price of the appointment for display: the booking service's own price label (e.g. a consultation at CHF 150), else the `priceFromLabel` of the published treatment linked to it (as in /leistungen/{slug}); null when there is neither."},"resourceIds":{"type":"array","items":{"type":"string"},"description":"Active specialists who perform the service (ids from `resources`). An empty list means nobody can be booked for it. Free slots always come from /buchung/zeitfenster."},"serviceSlug":{"type":"string","nullable":true,"description":"Slug of the published treatment linked to the service (GET /leistungen/{slug}); null when no published treatment points at it."},"basicInsurance":{"type":"boolean","description":"true: a service covered by the basic insurance (KVG) that is booked by phone only. Show it for information with a note that booking is by phone and the practice's phone; it cannot be booked online (/buchung/zeitfenster and /buchung/termine answer 400 basic_insurance_phone_only). Absent means false.","x-ph-since":"1.0.0"},"bookingPolicy":{"type":"string","enum":["instant","confirm","pay"],"description":"How a booking ends. instant: booked at once. confirm: the practice confirms the visit (POST /buchung/termine answers status pending; tell the patient the practice will confirm). pay: the patient pays online first (the answer carries `paymentUrl`). Already resolved: a practice without online payments serves pay as confirm. Absent means instant.","x-ph-since":"1.0.0"},"prepay":{"type":"object","nullable":true,"description":"The online prepayment: with pay, and with confirm as a card hold taken only once the practice confirms. null when there is none.","properties":{"amount":{"type":"integer","description":"Amount in Rappen (CHF minor units)."},"currency":{"type":"string","enum":["CHF"],"description":"Always CHF."},"kind":{"type":"string","enum":["full","deposit"],"description":"full: the whole price; deposit: part of it, the rest is paid at the practice."},"label":{"type":"string","description":"Ready-made label, e.g. Anzahlung CHF 50.00."}},"required":["amount","currency","kind","label"],"x-ph-since":"1.0.0"}},"required":["id","name","durationMin","priceLabel","resourceIds","serviceSlug"],"x-ph-since":"1.0.0"},"BookingCatalogResource":{"type":"object","description":"An active specialist who can be booked.","properties":{"id":{"type":"string","description":"Specialist id: the `resource` parameter of /buchung/zeitfenster."},"name":{"type":"string","description":"Full name."},"title":{"type":"string","nullable":true,"description":"Professional title, e.g. Dr. med.; null when none."},"doctorSlug":{"type":"string","nullable":true,"description":"Slug of the published team profile (GET /fachpersonen/{slug}); null when no published profile points at the specialist."},"locationIds":{"type":"array","items":{"type":"string"},"description":"The locations the specialist works in, ids from `locations`.","x-ph-since":"1.0.0"}},"required":["id","name","title","doctorSlug"],"x-ph-since":"1.0.0"},"BookingCatalogLocation":{"type":"object","description":"A location with online booking.","properties":{"id":{"type":"string","description":"Location id: the `location` parameter of /buchung/zeitfenster."},"name":{"type":"string","description":"Name of the location."},"address":{"type":"string","nullable":true,"description":"Street and town; null when not set."}},"required":["id","name","address"],"x-ph-since":"1.0.0"}}},"security":[{"bearer":[]}],"paths":{"/api/v1/inhalte":{"get":{"summary":"Get a page and its site","description":"The page at a path together with the site it belongs to: everything a frontend needs to render that page. `page.kind` tells a page built from blocks from the eight entity pages. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","parameters":[{"name":"path","in":"query","schema":{"type":"string","default":"/"},"description":"Path of the page on the practice's site.","example":"/"}],"responses":{"200":{"description":"The site and the page.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"site":{"allOf":[{"$ref":"#/components/schemas/Site"}],"description":"The practice's site."},"page":{"allOf":[{"$ref":"#/components/schemas/ContentPage"}],"description":"The page at `path`."}},"required":["site","page"]},"examples":{"page":{"summary":"A page built from blocks (kind: page)","value":{"site":{"name":"Praxis Sonnenberg","primaryDomain":"sonnenberg.praxisherz.ch","menu":[{"label":"Startseite","path":"/"},{"label":"Behandlungen","path":"/behandlungen"},{"label":"Team","path":"/team"},{"label":"Termin buchen","path":"/online-termin"}],"theme":{"version":1,"colors":{"background":"#faf7f1","foreground":"#211d18","surface":"#fffdf9","surface2":"#f3ede1","muted":"#f1eadb","mutedForeground":"#655c4e","primary":"#7a5c2e","primaryForeground":"#fffdf9","accent":"#2c2a25","accentForeground":"#f5efe2","border":"#e3dac7"},"colorsDark":{"background":"#171310","foreground":"#f2ecdf","surface":"#201b15","surface2":"#2a241b","muted":"#242017","mutedForeground":"#b5aa93","primary":"#d3ab63","primaryForeground":"#231b0e","accent":"#ece0c6","accentForeground":"#1c1712","border":"#3a3326"},"radius":"0.375rem","radiusPill":"9999px","fontSans":"\"Inter\", \"Inter Fallback\", system-ui, sans-serif","fontHeading":"\"Fraunces\", \"Fraunces Fallback\", Georgia, serif","type":{"baseMinPx":16,"baseMaxPx":17.5,"ratioMin":1.18,"ratioMax":1.3,"headingWeight":560,"bodyWeight":400,"headingLineHeight":1.04,"bodyLineHeight":1.62,"headingTracking":-0.012},"rhythm":{"sectionYMinRem":5,"sectionYMaxRem":10,"container":"76rem"},"elevation":{"shadowColor":"#2a2114","strength":0.16},"motion":{"durationFastMs":180,"durationBaseMs":340,"durationSlowMs":750,"easeOut":"cubic-bezier(0.22, 1, 0.36, 1)","easeSpring":"cubic-bezier(0.32, 1.2, 0.6, 1)"},"imagery":{"overlayColor":"#1a1208","overlayOpacity":0.5,"imageRadius":"0.375rem"}},"profile":{"legalName":"Praxis Sonnenberg AG","phone":"+41 44 123 45 67","email":"empfang@praxis-sonnenberg.example","address":{"street":"Bahnhofstrasse 12","postalCode":"8001","city":"Zürich","district":"City","accessNote":"Eingang im Innenhof, Erdgeschoss, Lift."},"geo":{"lat":47.3717,"lng":8.539},"openingHours":{"mon":[{"opens":"08:00","closes":"20:00"}],"tue":[{"opens":"08:00","closes":"20:00"}],"wed":[{"opens":"08:00","closes":"20:00"}],"thu":[{"opens":"08:00","closes":"20:00"}],"fri":[{"opens":"08:00","closes":"16:00"}],"sat":[{"opens":"09:00","closes":"14:00"}]},"openingHoursNote":"Telefonisch erreichbar bis 19:00.","basicInsurance":{"contract":false},"payments":{"methods":["Bar","Karte","TWINT"]}},"noindex":false},"page":{"kind":"page","path":"/online-termin","title":"Termin online buchen","seoTitle":null,"seoDescription":"Online einen Termin in der Praxis Sonnenberg buchen: Behandlung, Fachperson und freien Termin wählen.","blocks":[{"id":"fw-nav","type":"nav.v1","props":{"variant":"with-cta","brandName":"Praxis Sonnenberg","links":[],"ctaLabel":"Termin vereinbaren","ctaHref":"/online-termin"}},{"id":"fw-booking-hero","type":"hero.v1","props":{"variant":"centered","eyebrow":"Online-Terminbuchung","heading":"Den passenden Termin wählen","subheading":"Der Kalender zeigt nur wirklich freie Termine. Die Bestätigung mit einem Link zum Verschieben kommt sofort per E-Mail."}}]}}},"services":{"summary":"An entity page (kind: services)","value":{"site":{"name":"Praxis Sonnenberg","primaryDomain":"sonnenberg.praxisherz.ch","menu":[{"label":"Startseite","path":"/"},{"label":"Behandlungen","path":"/behandlungen"},{"label":"Team","path":"/team"},{"label":"Termin buchen","path":"/online-termin"}],"theme":{"version":1,"colors":{"background":"#faf7f1","foreground":"#211d18","surface":"#fffdf9","surface2":"#f3ede1","muted":"#f1eadb","mutedForeground":"#655c4e","primary":"#7a5c2e","primaryForeground":"#fffdf9","accent":"#2c2a25","accentForeground":"#f5efe2","border":"#e3dac7"},"colorsDark":{"background":"#171310","foreground":"#f2ecdf","surface":"#201b15","surface2":"#2a241b","muted":"#242017","mutedForeground":"#b5aa93","primary":"#d3ab63","primaryForeground":"#231b0e","accent":"#ece0c6","accentForeground":"#1c1712","border":"#3a3326"},"radius":"0.375rem","radiusPill":"9999px","fontSans":"\"Inter\", \"Inter Fallback\", system-ui, sans-serif","fontHeading":"\"Fraunces\", \"Fraunces Fallback\", Georgia, serif","type":{"baseMinPx":16,"baseMaxPx":17.5,"ratioMin":1.18,"ratioMax":1.3,"headingWeight":560,"bodyWeight":400,"headingLineHeight":1.04,"bodyLineHeight":1.62,"headingTracking":-0.012},"rhythm":{"sectionYMinRem":5,"sectionYMaxRem":10,"container":"76rem"},"elevation":{"shadowColor":"#2a2114","strength":0.16},"motion":{"durationFastMs":180,"durationBaseMs":340,"durationSlowMs":750,"easeOut":"cubic-bezier(0.22, 1, 0.36, 1)","easeSpring":"cubic-bezier(0.32, 1.2, 0.6, 1)"},"imagery":{"overlayColor":"#1a1208","overlayOpacity":0.5,"imageRadius":"0.375rem"}},"profile":{"legalName":"Praxis Sonnenberg AG","phone":"+41 44 123 45 67","email":"empfang@praxis-sonnenberg.example","address":{"street":"Bahnhofstrasse 12","postalCode":"8001","city":"Zürich","district":"City","accessNote":"Eingang im Innenhof, Erdgeschoss, Lift."},"geo":{"lat":47.3717,"lng":8.539},"openingHours":{"mon":[{"opens":"08:00","closes":"20:00"}],"tue":[{"opens":"08:00","closes":"20:00"}],"wed":[{"opens":"08:00","closes":"20:00"}],"thu":[{"opens":"08:00","closes":"20:00"}],"fri":[{"opens":"08:00","closes":"16:00"}],"sat":[{"opens":"09:00","closes":"14:00"}]},"openingHoursNote":"Telefonisch erreichbar bis 19:00.","basicInsurance":{"contract":false},"payments":{"methods":["Bar","Karte","TWINT"]}},"noindex":false},"page":{"kind":"services","path":"/behandlungen","title":"Behandlungen und Leistungen","eyebrow":"Behandlungen","lead":"Das ganze Behandlungsangebot unserer Praxis: Ablauf, Preise und Antworten auf häufige Fragen.","breadcrumbs":[{"label":"Startseite","href":"/"},{"label":"Behandlungen"}],"cta":{"label":"Anrufen und Termin vereinbaren","href":"tel:+41441234567"},"services":[{"id":"svc_beratung","slug":"zahnaerztliche-beratung","name":"Zahnärztliche Beratung","category":"Diagnostik","shortDesc":"Untersuchung, Diagnose und ein schriftlicher Behandlungsplan mit Kostenvoranschlag, der Ausgangspunkt jeder Behandlung.","priceFromLabel":"CHF 120","durationLabel":"Termin 30 Min.","bookingServiceId":"bsvc_beratung","path":"/behandlungen/zahnaerztliche-beratung"},{"id":"svc_impl","slug":"implantat-beratung","name":"Implantat-Beratung mit 3D-Röntgen","category":"Implantologie","shortDesc":"DVT-Aufnahme, Beurteilung des Knochens und ein vollständiger Implantatplan in einem Termin.","priceFromLabel":"CHF 250","durationLabel":"Termin 45 Min.","bookingServiceId":"bsvc_impl","path":"/behandlungen/implantat-beratung"}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"No published page at this path, or the site is not published.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"page":{"summary":"No published page at this path","value":{"error":"Unter diesem Pfad ist keine Seite veröffentlicht."}},"site":{"summary":"The practice's site is not published","value":{"error":"Die Website ist nicht veröffentlicht."}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getContent","tags":["Content"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/content/get-content"}}},"/api/v1/website":{"get":{"summary":"Get the site","description":"The same `site` object GET /inhalte returns next to a page: name, domain, menu, theme and the practice profile (contact, address, hours, basic insurance, payments, logos). Use it for layouts, footers, JSON-LD and metadata without fetching a page. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"The site.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"site":{"allOf":[{"$ref":"#/components/schemas/Site"}],"description":"The practice's site."}},"required":["site"]},"examples":{"site":{"summary":"The site with its practice profile","value":{"site":{"name":"Praxis Sonnenberg","primaryDomain":"sonnenberg.praxisherz.ch","menu":[{"label":"Startseite","path":"/"},{"label":"Behandlungen","path":"/behandlungen"},{"label":"Team","path":"/team"},{"label":"Termin buchen","path":"/online-termin"}],"theme":{"version":1,"colors":{"background":"#faf7f1","foreground":"#211d18","surface":"#fffdf9","surface2":"#f3ede1","muted":"#f1eadb","mutedForeground":"#655c4e","primary":"#7a5c2e","primaryForeground":"#fffdf9","accent":"#2c2a25","accentForeground":"#f5efe2","border":"#e3dac7"},"colorsDark":{"background":"#171310","foreground":"#f2ecdf","surface":"#201b15","surface2":"#2a241b","muted":"#242017","mutedForeground":"#b5aa93","primary":"#d3ab63","primaryForeground":"#231b0e","accent":"#ece0c6","accentForeground":"#1c1712","border":"#3a3326"},"radius":"0.375rem","radiusPill":"9999px","fontSans":"\"Inter\", \"Inter Fallback\", system-ui, sans-serif","fontHeading":"\"Fraunces\", \"Fraunces Fallback\", Georgia, serif","type":{"baseMinPx":16,"baseMaxPx":17.5,"ratioMin":1.18,"ratioMax":1.3,"headingWeight":560,"bodyWeight":400,"headingLineHeight":1.04,"bodyLineHeight":1.62,"headingTracking":-0.012},"rhythm":{"sectionYMinRem":5,"sectionYMaxRem":10,"container":"76rem"},"elevation":{"shadowColor":"#2a2114","strength":0.16},"motion":{"durationFastMs":180,"durationBaseMs":340,"durationSlowMs":750,"easeOut":"cubic-bezier(0.22, 1, 0.36, 1)","easeSpring":"cubic-bezier(0.32, 1.2, 0.6, 1)"},"imagery":{"overlayColor":"#1a1208","overlayOpacity":0.5,"imageRadius":"0.375rem"}},"profile":{"legalName":"Praxis Sonnenberg AG","phone":"+41 44 123 45 67","email":"empfang@praxis-sonnenberg.example","address":{"street":"Bahnhofstrasse 12","postalCode":"8001","city":"Zürich","district":"City","accessNote":"Eingang im Innenhof, Erdgeschoss, Lift."},"geo":{"lat":47.3717,"lng":8.539},"openingHours":{"mon":[{"opens":"08:00","closes":"20:00"}],"tue":[{"opens":"08:00","closes":"20:00"}],"wed":[{"opens":"08:00","closes":"20:00"}],"thu":[{"opens":"08:00","closes":"20:00"}],"fri":[{"opens":"08:00","closes":"16:00"}],"sat":[{"opens":"09:00","closes":"14:00"}]},"openingHoursNote":"Telefonisch erreichbar bis 19:00.","basicInsurance":{"contract":false},"payments":{"methods":["Bar","Karte","TWINT"]}},"noindex":false}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"The practice's site is not published yet.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"site":{"summary":"The practice's site is not published","value":{"error":"Die Website ist nicht veröffentlicht."}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getSite","tags":["Content"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/content/get-site"}}},"/api/v1/standorte":{"get":{"summary":"List locations","description":"The practice's active locations, the primary one first. A practice with one location answers one, with the practice profile's address, phone and hours. Use it for a locations page, a location picker or one JSON-LD entry per location. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"The practice's locations.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"Active locations, the primary one first."}},"required":["locations"]},"examples":{"locations":{"summary":"A practice with two locations","value":{"locations":[{"id":"loc_zuerich","slug":"zuerich","name":"Zürich","primary":true,"phone":"+41 44 123 45 67","address":{"street":"Bahnhofstrasse 12","postalCode":"8001","city":"Zürich"},"openingHours":{"mon":[{"opens":"08:00","closes":"18:00"}]},"basicInsurance":{"contract":false}},{"id":"loc_winterthur","slug":"winterthur","name":"Winterthur","primary":false,"phone":"+41 52 123 45 67","address":{"street":"Marktgasse 20","postalCode":"8400","city":"Winterthur"},"openingHours":{"tue":[{"opens":"09:00","closes":"17:00"}]}}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listLocations","tags":["Content"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/content/list-locations"}}},"/api/v1/datenschutz":{"get":{"summary":"Get the privacy policy","description":"The practice's privacy policy and the information clause for its forms: the same text the practice's site shows at /datenschutz and under its forms. Render your policy page at `path` from `sections`, show `clause` (no required consent) and the optional `marketingText` box under every form, and send `privacyNoticeVersion` = `version` (and `marketingConsent` when the box was ticked) with the submission. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"The policy.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"privacy":{"allOf":[{"$ref":"#/components/schemas/PrivacyPolicy"}],"description":"The policy, the clause and its version."}},"required":["privacy"]},"examples":{"privacy":{"summary":"Policy, clause and version (sections trimmed to two)","value":{"privacy":{"version":"2026-09-30.f0683640","path":"/datenschutz","title":"Datenschutzerklärung","lead":"Wie die Praxis Sonnenberg Personendaten von Personen bearbeitet, die über diese Website Kontakt aufnehmen und Termine vereinbaren.","clause":{"text":"Verantwortlich für die Bearbeitung Ihrer Personendaten ist die Praxis Sonnenberg AG, Bahnhofstrasse 12, 8001 Zürich. Die Angaben aus dem Formular bearbeiten wir, um Ihre Anfrage zu beantworten und Ihren Termin zu vereinbaren und durchzuführen, einschliesslich Bestätigung und Erinnerung. Kontakt in Datenschutzfragen: empfang@praxis-sonnenberg.example. Ihnen stehen das Recht auf Auskunft, Berichtigung und Löschung sowie die weiteren Rechte nach dem DSG zu. Details:","linkLabel":"Datenschutzerklärung","href":"/datenschutz"},"marketingText":"Einwilligung zu Werbung (Praxis Sonnenberg): Ich möchte per SMS oder E-Mail Erinnerungen an Kontrolltermine, Bitten um eine Bewertung und Informationen zum Angebot erhalten. Die Einwilligung ist freiwillig und jederzeit widerrufbar.","sections":[{"id":"verantwortliche","heading":"1. Verantwortliche Stelle","body":"Verantwortlich für die Bearbeitung Ihrer Personendaten ist die Praxis Sonnenberg AG, Bahnhofstrasse 12, 8001 Zürich.\n\nFür Fragen zu Ihren Personendaten:\n- per E-Mail: empfang@praxis-sonnenberg.example\n- per Telefon: +41 44 123 45 67\n- per Post: Praxis Sonnenberg AG, Bahnhofstrasse 12, 8001 Zürich"},{"id":"datenschutzberatung","heading":"2. Datenschutzberatung","body":"Wir haben keine Datenschutzberatung ernannt. Für alle Anliegen zu Ihren Daten genügt eine E-Mail oder ein Anruf, die Kontaktangaben stehen in Ziffer 1."}],"missing":["uid","gln"]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"The practice's site is not published yet.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"site":{"summary":"The practice's site is not published","value":{"error":"Die Website ist nicht veröffentlicht."}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getPrivacy","tags":["Content"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/content/get-privacy"}}},"/api/v1/seiten":{"get":{"summary":"List published pages","description":"Every published path of the site: pages built from blocks and the entity pages (treatments, team, case studies, posts). `type` is the `kind` GET /inhalte returns, so you know what to expect before fetching a page. When a block page and an entity page share a path, the block page wins, as on the practice's site. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","parameters":[{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["page","landing","thanks","all"],"default":"page"},"description":"Which pages to list. Without it (or `page`): the site's pages and entity pages. `landing` or `thanks`: only ad landing pages or only their thank-you pages. `all`: everything.","example":"landing","x-ph-since":"1.0.0"}],"responses":{"200":{"description":"Every published path.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"pages":{"type":"array","items":{"$ref":"#/components/schemas/PageListItem"},"description":"The paths."}},"required":["pages"]},"examples":{"pages":{"summary":"Every published path (trimmed)","value":{"pages":[{"path":"/","title":"Praxis Sonnenberg, Zahnarztpraxis in Zürich","updatedAt":"2026-09-30T14:21:55.000Z","type":"page"},{"path":"/online-termin","title":"Termin online buchen","updatedAt":"2026-09-30T14:21:55.000Z","type":"page"},{"path":"/behandlungen","title":"Behandlungen und Leistungen","type":"services","updatedAt":"2026-09-30T14:21:55.000Z"},{"path":"/behandlungen/zahnaerztliche-beratung","title":"Zahnärztliche Beratung","type":"service","updatedAt":"2026-09-30T14:21:55.000Z"},{"path":"/team","title":"Unser Team","type":"team","updatedAt":"2026-09-30T14:21:55.000Z"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listPages","tags":["Content"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/content/list-pages"}}},"/api/v1/anfragen":{"post":{"summary":"Create a lead","parameters":[{"name":"strict","in":"query","required":false,"schema":{"type":"string","enum":["1","true"]},"description":"Reject a lead without a phone and e-mail (400 missing_contact), and one with an invalid `timeRequest` (400 invalid_time_request), instead of storing it with `warnings`.","example":"1","x-ph-since":"1.0.0"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadInput"},"examples":{"lead":{"summary":"A lead with campaign parameters and an extra field","value":{"name":"Anna Muster","phone":"+41 79 123 45 67","email":"anna.muster@example.com","message":"Bitte um einen Rückruf wegen einer Dentalhygiene.","utm_source":"google","utm_medium":"cpc","utm_campaign":"dentalhygiene-herbst","formular":"landing-dentalhygiene"}},"aliases":{"summary":"Field-name aliases are accepted too","value":{"full_name":"Anna Muster","tel":"+41 79 123 45 67","comment":"Bitte um einen Rückruf."}},"timeRequest":{"summary":"A lead with a request for a time","value":{"name":"Peter Muster","phone":"+41 79 765 43 21","timeRequest":{"windows":[{"date":"2026-10-05","from":"08:00","to":"12:00"},{"date":"2026-10-06","from":"15:00","to":"18:00"}],"serviceSlug":"dentalhygiene","doctorSlug":"laura-keller"}}}}}},"description":"The enquiry as JSON."},"responses":{"200":{"description":"The lead was stored.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadCreated"},"examples":{"created":{"summary":"Lead stored","value":{"ok":true,"leadId":"rS2bIhedVSStSQLc7DL_T"}},"missing_contact":{"summary":"Stored without a phone or e-mail","value":{"ok":true,"leadId":"Sj2mY5tuT1UZP0QfgdvLH","warnings":["missing_contact"]}},"timeRequest":{"summary":"Stored with the request for a time","value":{"ok":true,"leadId":"PydHg_es-Kqw5bs1Y0-wu","timeRequest":{"id":"tqf-L61QFuCLO2ZXCM1OL","status":"pending","windows":[{"date":"2026-10-05","from":"08:00","to":"12:00"},{"date":"2026-10-06","from":"15:00","to":"18:00"}],"service":"Dentalhygiene","doctor":"Dr. med. dent. Laura Keller"}}},"invalid_time_request":{"summary":"Stored without an invalid request for a time","value":{"ok":true,"leadId":"J_ashz8nW11Am5rfS90rB","warnings":["invalid_time_request"]}}}}}},"400":{"description":"The lead was rejected and nothing was stored: the body is not JSON or not an object, or (only with ?strict=1) there is no contact or the request for a time is invalid.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadError"},"examples":{"invalid_json":{"summary":"The body is not JSON","value":{"error":"Ungültiges JSON.","code":"invalid_json"}},"invalid_body":{"summary":"The JSON is not an object","value":{"error":"Der Inhalt muss ein JSON-Objekt mit den Angaben der Anfrage sein.","code":"invalid_body"}},"missing_contact":{"summary":"?strict=1 and no phone or e-mail","value":{"error":"Kontakt erforderlich: Telefon (Feld phone/tel/telefon/phone_number, mindestens 6 Ziffern) oder E-Mail (Feld email/e-mail/mail).","code":"missing_contact"}},"invalid_time_request":{"summary":"?strict=1 and an invalid request for a time","value":{"error":"timeRequest: windows.0.date: Diesen Tag gibt es im Kalender nicht.","code":"invalid_time_request"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"createLead","tags":["Leads"],"description":"Stores a patient enquiry from your own form in the practice's CRM. It follows the same path as an enquiry from the practice's site: reception gets an e-mail, and a configured webhook receives `lead.created`. Call it from your server: the key is a secret. Field names are matched loosely (common aliases, any letter case), unknown fields are kept, and a lead without a phone or e-mail is stored with a warning unless you ask for `?strict=1`.","x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/leads/create-lead"}}},"/api/v1/rueckmeldungen":{"post":{"summary":"Send feedback","description":"Sends a remark about the practice's site or about the API to the platform team, from your frontend's backend or as a browser error report it forwards. Besides the per-minute limit there is a limit of 20 remarks per hour per practice, counted separately for each source (429 feedback_rate_limited with Retry-After). E-mail addresses, phone numbers, AHV numbers and tokens in addresses (e.g. a patient's /buchung/<token> link) are removed before storing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackInput"},"examples":{"bug":{"summary":"A bug report from a practice site's backend","value":{"kind":"bug","category":"feature","message":"Auf dem Handy verdeckt der Buchungsknopf die Fusszeile der Behandlungsseite.","pageUrl":"https://sonnenberg.praxisherz.ch/behandlungen/implantat-beratung?utm_source=google","viewport":"390x844","selector":"[data-cta=sticky-booking]"}}}}},"description":"The remark as JSON."},"responses":{"201":{"description":"The remark was accepted.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackCreated"},"examples":{"created":{"summary":"Remark accepted","value":{"id":"fb_YRNxN-wdvwgXufTq","status":"new"}},"redacted":{"summary":"Accepted after removing an e-mail and a phone number","value":{"id":"fb_m3-ehVwoPDTP7PMR","status":"new","redacted":["email","phone"]}}}}}},"400":{"description":"The remark was rejected: the body is not JSON (invalid_json) or a field is not valid (invalid_body).","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackError"},"examples":{"invalid_json":{"summary":"The body is not JSON","value":{"error":"Ungültiges JSON.","code":"invalid_json"}},"invalid_body":{"summary":"A field failed validation","value":{"error":"category: Invalid option: expected one of \"content\"|\"design\"|\"feature\"|\"backend\"|\"other\"","code":"invalid_body"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"description":"More than 120 requests in this minute, or 20 remarks in this hour for this source (code feedback_rate_limited). Retry after the `Retry-After` seconds.","headers":{"Retry-After":{"schema":{"type":"string"},"description":"Seconds to wait before retrying. 60 for the per-minute limit (a full window; RateLimit-Reset has the exact seconds left)."},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"feedback_rate_limited":{"summary":"The hourly limit for this source is used up","value":{"error":"Limit von 20 Rückmeldungen pro Stunde überschritten.","code":"feedback_rate_limited"}},"rate_limited":{"summary":"Over 120 requests in this minute","value":{"error":"Limit von 120 Aufrufen pro Minute überschritten."}}}}}}},"operationId":"sendFeedback","tags":["Feedback"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/feedback/send-feedback"}}},"/api/v1/medien":{"post":{"summary":"Upload an image","description":"Uploads an image to the practice's media library as multipart/form-data. The field `file` is required: JPEG, PNG, WebP or AVIF, at most 10 MB. SVG answers 415 (an SVG file can carry a script). The file is served at the returned `url`; width and height are read from the file.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"The image: JPEG, PNG, WebP or AVIF, at most 10 MB."},"alt":{"type":"string","maxLength":300,"description":"Alternative text, up to 300 characters."},"caption":{"type":"string","maxLength":300,"description":"Caption, up to 300 characters.","x-ph-since":"1.0.0"},"aiGenerated":{"type":"string","enum":["1","true","0","false"],"description":"1 or true when the image was generated by AI.","x-ph-since":"1.0.0"}},"required":["file"]}}},"description":"multipart/form-data with the file and its optional texts."},"responses":{"201":{"description":"The file was stored.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaItem"},"examples":{"created":{"summary":"File stored","value":{"id":"G4DBcqheiu_BMwt3Y_c_L","r2Key":"KdYaXquTXB/Hp7X9uzN-behandlungszimmer.webp","url":"/api/media-file/KdYaXquTXB/Hp7X9uzN-behandlungszimmer.webp","filename":"behandlungszimmer.webp","alt":"Behandlungszimmer mit Zahnarztstuhl","width":960,"height":600,"aiGenerated":false,"caption":"Behandlungszimmer, Erdgeschoss"}}}}}},"400":{"description":"The body is not multipart/form-data, or it has no `file`.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"missing_file":{"summary":"No file field","value":{"error":"Das Multipart-Feld «file» ist erforderlich."}},"not_multipart":{"summary":"The body is not multipart/form-data","value":{"error":"multipart/form-data erwartet."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"The file is larger than 10 MB.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"too_large":{"summary":"The file is larger than 10 MB","value":{"error":"Die Datei ist grösser als 10 MB."}}}}}},"415":{"description":"The file is not a JPEG, PNG, WebP or AVIF image.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unsupported_type":{"summary":"Not a JPEG, PNG, WebP or AVIF file","value":{"error":"Dateityp image/svg+xml nicht erlaubt, erlaubt sind image/jpeg, image/png, image/webp, image/avif."}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"uploadMedia","tags":["Media"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/media/upload-media"}},"get":{"summary":"List media","responses":{"200":{"description":"The media library.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/MediaItem"},"description":"The files."}},"required":["items"]},"examples":{"items":{"summary":"The media library","value":{"items":[{"id":"G4DBcqheiu_BMwt3Y_c_L","r2Key":"KdYaXquTXB/Hp7X9uzN-behandlungszimmer.webp","filename":"behandlungszimmer.webp","contentType":"image/webp","size":27044,"alt":"Behandlungszimmer mit Zahnarztstuhl","width":960,"height":600,"aiGenerated":false,"caption":"Behandlungszimmer, Erdgeschoss","createdAt":"2026-09-30T14:25:12.000Z","url":"/api/media-file/KdYaXquTXB/Hp7X9uzN-behandlungszimmer.webp"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listMedia","tags":["Media"],"description":"The practice's media library, newest first, up to 200 items.","x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/media/list-media"}}},"/api/v1/buchung/katalog":{"get":{"summary":"Get the booking catalog","description":"Active booking services and specialists, with links to the published treatments and team profiles. The ids are the `service` and `resource` parameters of /buchung/zeitfenster and the `serviceId` and `resourceId` of /buchung/termine. A treatment points at its service with `bookingServiceId`, a team profile at its specialist with `bookingResourceId`. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"Services and specialists.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"services":{"type":"array","items":{"$ref":"#/components/schemas/BookingCatalogService"},"description":"Active booking services."},"resources":{"type":"array","items":{"$ref":"#/components/schemas/BookingCatalogResource"},"description":"Active specialists."},"locations":{"type":"array","items":{"$ref":"#/components/schemas/BookingCatalogLocation"},"description":"Locations someone works in; pass one as `location` to /buchung/zeitfenster."}},"required":["services","resources"]},"examples":{"catalog":{"summary":"Bookable services (trimmed) and specialists, one of them booked by phone only","value":{"services":[{"id":"bsvc_beratung","name":"Zahnärztliche Beratung","durationMin":30,"priceLabel":"CHF 120","resourceIds":["bres_anna","bres_marco","bres_laura"],"serviceSlug":"zahnaerztliche-beratung","basicInsurance":false,"bookingPolicy":"instant","prepay":null},{"id":"bsvc_dentalhygiene","name":"Dentalhygiene (Reinigung, Pulverstrahl, Fluoridierung)","durationMin":60,"priceLabel":"ab CHF 160","resourceIds":["bres_laura"],"serviceSlug":"dentalhygiene","basicInsurance":false,"bookingPolicy":"pay","prepay":{"amount":5000,"currency":"CHF","kind":"deposit","label":"Anzahlung CHF 50"}},{"id":"bsvc_grundversicherung","name":"Zahnbehandlung über die Grundversicherung","durationMin":30,"priceLabel":null,"resourceIds":["bres_marco"],"serviceSlug":null,"basicInsurance":true}],"resources":[{"id":"bres_anna","name":"Anna Meier","title":"Dr. med. dent.","doctorSlug":"anna-meier"},{"id":"bres_marco","name":"Marco Huber","title":"Dr. med. dent.","doctorSlug":"marco-huber"},{"id":"bres_laura","name":"Laura Keller","title":"Dr. med. dent.","doctorSlug":"laura-keller"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getBookingCatalog","tags":["Booking"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/booking/get-booking-catalog"}}},"/api/v1/buchung/zeitfenster":{"get":{"summary":"List free slots","description":"Free slots of a service, counted live from schedules, time off and booked visits; optionally of one specialist. The range is at most 31 days. Without `resource` you get the slots of every specialist who performs the service. Responses are never cached (`Cache-Control: no-store`): availability changes live.","parameters":[{"name":"service","in":"query","required":true,"schema":{"type":"string"},"description":"The booking service id (catalog `services[].id`).","example":"bsvc_beratung"},{"name":"resource","in":"query","schema":{"type":"string"},"description":"The specialist id (catalog `resources[].id`); omit for any specialist.","example":"bres_marco"},{"name":"location","in":"query","schema":{"type":"string"},"description":"The location id (catalog `locations[].id`); omit for every location.","example":"loc_winterthur"},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"First day, a local date YYYY-MM-DD (inclusive).","example":"2026-10-05"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date"},"description":"Last day, a local date YYYY-MM-DD (inclusive).","example":"2026-10-05"}],"responses":{"200":{"description":"Free slots, earliest first.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"slots":{"type":"array","items":{"$ref":"#/components/schemas/Slot"},"description":"The slots."}},"required":["slots"]},"examples":{"slots":{"summary":"Free slots of one specialist on one day (trimmed to three)","value":{"slots":[{"resourceId":"bres_marco","startAt":1791180000,"endAt":1791181800,"localDate":"2026-10-05","localStartMin":480},{"resourceId":"bres_marco","startAt":1791180900,"endAt":1791182700,"localDate":"2026-10-05","localStartMin":495},{"resourceId":"bres_marco","startAt":1791181800,"endAt":1791183600,"localDate":"2026-10-05","localStartMin":510}]}}}}}},"400":{"description":"A parameter is missing or not a date, the range is longer than 31 days, the service or specialist is unknown, or the service is booked by phone only (code basic_insurance_phone_only).","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_params":{"summary":"A required parameter is missing","value":{"error":"Erforderliche Parameter: service, from (YYYY-MM-DD), to (YYYY-MM-DD)."}},"range_too_large":{"summary":"More than 31 days","value":{"error":"Ungültiger Datumsbereich (höchstens 31 Tage)."}},"unknown":{"summary":"Unknown service or specialist","value":{"error":"Leistung oder Fachperson nicht gefunden."}},"basic_insurance_phone_only":{"summary":"A service booked by phone only","value":{"error":"Diese Leistung wird nur telefonisch gebucht.","code":"basic_insurance_phone_only"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getSlots","tags":["Booking"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/booking/get-slots"}}},"/api/v1/buchung/termine":{"get":{"summary":"List appointments","description":"The practice's appointments with the names of the service and the specialist, up to 500, earliest first. `from` and `to` are days (YYYY-MM-DD, inclusive), counted as UTC days. The list holds patient data (name, phone, e-mail): call it from your server only and never show it to the public. Responses are never cached.","parameters":[{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"First day, YYYY-MM-DD (inclusive).","example":"2026-10-05"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"Last day, YYYY-MM-DD (inclusive).","example":"2026-10-05"}],"responses":{"200":{"description":"The appointments.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"appointments":{"type":"array","items":{"$ref":"#/components/schemas/Appointment"},"description":"The appointments."}},"required":["appointments"]},"examples":{"appointments":{"summary":"Appointments on one day","value":{"appointments":[{"id":"Onqp5yPLKb3IFff2WKfnV","startAt":1791181800,"endAt":1791183600,"status":"confirmed","patientName":"Peter Muster","patientPhone":"+41797654321","patientEmail":"peter.muster@example.com","note":"Erster Termin, ein Zahn reagiert auf Kälte.","leadId":"lead_1","serviceId":"bsvc_beratung","serviceName":"Zahnärztliche Beratung","resourceId":"bres_marco","resourceName":"Marco Huber","source":"api"}]}}}}}},"400":{"description":"`from` or `to` is not a date YYYY-MM-DD.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_params":{"summary":"from or to is not YYYY-MM-DD","value":{"error":"from und to müssen Daten im Format YYYY-MM-DD sein."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listAppointments","tags":["Booking"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/booking/list-appointments"}},"post":{"summary":"Book an appointment","description":"Books a visit at a free slot from GET /buchung/zeitfenster, in the same calendar as the practice's site. The patient's lead is found by phone number (however it is written) or created; the booking moves the lead to the booked stage unless it is already at a visited, not interested or unreachable stage (the stage stays, the visit is stored). The front desk gets a new-appointment e-mail with an .ics file. Sending the same data again within 5 minutes returns the existing appointment instead of a second one. If the slot was taken in the meantime you get 409 with that day's free slots. A service whose `bookingPolicy` is confirm or pay answers `status: pending`: the slot is held until `pendingUntil` and the lead's stage changes only once the visit is confirmed; with a prepayment the answer adds `paymentUrl`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentInput"},"examples":{"appointment":{"summary":"Book the slot at 09:00","value":{"serviceId":"bsvc_beratung","resourceId":"bres_marco","startAt":1791181800,"patient":{"name":"Peter Muster","phone":"+41 79 765 43 21","email":"peter.muster@example.com"},"note":"Erster Termin, ein Zahn reagiert auf Kälte."}}}}},"description":"The appointment as JSON."},"responses":{"201":{"description":"The appointment was booked.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentCreated"},"examples":{"created":{"summary":"Appointment booked (a resend within 5 minutes returns the same)","value":{"appointment":{"id":"Onqp5yPLKb3IFff2WKfnV","startAt":1791181800,"endAt":1791183600,"leadId":"lead_1"}}}}}}},"400":{"description":"The body is not JSON, a required field is missing, or the service is booked by phone only (code basic_insurance_phone_only). Nothing was stored.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_json":{"summary":"The body is not JSON","value":{"error":"Ungültiges JSON."}},"missing_fields":{"summary":"serviceId, resourceId or startAt missing","value":{"error":"Erforderlich: serviceId, resourceId, startAt (Epoch-Sekunden, UTC)."}},"missing_patient":{"summary":"patient.name or patient.phone missing","value":{"error":"Erforderlich: patient.name, patient.phone."}},"basic_insurance_phone_only":{"summary":"A service booked by phone only","value":{"error":"Diese Leistung wird nur telefonisch gebucht.","code":"basic_insurance_phone_only"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"description":"The slot is not free (taken in the meantime, or `startAt` is not a slot start). Nothing was stored; offer one of `slots`.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlotTaken"},"examples":{"slot_taken":{"summary":"The slot was taken in the meantime (slots trimmed to three)","value":{"error":"slot_taken","slots":[{"resourceId":"bres_marco","startAt":1791183600,"endAt":1791185400,"localDate":"2026-10-05","localStartMin":540},{"resourceId":"bres_marco","startAt":1791184500,"endAt":1791186300,"localDate":"2026-10-05","localStartMin":555},{"resourceId":"bres_marco","startAt":1791185400,"endAt":1791187200,"localDate":"2026-10-05","localStartMin":570}]}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"createAppointment","tags":["Booking"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/booking/create-appointment"}}},"/api/v1/leistungen":{"get":{"summary":"List treatments","description":"The practice's published treatments, in the practice's order. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"Published treatments.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"services":{"type":"array","items":{"$ref":"#/components/schemas/ServiceSummary"},"description":"The treatments."}},"required":["services"]},"examples":{"services":{"summary":"Published treatments (trimmed to two)","value":{"services":[{"id":"svc_beratung","slug":"zahnaerztliche-beratung","name":"Zahnärztliche Beratung","category":"Diagnostik","shortDesc":"Untersuchung, Diagnose und ein schriftlicher Behandlungsplan mit Kostenvoranschlag, der Ausgangspunkt jeder Behandlung.","priceFromLabel":"CHF 120","durationLabel":"Termin 30 Min.","bookingServiceId":"bsvc_beratung","path":"/behandlungen/zahnaerztliche-beratung"},{"id":"svc_impl","slug":"implantat-beratung","name":"Implantat-Beratung mit 3D-Röntgen","category":"Implantologie","shortDesc":"DVT-Aufnahme, Beurteilung des Knochens und ein vollständiger Implantatplan in einem Termin.","priceFromLabel":"CHF 250","durationLabel":"Termin 45 Min.","bookingServiceId":"bsvc_impl","path":"/behandlungen/implantat-beratung"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listServices","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/list-services"}}},"/api/v1/leistungen/{slug}":{"get":{"summary":"Get a treatment","description":"One published treatment with its content blocks, questions and answers, and related treatments. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]+$"},"description":"Slug of the treatment (lower-case letters, digits, hyphens).","example":"dentalhygiene"}],"responses":{"200":{"description":"The treatment.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"service":{"allOf":[{"$ref":"#/components/schemas/ServiceDetail"}],"description":"The treatment."}},"required":["service"]},"examples":{"service":{"summary":"A treatment page","value":{"service":{"id":"svc_dentalhygiene","slug":"dentalhygiene","path":"/behandlungen/dentalhygiene","name":"Dentalhygiene","category":"Prophylaxe","shortDesc":"Reinigung mit Ultraschall und Pulverstrahl, Fluoridierung und Anleitung zur Mundhygiene, empfohlen alle 6 Monate.","priceFromLabel":"ab CHF 160","durationLabel":"Behandlung 60 Min.","bookingServiceId":"bsvc_dentalhygiene","hero":null,"bodyBlocks":[{"id":"svc-dh-body","type":"rich-text.v1","props":{"variant":"narrow","heading":"Vorbeugen statt behandeln","body":"Eine regelmässige Dentalhygiene entfernt Zahnstein, bevor er das Zahnfleisch entzündet, und macht Karies früh sichtbar, solange eine kleine Füllung genügt."}},{"id":"svc-dh-cta","type":"booking.v1","props":{"heading":"Dentalhygiene buchen","subheading":"Die Behandlung führt Dr. med. dent. Laura Keller durch.","preselectedServiceId":"bsvc_dentalhygiene"}}],"faq":[{"question":"Ist eine Dentalhygiene schmerzhaft?","answer":"Meistens kaum. Bei empfindlichen Zähnen ist eine oberflächliche Betäubung möglich; eine Empfindlichkeit danach klingt innert eines Tages ab."},{"question":"Wie oft ist eine Dentalhygiene sinnvoll?","answer":"In der Regel alle 6 Monate; mit Zahnspange oder Implantaten alle 3 bis 4 Monate."}],"related":[{"id":"svc_kontrolle","slug":"kontrolltermin","path":"/behandlungen/kontrolltermin","name":"Kontrolltermin","priceFromLabel":"CHF 90"},{"id":"svc_beratung","slug":"zahnaerztliche-beratung","path":"/behandlungen/zahnaerztliche-beratung","name":"Zahnärztliche Beratung","priceFromLabel":"CHF 120"}],"updatedAt":"2026-09-30T14:21:55.000Z"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getService","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/get-service"}}},"/api/v1/fachpersonen":{"get":{"summary":"List team members","description":"The practice's published team members, in the practice's order. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"Published team members.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"doctors":{"type":"array","items":{"$ref":"#/components/schemas/DoctorSummary"},"description":"The team."}},"required":["doctors"]},"examples":{"doctors":{"summary":"Published team members (trimmed to two)","value":{"doctors":[{"id":"doc_anna","slug":"anna-meier","name":"Anna Meier","title":"Dr. med. dent.","bookingResourceId":"bres_anna","path":"/team/anna-meier","specializations":["Implantologie","Oralchirurgie","Prothetik"],"photo":null},{"id":"doc_marco","slug":"marco-huber","name":"Marco Huber","title":"Dr. med. dent.","bookingResourceId":"bres_marco","path":"/team/marco-huber","specializations":["Endodontie","Zahnerhaltung"],"photo":null}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listDoctors","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/list-doctors"}}},"/api/v1/fachpersonen/{slug}":{"get":{"summary":"Get a team member","description":"One published team member with the full profile. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]+$"},"description":"Slug of the team member (lower-case letters, digits, hyphens).","example":"laura-keller"}],"responses":{"200":{"description":"The team member.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"doctor":{"allOf":[{"$ref":"#/components/schemas/DoctorDetail"}],"description":"The team member."}},"required":["doctor"]},"examples":{"doctor":{"summary":"A team member's profile","value":{"doctor":{"id":"doc_laura","slug":"laura-keller","path":"/team/laura-keller","name":"Laura Keller","title":"Dr. med. dent.","specializations":["Ästhetische Zahnmedizin","Bleaching","Dentalhygiene"],"photo":null,"bookingResourceId":"bres_laura","bio":"Verbindet Prophylaxe mit ästhetischer Zahnmedizin, von der Dentalhygiene über das Bleaching bis zum Bonding. Den Entwurf des neuen Lächelns bespricht Laura Keller vor jeder Behandlung.","updatedAt":"2026-09-30T14:21:55.000Z"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getDoctor","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/get-doctor"}}},"/api/v1/fallbeispiele":{"get":{"summary":"List case studies","description":"The practice's published case studies, newest first. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"Published case studies.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"caseStudies":{"type":"array","items":{"$ref":"#/components/schemas/CaseStudySummary"},"description":"The case studies."}},"required":["caseStudies"]},"examples":{"caseStudies":{"summary":"Published case studies","value":{"caseStudies":[{"id":"cs_laecheln","slug":"neues-laecheln","title":"Neues Lächeln mit Implantaten","summary":"Patient, 58 Jahre, nach langer Zeit ohne Zahnarztbesuch: sechs Implantate, Vollkeramikkronen und ein neuer Biss in vier Monaten, ohne einen Tag ohne Zähne.","publishedAt":"2026-06-20T00:00:00.000Z","path":"/fallbeispiele/neues-laecheln","image":null},{"id":"cs_bleaching","slug":"bleaching-vor-der-hochzeit","title":"Vor der Hochzeit: Bleaching und Bonding","summary":"Braut, 29 Jahre, sechs Wochen vor der Hochzeit: Bleaching in der Praxis und Bonding der Schneidekanten, ein natürliches Ergebnis ohne Beschleifen der Zähne.","publishedAt":"2026-05-15T00:00:00.000Z","path":"/fallbeispiele/bleaching-vor-der-hochzeit","image":null}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listCaseStudies","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/list-case-studies"}}},"/api/v1/fallbeispiele/{slug}":{"get":{"summary":"Get a case study","description":"One published case study with before and after images, content blocks and linked treatments. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]+$"},"description":"Slug of the case study (lower-case letters, digits, hyphens).","example":"neues-laecheln"}],"responses":{"200":{"description":"The case study.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"caseStudy":{"allOf":[{"$ref":"#/components/schemas/CaseStudyDetail"}],"description":"The case study."}},"required":["caseStudy"]},"examples":{"caseStudy":{"summary":"A case study (body trimmed to one block)","value":{"caseStudy":{"id":"cs_laecheln","slug":"neues-laecheln","path":"/fallbeispiele/neues-laecheln","title":"Neues Lächeln mit Implantaten","summary":"Patient, 58 Jahre, nach langer Zeit ohne Zahnarztbesuch: sechs Implantate, Vollkeramikkronen und ein neuer Biss in vier Monaten, ohne einen Tag ohne Zähne.","before":null,"after":null,"bodyBlocks":[{"id":"cs-laecheln-ausgangslage","type":"rich-text.v1","props":{"variant":"narrow","heading":"Ausgangslage","body":"Herr Brunner kam nach über zehn Jahren ohne Behandlung zu uns: fehlende Seitenzähne, abgenutzte Frontzähne und ein abgesenkter Biss, der das Gesicht veränderte. Am meisten Sorgen machte ihm nicht die Behandlung, sondern die Vorstellung, eine Zeit lang ohne Zähne zu sein."}}],"services":[],"publishedAt":"2026-06-20T00:00:00.000Z","updatedAt":"2026-09-30T14:21:58.000Z"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getCaseStudy","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/get-case-study"}}},"/api/v1/beitraege":{"get":{"summary":"List posts","description":"The practice's published posts, newest first. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","responses":{"200":{"description":"Published posts.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/PostSummary"},"description":"The posts."}},"required":["posts"]},"examples":{"posts":{"summary":"Published posts (trimmed to two)","value":{"posts":[{"id":"post_roentgen","slug":"neues-roentgengeraet","title":"Neues 3D-Röntgengerät in der Praxis","excerpt":"Seit Juni erfolgt die ganze Implantat-Diagnostik direkt in der Praxis: das neue DVT verkürzt die Planung auf einen Termin.","publishedAt":"2026-06-28T00:00:00.000Z","path":"/aktuelles/neues-roentgengeraet","cover":null},{"id":"post_sommer","slug":"dentalhygiene-im-sommer","title":"Zusätzliche Termine für die Dentalhygiene","excerpt":"Bis Ende August bietet die Praxis zusätzliche Termine für die Dentalhygiene am Samstagvormittag an.","publishedAt":"2026-06-15T00:00:00.000Z","path":"/aktuelles/dentalhygiene-im-sommer","cover":null}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listPosts","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/list-posts"}}},"/api/v1/beitraege/{slug}":{"get":{"summary":"Get a post","description":"One published post with its content blocks and SEO fields. Responses are private to the key: `Cache-Control: private, max-age=60`, so cache them on your side for up to a minute.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9-]+$"},"description":"Slug of the post (lower-case letters, digits, hyphens).","example":"neues-roentgengeraet"}],"responses":{"200":{"description":"The post.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"type":"object","properties":{"post":{"allOf":[{"$ref":"#/components/schemas/PostDetail"}],"description":"The post."}},"required":["post"]},"examples":{"post":{"summary":"A post (body trimmed to one paragraph)","value":{"post":{"id":"post_roentgen","slug":"neues-roentgengeraet","path":"/aktuelles/neues-roentgengeraet","title":"Neues 3D-Röntgengerät in der Praxis","excerpt":"Seit Juni erfolgt die ganze Implantat-Diagnostik direkt in der Praxis: das neue DVT verkürzt die Planung auf einen Termin.","cover":null,"publishedAt":"2026-06-28T00:00:00.000Z","bodyBlocks":[{"id":"post-roentgen-body","type":"rich-text.v1","props":{"variant":"narrow","body":"Bisher mussten manche Patientinnen und Patienten für die 3D-Aufnahme in ein externes Institut und mit einer CD zu uns zurückkommen. Seit Juni findet die ganze Diagnostik vor Ort statt: die Aufnahme dauert wenige Sekunden, und das Ergebnis besprechen wir gleich im selben Termin."}}],"seoTitle":null,"seoDescription":"Die Praxis Sonnenberg hat ein neues DVT-Gerät: 3D-Diagnostik vor Ort, geringere Strahlendosis und Implantatplanung in einem Termin.","ogImage":null,"updatedAt":"2026-09-30T14:21:58.000Z"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getPost","tags":["Entities"],"x-ph-since":"1.0.0","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities/get-post"}}}},"tags":[{"name":"Content","description":"The practice's site as data: a page with its site, the site alone, the privacy policy and every published path.","externalDocs":{"url":"https://praxisherz.ch/docs/reference/content"}},{"name":"Entities","description":"Treatments, team members, case studies and posts, each with a list and a detail endpoint.","externalDocs":{"url":"https://praxisherz.ch/docs/reference/entities"}},{"name":"Booking","description":"Bookable services and specialists, free slots, booking an appointment and the practice's appointment list.","externalDocs":{"url":"https://praxisherz.ch/docs/reference/booking"}},{"name":"Leads","description":"Patient enquiries from your own forms, stored in the practice's CRM with the same e-mail to reception as an enquiry from the practice's site.","externalDocs":{"url":"https://praxisherz.ch/docs/reference/leads"}},{"name":"Media","description":"The practice's media library: list the files and upload images.","externalDocs":{"url":"https://praxisherz.ch/docs/reference/media"}},{"name":"Feedback","description":"Remarks about the practice's site or about the API, sent to the platform team. Never patient data.","externalDocs":{"url":"https://praxisherz.ch/docs/reference/feedback"}}],"externalDocs":{"description":"Guides and reference","url":"https://praxisherz.ch/docs"}}