{"openapi":"3.0.3","info":{"title":"pozyskajpacjenta.pl — API v1","version":"1.29.0","description":"The public API of pozyskajpacjenta.pl for your own clinic frontends and integrations: the clinic's pages, treatments, team, booking and enquiries as JSON. Authenticate with the header `Authorization: Bearer <key>` (the clinic's key from the panel, Ustawienia). Limit: 120 requests per minute per key; every response to a valid key carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. The `/api/v1/…` paths also answer as `https://api.pozyskajpacjenta.pl/v1/…`."},"servers":[{"url":"https://api.pozyskajpacjenta.pl"},{"url":"https://app.pozyskajpacjenta.pl","description":"production"}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"The clinic's secret key (pp_live_…) from the panel, Ustawienia. 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":"missing Authorization: Bearer <klucz z panelu Ustawienia>"}},"invalid":{"summary":"Unknown or rotated key","value":{"error":"invalid key"}}}}}},"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":"nie znaleziono"}}}}}},"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":"przekroczono limit 120 zapytań/min"}}}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable message (Polish). 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, nfz_phone_only, feedback_rate_limited.","x-pp-since":"1.3.0"}},"required":["error"],"description":"An error. `error` is a human-readable message in Polish; branch on the HTTP status and, where the endpoint sends one, on `code`.","x-pp-since":"1.1.0"},"LeadError":{"type":"object","description":"A rejected lead. Nothing was stored.","properties":{"error":{"type":"string","description":"Human-readable message (Polish)."},"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-pp-since":"1.3.0"},"MenuItem":{"type":"object","properties":{"label":{"type":"string","description":"Text of the menu item."},"path":{"type":"string","description":"Path on the clinic's site, e.g. /zabiegi."}},"required":["label","path"],"description":"One item of the site's main menu.","x-pp-since":"1.1.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-pp-since":"1.1.0"},"Site":{"type":"object","properties":{"name":{"type":"string","description":"Name of the clinic 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 clinic profile; null until the clinic fills it in.","x-pp-since":"1.4.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-pp-since":"1.5.0"}},"required":["name","menu","theme","profile"],"description":"The clinic's site: name, domain, menu, theme and the clinic profile.","x-pp-since":"1.1.0"},"SiteProfile":{"type":"object","description":"The clinic'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":["stomatologia","medycyna-estetyczna","fizjoterapia","ginekologia","dermatologia","okulistyka","weterynaria","psychologia","centrum-medyczne","ortopedia","pediatria","dietetyka"],"description":"The clinic's specialty: a key of the platform's list (e.g. stomatologia, okulistyka), chosen in the start wizard or by the site migration. Absent when none (or \"other\") was chosen.","x-pp-since":"1.13.0"},"vocabulary":{"type":"string","enum":["medical","optical","vet","business"],"description":"The word family of the clinic's site on the platform: `medical` (dental and medical practices), `optical` (an optician's shop, filed under okulistyka but not a healthcare entity), `vet`, or `business` (any other business that is not a healthcare entity). Pick the UI words (treatment or service, doctor or specialist) and the schema.org types by it. `medical` is also the answer when the platform knows nothing about the business (no specialty or entity kind from the wizard), so do not take it as a decision about the vertical. Absent on an older server: do not assume `medical`.","x-pp-since":"1.26.0"},"legalName":{"type":"string","description":"Registered name of the entity that runs the clinic."},"phone":{"type":"string","description":"Phone number for display, e.g. +48 22 100 20 30. For a tel: link keep the digits and the +."},"email":{"type":"string","format":"email","description":"The clinic's contact e-mail."},"address":{"allOf":[{"$ref":"#/components/schemas/SiteAddress"}],"description":"The clinic's address."},"geo":{"type":"object","properties":{"lat":{"type":"number","description":"Latitude."},"lng":{"type":"number","description":"Longitude."}},"required":["lat","lng"],"description":"Map point of the clinic."},"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."},"nfz":{"type":"object","description":"Contract with the National Health Fund (NFZ).","properties":{"contract":{"type":"boolean","description":"true when the clinic has an NFZ contract."},"note":{"type":"string","description":"Note about the NFZ contract, e.g. which services it covers."}},"required":["contract"]},"payments":{"type":"object","properties":{"methods":{"type":"array","items":{"type":"string"},"description":"Payment methods as the clinic names them, e.g. gotówka, karta, BLIK."},"note":{"type":"string","description":"Note about payments, e.g. instalments."}},"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 clinic'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":"Registry numbers. Show only the ones that are set.","properties":{"rpwdl":{"type":"string","description":"Number in the register of medical entities (RPWDL)."},"nip":{"type":"string","description":"Tax identification number (NIP)."},"regon":{"type":"string","description":"Statistical number (REGON)."},"krs":{"type":"string","description":"Court register number (KRS)."}}},"privacy":{"type":"object","description":"Data for the privacy policy and the information clause. The controller is `legalName` with the address and the `registry` numbers. Full policy text: GET /privacy.","properties":{"contactEmail":{"type":"string","format":"email","description":"E-mail for personal-data requests; the clinic's `email` when unset.","x-pp-since":"1.7.0"},"registeredOffice":{"type":"string","description":"The controller's registered office, when it differs from the clinic address.","x-pp-since":"1.7.0"},"dpoName":{"type":"string","description":"Data protection officer, when one is appointed.","x-pp-since":"1.7.0"},"dpoEmail":{"type":"string","format":"email","description":"The data protection officer's e-mail.","x-pp-since":"1.7.0"}},"x-pp-since":"1.7.0"},"reviewsPolicy":{"type":"string","description":"How the clinic 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-pp-since":"1.4.0"},"SiteAddress":{"type":"object","properties":{"street":{"type":"string","description":"Street and number."},"postalCode":{"type":"string","pattern":"^\\d{2}-\\d{3}$","description":"Postal code, 00-000."},"city":{"type":"string","description":"City."},"district":{"type":"string","description":"District, e.g. Mokotów."},"accessNote":{"type":"string","description":"How to get in: entrance, floor, parking, lift."}},"required":["street","postalCode","city"],"description":"A postal address in Poland.","x-pp-since":"1.4.0"},"OpeningHours":{"type":"object","description":"Opening hours per weekday (keys mon to sun, local time Europe/Warsaw). 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-pp-since":"1.4.0"},"Location":{"type":"object","description":"A location of the clinic: one brand and one site, each location with its own address, phone, hours and NFZ. The primary location carries the clinic profile's values.","properties":{"id":{"type":"string","description":"Location id."},"slug":{"type":"string","description":"The location's address part, unique within the clinic."},"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 clinic 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."},"nfz":{"type":"object","properties":{"contract":{"type":"boolean","description":"true when the location has an NFZ contract."},"note":{"type":"string","description":"Note about the NFZ contract."}},"required":["contract"],"description":"Contract with the National Health Fund (NFZ) at this location."}},"required":["id","slug","name","primary"],"x-pp-since":"1.11.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-pp-since":"1.4.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. Since 1.19.0 a landing page may carry `popup-cta.v1`: it has no content of its own and presents the page's one contact block (the form, the booking widget or the call band, which stays on the page) in a window with a bar at the bottom of the screen; a frontend that skips it loses nothing. Since 1.27.0 a `team-grid.v1` with `fromTeam` and a `pricing.v1` or `services-grid.v1` with `fromServices` take their people or treatments from the clinic's data: the answer has them filled in `members` and `items` (at least one, as before), and these props only tell you the section moves with the team and the treatments. A section with nothing to show is not in the list. Since 1.29.0 every block of a page carries `style.tone`; a sub-page's `nav.v1` and `footer.v1` with `fromHome` arrive with the home page's content, and `contact.v1` (address, phone, e-mail, opening hours) arrives filled from the clinic's data.","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`."},"style":{"type":"object","description":"The section's frame, beside its content. Present on the blocks of a page.","properties":{"tone":{"type":"string","enum":["base","soft","contrast","brand"],"description":"The surface the section stands on: `base` (the page's ground), `soft` (a quiet step off it), `contrast` (the opposite polarity: a dark band on a light theme) or `brand` (the theme's main colour as the ground). The clinic's site alternates `base` and `soft` down the page unless the clinic chose a tone; derive a tone's colours from the theme tokens.","x-pp-since":"1.29.0"}},"x-pp-since":"1.29.0"}},"required":["id","type","props"],"additionalProperties":true,"x-pp-since":"1.1.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. SVG files uploaded before 1.6.1 always come as 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 clinic 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-pp-since":"1.4.0"},"height":{"type":"integer","description":"Height of the original in px.","x-pp-since":"1.4.0"},"aiGenerated":{"type":"boolean","description":"The image was generated by AI: label it next to the image (e.g. in a caption).","x-pp-since":"1.4.0"},"caption":{"type":"string","description":"Caption to show under the image.","x-pp-since":"1.4.0"}},"required":["url"],"x-pp-since":"1.1.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-pp-since":"1.1.0"},"Cta":{"type":"object","description":"The page's one call to action: the clinic'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 clinic's site."}},"required":["label","href"],"x-pp-since":"1.1.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. od 290 zł; null when the clinic shows no price."}},"required":["id","slug","path","name"],"description":"A short reference to a published treatment.","x-pp-since":"1.1.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 clinic's site, e.g. /zabiegi/implanty."},"name":{"type":"string","description":"Treatment name."},"category":{"type":"string","nullable":true,"description":"Category the clinic 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. od 290 zł; null when the clinic shows no price."},"durationLabel":{"type":"string","nullable":true,"description":"Duration label, e.g. zabieg 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 /booking/catalog and the `service` parameter of /booking/slots. null when the treatment is not linked to booking or the linked service is inactive.","x-pp-since":"1.3.0"}},"required":["id","slug","path","name","bookingServiceId"],"description":"A published treatment in a list.","x-pp-since":"1.1.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 clinic 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-pp-since":"1.1.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. lek. dent.; null when none."},"specializations":{"type":"array","items":{"type":"string"},"description":"Specialisations, in the clinic'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 /booking/catalog and the `resource` parameter of /booking/slots. null when the profile is not linked to booking or the specialist is inactive.","x-pp-since":"1.3.0"}},"required":["id","slug","path","name","specializations","bookingResourceId"],"description":"A published team member in a list.","x-pp-since":"1.1.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-pp-since":"1.1.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. Since 1.20.0: a patient's photo added with consent, only when the patient agreed to show it on the clinic's site, served from /api/case-study-photo/{id} (supports ?w= and ?f=, cached briefly). A case study added before 1.20.0 keeps its library image as before until the patient refuses or withdraws. Otherwise null."},"publishedAt":{"type":"string","format":"date-time","nullable":true,"description":"Publication date, ISO 8601; null when unknown."},"resultsNote":{"type":"string","description":"Since 1.21.0: the fixed note to show with every case study (\"Efekty zależą od indywidualnych cech pacjenta.\")."}},"required":["id","slug","path","title"],"description":"A published case study in a list.","x-pp-since":"1.1.0"},"CaseStudyStory":{"type":"object","description":"The case study's description from the owner's wizard, in four short parts.","properties":{"problem":{"type":"string","description":"What the patient came with."},"treatment":{"type":"string","description":"What the clinic did."},"course":{"type":"string","description":"How the treatment went."},"effect":{"type":"string","description":"The result."}},"required":["problem","treatment","course","effect"],"x-pp-since":"1.21.0"},"CaseStudyPair":{"type":"object","description":"A before and after pair of photos. Returned only with the patient's consent for every photo; null without it.","properties":{"before":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Before photo; null without consent."},"after":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"After photo; null without consent."},"view":{"type":"string","nullable":true,"description":"The view (front, profile, smile, area…); null when not set."}},"required":["before","after","view"],"x-pp-since":"1.21.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. Since 1.20.0: a patient's photo added with consent, only when the patient agreed to show it on the clinic's site, served from /api/case-study-photo/{id} (supports ?w= and ?f=, cached briefly). A case study added before 1.20.0 keeps its library image as before until the patient refuses or withdraws. Otherwise null."},"after":{"allOf":[{"$ref":"#/components/schemas/MediaRef"}],"nullable":true,"description":"Image after the treatment. Since 1.20.0: a patient's photo added with consent, only when the patient agreed to show it on the clinic's site, served from /api/case-study-photo/{id} (supports ?w= and ?f=, cached briefly). A case study added before 1.20.0 keeps its library image as before until the patient refuses or withdraws. Otherwise null."},"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."},"resultsNote":{"type":"string","description":"Since 1.21.0: the fixed note to show with every case study (\"Efekty zależą od indywidualnych cech pacjenta.\")."},"story":{"allOf":[{"$ref":"#/components/schemas/CaseStudyStory"}],"nullable":true,"description":"Since 1.21.0: the four-part description; null for older case studies (their text is in bodyBlocks)."},"pairs":{"type":"array","items":{"$ref":"#/components/schemas/CaseStudyPair"},"description":"Since 1.21.0: every before and after pair; empty without the patient's consent."},"sessionsCount":{"type":"integer","nullable":true,"description":"Since 1.21.0: the number of treatment sessions; null when not given."},"resultAfterDays":{"type":"integer","nullable":true,"description":"Since 1.21.0: how many days after the treatment the after photo was taken; null when not given."},"doctorSlug":{"type":"string","nullable":true,"description":"Since 1.21.0: the specialist (GET /doctors/{slug}) when published; null otherwise."}},"required":["id","slug","path","title","bodyBlocks","services","updatedAt"],"description":"A published case study with before and after images and its content.","x-pp-since":"1.1.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-pp-since":"1.1.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-pp-since":"1.1.0"},"PageContent":{"type":"object","description":"A page built from blocks in the clinic's page editor.","properties":{"kind":{"type":"string","enum":["page"],"description":"Always `page`."},"path":{"type":"string","description":"Path of the page on the clinic'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-pp-since":"1.1.0"},"ServicesIndexContent":{"type":"object","description":"The treatments index (/zabiegi).","properties":{"kind":{"type":"string","enum":["services"],"description":"Always `services`."},"path":{"type":"string","description":"Path of the page on the clinic'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 clinic left it empty."},"services":{"type":"array","items":{"$ref":"#/components/schemas/ServiceSummary"},"description":"Published treatments, in the clinic's order."}},"required":["kind","path","title","breadcrumbs","cta","services"],"x-pp-since":"1.1.0"},"ServiceContent":{"type":"object","description":"A treatment page (/zabiegi/{slug}).","properties":{"kind":{"type":"string","enum":["service"],"description":"Always `service`."},"path":{"type":"string","description":"Path of the page on the clinic'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-pp-since":"1.1.0"},"TeamContent":{"type":"object","description":"The team page (/zespol).","properties":{"kind":{"type":"string","enum":["team"],"description":"Always `team`."},"path":{"type":"string","description":"Path of the page on the clinic'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 clinic left it empty."},"doctors":{"type":"array","items":{"$ref":"#/components/schemas/DoctorSummary"},"description":"Published team members, in the clinic's order."}},"required":["kind","path","title","breadcrumbs","cta","doctors"],"x-pp-since":"1.1.0"},"DoctorContent":{"type":"object","description":"A team member's profile page (/zespol/{slug}).","properties":{"kind":{"type":"string","enum":["doctor"],"description":"Always `doctor`."},"path":{"type":"string","description":"Path of the page on the clinic'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 clinic's treatments shown on the profile."}},"required":["kind","path","title","breadcrumbs","cta","doctor","services"],"x-pp-since":"1.1.0"},"CaseStudiesIndexContent":{"type":"object","description":"The case studies index (/realizacje).","properties":{"kind":{"type":"string","enum":["case-studies"],"description":"Always `case-studies`."},"path":{"type":"string","description":"Path of the page on the clinic'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 clinic 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-pp-since":"1.1.0"},"CaseStudyContent":{"type":"object","description":"A case study page (/realizacje/{slug}).","properties":{"kind":{"type":"string","enum":["case-study"],"description":"Always `case-study`."},"path":{"type":"string","description":"Path of the page on the clinic'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-pp-since":"1.1.0"},"PostsIndexContent":{"type":"object","description":"The posts index (/aktualnosci).","properties":{"kind":{"type":"string","enum":["posts"],"description":"Always `posts`."},"path":{"type":"string","description":"Path of the page on the clinic'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-pp-since":"1.1.0"},"PostContent":{"type":"object","description":"A post page (/aktualnosci/{slug}).","properties":{"kind":{"type":"string","enum":["post"],"description":"Always `post`."},"path":{"type":"string","description":"Path of the page on the clinic'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-pp-since":"1.1.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 clinic'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-pp-since":"1.1.0"},"PageListItem":{"type":"object","properties":{"path":{"type":"string","description":"Path on the clinic's site; pass it to GET /content."},"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 /content 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-pp-since":"1.10.0"},"seoTitle":{"type":["string","null"],"description":"The title for search engines as the clinic set it; null when it has none, then use `title`. Only on items of type `page`.","x-pp-since":"1.28.0"},"seoDescription":{"type":["string","null"],"description":"The description for search engines as the clinic set it; null when it has none. Only on items of type `page`.","x-pp-since":"1.28.0"}},"required":["path","title","type","updatedAt"],"description":"A published path of the site.","x-pp-since":"1.1.0"},"RedirectRule":{"type":"object","properties":{"from":{"type":"string","description":"The old path, with its query when the rule has one."},"to":{"type":"string","description":"The path on this site. Chains are already flattened: this is the final address."},"status":{"type":"integer","enum":[301,302],"description":"`301` for a permanent move, `302` for a temporary one."}},"required":["from","to","status"],"description":"An old address of the site and where it leads now.","x-pp-since":"1.28.0"},"LeadInput":{"type":"object","description":"A patient enquiry. Every field is optional. Polish and English field names are accepted (imie, telefon, wiadomosc…); 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, imie, imię, imie_nazwisko."},"phone":{"type":"string","description":"Phone number. Aliases: tel, telefon, phone_number, numer."},"email":{"type":"string","description":"E-mail address. Aliases: e-mail, mail."},"message":{"type":"string","description":"The patient's message. Aliases: wiadomosc, wiadomość, msg, comment, opis."},"utm_source":{"type":"string","description":"Campaign source, stored with the lead's tracking data."},"utm_medium":{"type":"string","description":"Campaign medium.","x-pp-since":"1.1.0"},"utm_campaign":{"type":"string","description":"Campaign name.","x-pp-since":"1.1.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 /privacy). Stored with the submission and its time; without it the submission is stored without a clause version.","x-pp-since":"1.7.0"},"marketingConsent":{"type":"boolean","description":"true when the patient ticked the optional marketing box (label `marketingText` from GET /privacy). 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-pp-since":"1.7.0"},"timeRequest":{"allOf":[{"$ref":"#/components/schemas/TimeRequestInput"}],"description":"A request for a time: the patient's preferred windows, for clinics that keep their calendar in their own system.","x-pp-since":"1.9.0"}},"additionalProperties":true,"x-pp-since":"1.0.0"},"TimeWindow":{"type":"object","description":"A preferred window in Polish time (Europe/Warsaw): a day and hours from and to. The clinic 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 clinic 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-pp-since":"1.9.0"},"TimeRequestInput":{"type":"object","description":"A request for a time: the patient picks a treatment, optionally a doctor, and 1 to 3 preferred windows. It is stored with the lead and reception gets a \"Prośba o termin\" e-mail; 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 clinic'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 /services)."},"doctorSlug":{"type":"string","description":"Slug of a published team member (GET /doctors); 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-pp-since":"1.9.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-pp-since":"1.9.0"},"LeadCreated":{"type":"object","properties":{"ok":{"type":"boolean","description":"Always true."},"leadId":{"type":"string","description":"Id of the lead in the clinic'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-pp-since":"1.3.0"},"timeRequest":{"allOf":[{"$ref":"#/components/schemas/LeadTimeRequest"}],"description":"The stored request for a time, when you sent a valid one.","x-pp-since":"1.9.0"}},"required":["ok","leadId"],"description":"The stored lead.","x-pp-since":"1.1.0"},"FeedbackInput":{"type":"object","description":"A remark about the clinic'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 PESEL-like 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":["uwaga","potrzeba_api","blad"],"default":"uwaga","description":"uwaga: content or look. potrzeba_api: something your frontend needs from the API. blad: something does not work."},"category":{"type":"string","enum":["tresc","wyglad","funkcja","backend","inne"],"default":"inne","description":"Area: tresc (content), wyglad (look), funkcja (a feature), backend, inne (other)."},"source":{"type":"string","enum":["site","fabryka","client_error"],"default":"site","description":"site: the clinic 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-pp-reserved-enum":["fabryka"]},"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-pp-since":"1.6.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","pesel"]},"description":"Only when something was removed before storing: email, phone, pesel."}},"required":["id","status"],"description":"The accepted remark.","x-pp-since":"1.6.0"},"FeedbackError":{"type":"object","description":"A rejected remark. Nothing was stored.","properties":{"error":{"type":"string","description":"Human-readable message (Polish); 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-pp-since":"1.6.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 /media."},"size":{"type":"integer","description":"Size in bytes. Listed by GET /media."},"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-pp-since":"1.4.0"},"height":{"type":"integer","nullable":true,"description":"Height in px; null when it could not be read.","x-pp-since":"1.4.0"},"aiGenerated":{"type":"boolean","description":"The image was generated by AI.","x-pp-since":"1.4.0"},"caption":{"type":"string","nullable":true,"description":"Caption; null when empty.","x-pp-since":"1.4.0"},"createdAt":{"type":"string","format":"date-time","description":"Upload time, ISO 8601. Sent by GET /media (since 1.2.0), not by POST /media.","x-pp-since":"1.9.1"}},"required":["id","r2Key","url","filename"],"x-pp-since":"1.2.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 clinic has no locations recorded.","x-pp-since":"1.12.0"}},"required":["resourceId","startAt","endAt","localDate","localStartMin"],"x-pp-since":"1.2.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 /privacy). Stored with the submission and its time; without it the submission is stored without a clause version.","x-pp-since":"1.7.0"},"marketingConsent":{"type":"boolean","description":"true when the patient ticked the optional marketing box (label `marketingText` from GET /privacy). 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-pp-since":"1.7.0"},"locationId":{"type":"string","description":"The location picked; the slot must be there, otherwise 409.","x-pp-since":"1.12.0"}},"required":["serviceId","resourceId","startAt","patient"],"description":"An appointment to book at a slot from GET /booking/slots.","x-pp-since":"1.2.0"},"PrivacyPolicy":{"type":"object","description":"The clinic's privacy policy and the information clause (art. 13 GDPR) for its forms, generated from the clinic profile. Booking and enquiries rest on art. 6(1)(b) and art. 9(2)(h) GDPR: the form shows `clause` with a link to the policy (no required consent) and one optional, unticked marketing box labelled `marketingText`. Send `version` back as `privacyNoticeVersion` with POST /leads and POST /booking/appointments.","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 clinic'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","nip","rpwdl","contactEmail"]},"description":"Profile fields the policy needs that the clinic has not filled in yet (the policy is served anyway)."}},"required":["version","path","title","lead","clause","marketingText","sections","missing"],"x-pp-since":"1.7.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 clinic's CRM."},"status":{"type":"string","enum":["confirmed","pending"],"description":"confirmed: the visit is booked. pending: the slot is held while the clinic confirms it or, with `paymentUrl`, until the patient pays. Tell the patient plainly that the clinic will confirm. Absent on older servers, which means confirmed.","x-pp-since":"1.13.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. Since 1.23.1 never later than `startAt`: the clinic decides at the latest an hour before the visit (a visit closer than that still leaves at least 15 minutes, but never past its start) and a payment window closes before the visit starts; a visit too close for an online payment (less than Stripe's minimum window) is booked without one.","x-pp-since":"1.13.0"},"paymentUrl":{"type":"string","format":"uri","description":"The clinic's Stripe Checkout page for the prepayment or deposit. Send the patient there; after paying they come back to the clinic's site (/wizyta/platnosc/…). Absent when there is nothing to pay.","x-pp-since":"1.13.0"}},"required":["id","startAt","endAt","leadId"],"description":"The appointment."}},"required":["appointment"],"description":"The booked appointment.","x-pp-since":"1.2.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 or no_show; since 1.13.0 also pending (waits for the clinic's confirmation or the online payment)."},"pendingUntil":{"type":"integer","nullable":true,"description":"With pending: until when the slot is held, epoch seconds (UTC). Since 1.23.1 never later than `startAt`.","x-pp-since":"1.13.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 clinic'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 clinic's calendar. Contains patient data: keep it server-side.","x-pp-since":"1.2.0"},"SlotTaken":{"type":"object","description":"The 409 of POST /booking/appointments: the slot was taken in the meantime, nothing was stored. `slots` is a fresh list of that day's free slots (the shape of /booking/slots), 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-pp-since":"1.2.0"},"BookingCatalogService":{"type":"object","description":"An active booking service.","properties":{"id":{"type":"string","description":"Service id: the `service` parameter of /booking/slots."},"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 visit for display: the booking service's own price label (e.g. a consultation at 300 zł), else the `priceFromLabel` of the published treatment linked to it (as in /services/{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 /booking/slots."},"serviceSlug":{"type":"string","nullable":true,"description":"Slug of the published treatment linked to the service (GET /services/{slug}); null when no published treatment points at it."},"nfz":{"type":"boolean","description":"true: an NFZ (public health fund) service. Show it for information with a note that booking is by phone and the clinic's phone; it cannot be booked online (/booking/slots and /booking/appointments answer 400 nfz_phone_only). Absent on older servers, which means false.","x-pp-since":"1.8.0"},"bookingPolicy":{"type":"string","enum":["instant","confirm","pay"],"description":"How a booking ends. instant: booked at once. confirm: the clinic confirms the visit (POST /booking/appointments answers status pending; tell the patient the clinic will confirm). pay: the patient pays online first (the answer carries `paymentUrl`). Already resolved: a clinic without online payments serves pay as confirm. Absent on older servers, which means instant.","x-pp-since":"1.13.0"},"prepay":{"type":"object","nullable":true,"description":"The online prepayment: with pay, and with confirm as a card hold taken only once the clinic confirms. null when there is none.","properties":{"amount":{"type":"integer","description":"Amount in grosze (PLN minor units)."},"currency":{"type":"string","enum":["PLN"],"description":"Always PLN."},"kind":{"type":"string","enum":["full","deposit"],"description":"full: the whole price; deposit: part of it, the rest is paid at the clinic."},"label":{"type":"string","description":"Ready-made label, e.g. Zaliczka 50,00 zł."}},"required":["amount","currency","kind","label"],"x-pp-since":"1.13.0"}},"required":["id","name","durationMin","priceLabel","resourceIds","serviceSlug"],"x-pp-since":"1.3.0"},"BookingCatalogResource":{"type":"object","description":"An active specialist who can be booked.","properties":{"id":{"type":"string","description":"Specialist id: the `resource` parameter of /booking/slots."},"name":{"type":"string","description":"Full name."},"title":{"type":"string","nullable":true,"description":"Professional title, e.g. lek. dent.; null when none."},"doctorSlug":{"type":"string","nullable":true,"description":"Slug of the published team profile (GET /doctors/{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-pp-since":"1.12.0"}},"required":["id","name","title","doctorSlug"],"x-pp-since":"1.3.0"},"BookingCatalogLocation":{"type":"object","description":"A location with online booking.","properties":{"id":{"type":"string","description":"Location id: the `location` parameter of /booking/slots."},"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-pp-since":"1.12.0"},"ConversationLead":{"type":"object","description":"The patient of a conversation: their lead in the clinic's CRM.","properties":{"id":{"type":"string","description":"The lead id: the `leadId` POST /leads returns and webhook events carry."},"name":{"type":"string","nullable":true,"description":"The patient's name; null when unknown."},"phone":{"type":"string","nullable":true,"description":"The patient's phone; null when unknown."},"email":{"type":"string","nullable":true,"description":"The patient's e-mail; null when unknown."},"message":{"type":"string","nullable":true,"description":"The enquiry the patient sent: a form's message or the first chat message. null when there was none."},"source":{"type":"string","description":"Where the lead came from: form, embed, api, manual, inbound or chat."},"stage":{"type":"string","description":"The lead's stage in the clinic's CRM, e.g. new, scheduled, visited, not_interested."},"locationId":{"type":"string","nullable":true,"description":"The clinic location the enquiry is for (GET /locations); null when not known."},"createdAt":{"type":"string","format":"date-time","description":"When the lead was created."}},"required":["id","name","phone","email","message","source","stage","locationId","createdAt"],"x-pp-since":"1.14.0"},"Conversation":{"type":"object","description":"A conversation: the clinic's thread with one patient, every channel in it. Its id is the `conversationId` of webhook events.","properties":{"id":{"type":"string","description":"Conversation id."},"status":{"type":"string","enum":["open","closed"],"description":"open or closed. The clinic closes a conversation in its inbox; a new message from the patient opens it again."},"lead":{"allOf":[{"$ref":"#/components/schemas/ConversationLead"}],"description":"The patient."},"lastChannel":{"type":"string","enum":["email","sms","whatsapp","instagram","chat","messenger"],"nullable":true,"description":"Channel of the newest message: email, sms, whatsapp, instagram, chat (the chat on the clinic's site) or, since 1.15.0, messenger (the clinic's Facebook Page). null before the first message."},"lastMessageAt":{"type":"string","format":"date-time","nullable":true,"description":"Time of the newest message; null before the first one."},"lastInboundAt":{"type":"string","format":"date-time","nullable":true,"description":"Time of the patient's newest message; null when the patient has not written."},"unreadCount":{"type":"integer","minimum":0,"description":"The patient's messages nobody at the clinic has opened yet."},"createdAt":{"type":"string","format":"date-time","description":"When the conversation began."},"updatedAt":{"type":"string","format":"date-time","description":"The later of the conversation's last change (a message, a read, open or closed) and the lead's (stage, contact data). The `updated_since` filter compares with it; delivery receipts do not change it."}},"required":["id","status","lead","lastChannel","lastMessageAt","lastInboundAt","unreadCount","createdAt","updatedAt"],"x-pp-since":"1.14.0"},"ConversationChannelState":{"type":"object","description":"Whether a message can go out on a channel right now, by the rules POST /conversations/{id}/messages applies.","properties":{"channel":{"type":"string","enum":["email","sms","whatsapp","instagram","chat","messenger"],"description":"The channel: email, sms, whatsapp, instagram, chat (the chat on the clinic's site) or, since 1.15.0, messenger (the clinic's Facebook Page)."},"available":{"type":"boolean","description":"true when a message sent now would be accepted."},"reason":{"type":"string","enum":["lead_archived","opted_out","no_email","email_domain_not_configured","no_phone","no_smsapi_token","chat_not_started","meta_platform_disabled","wa_not_configured","wa_window_closed","ig_not_configured","ig_no_user_id","ig_no_user_thread","ig_window_closed","fb_not_configured","fb_no_user_thread","fb_window_closed","template_not_found","template_channel","address_required","address_unknown","chat_disabled"],"nullable":true,"description":"Why not, when `available` is false: the code a send would get in its 409 (see SendRefused). null when available."}},"required":["channel","available","reason"],"x-pp-since":"1.14.0"},"ConversationIdentity":{"type":"object","description":"One of the patient's addresses on a channel. A patient may have several numbers, e-mail addresses and Messenger or Instagram ids; a message without `to` goes to the one they last wrote from on that channel.","properties":{"channel":{"type":"string","enum":["sms","whatsapp","email","instagram","messenger"],"description":"sms, whatsapp, email, instagram or messenger (a number is listed for SMS and WhatsApp)."},"address":{"type":"string","description":"The number (E.164), the e-mail address (lower case) or the patient's Instagram or Messenger id: the `to` of POST /conversations/{id}/messages."},"primary":{"type":"boolean","description":"The lead's own phone, e-mail address or Instagram id: reminders and the clinic's other automated messages go there."},"verified":{"type":"boolean","description":"The patient wrote from it. An address the clinic added by hand is false until the patient writes from it."},"optedOut":{"type":"boolean","description":"The address opted out of messages on this channel (an SMS STOP from this number): nothing goes to it, the patient's other addresses are not affected."},"replyDefault":{"type":"boolean","description":"A message on this channel without `to` goes here."},"lastInboundAt":{"type":"string","format":"date-time","nullable":true,"description":"The patient's newest message from this address on this channel; null when none."}},"required":["channel","address","primary","verified","optedOut","replyDefault","lastInboundAt"],"x-pp-since":"1.16.0"},"ConversationMergedEnquiry":{"type":"object","description":"The enquiry of a lead the clinic merged into this conversation's lead: the same patient wrote twice (a form and the chat, a Lead Ads form and WhatsApp) and the clinic joined the two cards. It is read from the merged lead while the merge lasts, never copied: when the clinic undoes the merge it is that lead's own `lead.message` again and leaves this list.","properties":{"leadId":{"type":"string","description":"The merged lead's own id."},"text":{"type":"string","description":"What the patient wrote: a form's message, the first chat message, a Lead Ads answer. Written by the patient: show it as their words."},"source":{"type":"string","description":"Where the merged lead came from, like `lead.source`: form, embed, api, manual, inbound or chat."},"channel":{"type":"string","enum":["phone","in_person","missed_call"],"nullable":true,"description":"For a `manual` or `inbound` lead, how it came: phone or in_person (entered by the clinic) or missed_call; null otherwise. New values may be added."},"createdAt":{"type":"string","format":"date-time","description":"When the patient sent it (the merged lead's creation)."}},"required":["leadId","text","source","channel","createdAt"],"x-pp-since":"1.18.0"},"ConversationTemplate":{"type":"object","description":"A reply template of the clinic, filled in for this conversation's patient (name, clinic, address, phone, booking link).","properties":{"id":{"type":"string","description":"Template id: the `templateId` of POST /conversations/{id}/messages."},"name":{"type":"string","description":"The template's name in the clinic's inbox."},"text":{"type":"string","description":"The text the patient would get."},"channels":{"type":"array","items":{"type":"string","enum":["email","sms","whatsapp","instagram","chat","messenger"]},"nullable":true,"description":"The channels the template suits; null means every channel."}},"required":["id","name","text","channels"],"x-pp-since":"1.14.0"},"MessageAttachment":{"type":"object","description":"A file that came with a message: its name, type and size, not the file.","properties":{"filename":{"type":"string","description":"File name."},"mime":{"type":"string","description":"Media type, e.g. image/jpeg."},"size":{"type":"integer","minimum":0,"description":"Size in bytes."}},"required":["filename","mime","size"],"x-pp-since":"1.14.0"},"ConversationMessage":{"type":"object","description":"A message of a conversation, from the patient or from the clinic.","properties":{"id":{"type":"string","description":"Message id: the `messageId` of webhook events."},"conversationId":{"type":"string","description":"The conversation it belongs to."},"channel":{"type":"string","enum":["email","sms","whatsapp","instagram","chat","messenger"],"description":"The channel: email, sms, whatsapp, instagram, chat (the chat on the clinic's site) or, since 1.15.0, messenger (the clinic's Facebook Page)."},"direction":{"type":"string","enum":["inbound","outbound","outbound_echo"],"description":"inbound: from the patient. outbound: from the clinic (the inbox, an automation or this API). outbound_echo: the clinic replied from the WhatsApp or Instagram app itself."},"status":{"type":"string","enum":["queued","sent","delivered","read","failed","received"],"description":"received for every inbound message. Outbound messages move forward only: queued, sent, delivered, read; or failed. queued is a reply from the clinic's panel held on the server for a few seconds before it leaves: it may still vanish from the conversation when the person takes it back, and a message taken back is in no answer of the API. A message you send through the API is never held."},"subject":{"type":"string","nullable":true,"description":"E-mail subject; null on other channels."},"text":{"type":"string","nullable":true,"description":"The text; null for a message with attachments only."},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/MessageAttachment"},"description":"Files that came with the message."},"createdAt":{"type":"string","format":"date-time","description":"When the message was stored."},"sentAt":{"type":"string","format":"date-time","nullable":true,"description":"Outbound: when the provider accepted it; null otherwise."},"deliveredAt":{"type":"string","format":"date-time","nullable":true,"description":"Outbound: when it reached the patient's device, where the channel reports it."},"readAt":{"type":"string","format":"date-time","nullable":true,"description":"Inbound: when someone at the clinic opened it. Outbound: the provider's read receipt, where the channel reports it."}},"required":["id","conversationId","channel","direction","status","subject","text","attachments","createdAt","sentAt","deliveredAt","readAt"],"x-pp-since":"1.14.0"},"SendMessageInput":{"type":"object","description":"The message to send: `text`, or `templateId` (a template from GET /conversations/{id}, filled in for the patient), never both.","properties":{"channel":{"type":"string","enum":["email","sms","whatsapp","instagram","chat","messenger"],"description":"The channel: email, sms, whatsapp, instagram, chat (the chat on the clinic's site) or, since 1.15.0, messenger (the clinic's Facebook Page). Pick one GET /conversations/{id} lists as available."},"text":{"type":"string","minLength":1,"maxLength":4000,"description":"The text, 1 to 4000 characters, trimmed. An SMS carries its first 480 characters."},"templateId":{"type":"string","maxLength":64,"description":"A template id from GET /conversations/{id} `templates`."},"to":{"type":"string","minLength":1,"maxLength":200,"description":"Since 1.16.0: the address to send to, one of the conversation's `identities[].address` on that channel. Omit it to answer where the patient last wrote from (`replyDefault`)."}},"required":["channel"],"x-pp-since":"1.14.0"},"SendRefused":{"type":"object","description":"The 409 of POST /conversations/{id}/messages: the rules do not let this message out now, and nothing was sent. New codes may be added: treat an unknown one as a refusal.","properties":{"error":{"type":"string","description":"Human-readable reason (Polish)."},"code":{"type":"string","enum":["lead_archived","opted_out","no_email","email_domain_not_configured","no_phone","no_smsapi_token","chat_not_started","meta_platform_disabled","wa_not_configured","wa_window_closed","ig_not_configured","ig_no_user_id","ig_no_user_thread","ig_window_closed","fb_not_configured","fb_no_user_thread","fb_window_closed","template_not_found","template_channel","address_required","address_unknown","chat_disabled"],"description":"lead_archived, opted_out (the patient opted out of this channel), no_email, email_domain_not_configured, no_phone, no_smsapi_token (the clinic has no SMS provider), chat_not_started (the patient never wrote on the chat), meta_platform_disabled, wa_not_configured, wa_window_closed (24 hours since the patient's last WhatsApp message), ig_not_configured, ig_no_user_id, ig_no_user_thread (the patient never wrote on Instagram), ig_window_closed (24 hours since the patient's last Instagram message), since 1.15.0 fb_not_configured, fb_no_user_thread (the patient never wrote on Messenger), fb_window_closed (24 hours since the patient's last Messenger message), template_not_found, template_channel (the template is not for this channel), since 1.16.0 address_required (the address the patient last wrote from cannot take the message: pass another one in `to`) and address_unknown (`to` is not one of the patient's addresses on that channel), since 1.17.0 chat_disabled (the chat on the clinic's site is off: the patient would not see the message)."}},"required":["error","code"],"x-pp-since":"1.14.0"},"PatientProcess":{"type":"object","description":"The clinic's patient process: six steps from the enquiry to the next visit, each with its actions. Built from the clinic's settings; never patient data.","properties":{"version":{"type":"integer","enum":[1],"description":"Version of the process shape (1)."},"isDefault":{"type":"boolean","description":"true while the clinic has not changed the default process."},"updatedAt":{"type":"string","format":"date-time","nullable":true,"description":"When the clinic last changed the process; null for the default one."},"speedTargetMinutes":{"type":"integer","description":"Reception's target for calling a new patient back, in minutes."},"steps":{"type":"array","items":{"$ref":"#/components/schemas/ProcessStep"},"description":"The steps, in order."}},"required":["version","isDefault","updatedAt","speedTargetMinutes","steps"],"x-pp-since":"1.24.0"},"ProcessStep":{"type":"object","description":"A step of the patient process.","properties":{"key":{"type":"string","enum":["zapytanie","szybka_odpowiedz","umowienie","przypomnienie","po_wizycie","recall"],"description":"The step's key."},"position":{"type":"integer","description":"The step's number, from 1."},"title":{"type":"string","description":"Name of the step (Polish)."},"summary":{"type":"string","description":"What happens to the patient at this step (Polish)."},"note":{"type":"string","nullable":true,"description":"The clinic's note for reception at this step (Polish), or null."},"actions":{"type":"array","items":{"$ref":"#/components/schemas/ProcessAction"},"description":"The step's actions."}},"required":["key","position","title","summary","note","actions"],"x-pp-since":"1.24.0"},"ProcessAction":{"type":"object","description":"An action of a step: who, through which channels, when, with which words, and its state.","properties":{"key":{"type":"string","enum":["powiadomienie_recepcji","sms_recepcji","sms_potwierdzenie","pierwszy_kontakt","sms_ponowienie","potwierdzenie_wizyty","przypomnienie_24h","przypomnienie_2h","opinia_google","zaproszenie_recall"],"description":"The action's key."},"label":{"type":"string","description":"What happens (Polish)."},"who":{"type":"string","enum":["pacjent","recepcja"],"description":"`pacjent`: the patient gets a message; `recepcja`: reception acts."},"channels":{"type":"array","items":{"type":"string","enum":["email","sms","telefon","panel"]},"description":"The action's channels."},"timing":{"type":"object","description":"When it happens.","properties":{"kind":{"type":"string","enum":["immediately","within_minutes","after_lead_hours","before_visit_hours","after_visit_days","after_visit_months"],"description":"The kind of timing."},"value":{"description":"Minutes, hours or days; for `after_visit_months` the list of months set on the clinic's treatments. Absent for `immediately`."}},"required":["kind"]},"timingLabel":{"type":"string","description":"The timing in words (Polish)."},"template":{"type":"object","description":"The words.","properties":{"source":{"type":"string","enum":["platform","clinic"],"description":"`platform`: the platform's text; `clinic`: the clinic's own."},"text":{"type":"string","nullable":true,"description":"The full SMS text the patient gets; null for e-mails."},"description":{"type":"string","description":"What the message holds (Polish)."}},"required":["source","text","description"]},"state":{"type":"string","enum":["on","off","needs_setup"],"description":"`on`, `off`, or `needs_setup` (switched on but something it needs is missing)."},"stateReason":{"type":"string","nullable":true,"description":"Why it is off or what it lacks (Polish), or null."},"setupHref":{"type":"string","nullable":true,"description":"The clinic panel screen that sets it, or null."}},"required":["key","label","who","channels","timing","timingLabel","template","state","stateReason","setupHref"],"x-pp-since":"1.24.0"},"ProcessError":{"type":"object","description":"The clinic has not switched the patient process on.","properties":{"error":{"type":"string","description":"Message in Polish."},"code":{"type":"string","enum":["feature_disabled"],"description":"Error code."}},"required":["error","code"],"x-pp-since":"1.24.0"},"ConversationError":{"type":"object","description":"An error of the conversation endpoints.","properties":{"error":{"type":"string","description":"Human-readable message (Polish)."},"code":{"type":"string","enum":["feature_disabled","missing_scope","invalid_query","invalid_json","invalid_body","not_found","send_failed"],"description":"feature_disabled, missing_scope, invalid_query, invalid_json, invalid_body, not_found or send_failed."},"scope":{"type":"string","enum":["conversations:read","conversations:write","case_studies:write"],"description":"With missing_scope: the scope the key lacks. Since 1.25.0 the key scopes also include case_studies:write (the clinic's own AI assistant, connected with the key, drafts case studies); no conversation endpoint asks for it."},"retryable":{"type":"boolean","description":"With send_failed: true when trying again later may work (a provider's temporary failure)."}},"required":["error","code"],"x-pp-since":"1.14.0"}}},"security":[{"bearer":[]}],"paths":{"/api/v1/content":{"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 clinic'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 clinic'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":"Klinika Wzorcowa","primaryDomain":"wzorcowa.pozyskajpacjenta.pl","menu":[{"label":"Strona główna","path":"/"},{"label":"Zabiegi","path":"/zabiegi"},{"label":"Zespół","path":"/zespol"},{"label":"Rezerwacja","path":"/rezerwacja"}],"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":"Klinika Wzorcowa sp. z o.o.","phone":"+48 22 100 20 30","email":"recepcja@klinika-wzorcowa.example","address":{"street":"ul. Przykładowa 12","postalCode":"00-950","city":"Warszawa","district":"Mokotów","accessNote":"Wejście od podwórza, parter, winda."},"geo":{"lat":52.1935,"lng":21.0346},"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":"Rejestracja telefoniczna do 19:00.","nfz":{"contract":false},"payments":{"methods":["gotówka","karta","BLIK"]}},"noindex":false},"page":{"kind":"page","path":"/rezerwacja","title":"Rezerwacja wizyty","seoTitle":null,"seoDescription":"Zarezerwuj wizytę w Klinice Wzorcowej online: wybierz zabieg, lekarza i wolny termin.","blocks":[{"id":"fw-nav","type":"nav.v1","props":{"variant":"with-cta","brandName":"Klinika Wzorcowa","links":[],"ctaLabel":"Umów wizytę","ctaHref":"/rezerwacja"}},{"id":"fw-rez-hero","type":"hero.v1","props":{"variant":"centered","eyebrow":"Rezerwacja online","heading":"Wybierz termin, który Ci pasuje","subheading":"Kalendarz pokazuje wyłącznie realnie wolne terminy. Potwierdzenie i link do zmiany rezerwacji dostaniesz e-mailem od razu."}}]}}},"services":{"summary":"An entity page (kind: services)","value":{"site":{"name":"Klinika Wzorcowa","primaryDomain":"wzorcowa.pozyskajpacjenta.pl","menu":[{"label":"Strona główna","path":"/"},{"label":"Zabiegi","path":"/zabiegi"},{"label":"Zespół","path":"/zespol"},{"label":"Rezerwacja","path":"/rezerwacja"}],"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":"Klinika Wzorcowa sp. z o.o.","phone":"+48 22 100 20 30","email":"recepcja@klinika-wzorcowa.example","address":{"street":"ul. Przykładowa 12","postalCode":"00-950","city":"Warszawa","district":"Mokotów","accessNote":"Wejście od podwórza, parter, winda."},"geo":{"lat":52.1935,"lng":21.0346},"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":"Rejestracja telefoniczna do 19:00.","nfz":{"contract":false},"payments":{"methods":["gotówka","karta","BLIK"]}},"noindex":false},"page":{"kind":"services","path":"/zabiegi","title":"Zabiegi i usługi","eyebrow":"Zabiegi","lead":"Pełen zakres leczenia w naszej klinice, poznaj przebieg, ceny i odpowiedzi na najczęstsze pytania.","breadcrumbs":[{"label":"Strona główna","href":"/"},{"label":"Zabiegi"}],"cta":{"label":"Zadzwoń i umów wizytę","href":"tel:+48221002030"},"services":[{"id":"svc_konsultacja","slug":"konsultacja-stomatologiczna","name":"Konsultacja stomatologiczna","category":"Diagnostyka","shortDesc":"Przegląd jamy ustnej, diagnoza i plan leczenia z wyceną na piśmie, punkt startowy każdego leczenia.","priceFromLabel":"200 zł","durationLabel":"wizyta 30 min","bookingServiceId":"bsvc_konsultacja","path":"/zabiegi/konsultacja-stomatologiczna"},{"id":"svc_impl","slug":"konsultacja-implantologiczna","name":"Konsultacja implantologiczna z tomografią 3D","category":"Implantologia","shortDesc":"Tomografia CBCT, ocena warunków kostnych i kompletny plan odbudowy implantologicznej na jednej wizycie.","priceFromLabel":"350 zł","durationLabel":"wizyta 45 min","bookingServiceId":"bsvc_impl","path":"/zabiegi/konsultacja-implantologiczna"}]}}}}}}},"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":"page not found"}},"site":{"summary":"The clinic's site is not published","value":{"error":"site not published"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getContent","tags":["Content"],"x-pp-since":"1.0.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content/get-content"}}},"/api/v1/site":{"get":{"summary":"Get the site","description":"The same `site` object GET /content returns next to a page: name, domain, menu, theme and the clinic profile (contact, address, hours, NFZ, 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 clinic's site."}},"required":["site"]},"examples":{"site":{"summary":"The site with its clinic profile","value":{"site":{"name":"Klinika Wzorcowa","primaryDomain":"wzorcowa.pozyskajpacjenta.pl","menu":[{"label":"Strona główna","path":"/"},{"label":"Zabiegi","path":"/zabiegi"},{"label":"Zespół","path":"/zespol"},{"label":"Rezerwacja","path":"/rezerwacja"}],"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":"Klinika Wzorcowa sp. z o.o.","phone":"+48 22 100 20 30","email":"recepcja@klinika-wzorcowa.example","address":{"street":"ul. Przykładowa 12","postalCode":"00-950","city":"Warszawa","district":"Mokotów","accessNote":"Wejście od podwórza, parter, winda."},"geo":{"lat":52.1935,"lng":21.0346},"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":"Rejestracja telefoniczna do 19:00.","nfz":{"contract":false},"payments":{"methods":["gotówka","karta","BLIK"]}},"noindex":false}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"The clinic'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 clinic's site is not published","value":{"error":"site not published"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getSite","tags":["Content"],"x-pp-since":"1.4.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content/get-site"}}},"/api/v1/locations":{"get":{"summary":"List locations","description":"The clinic's active locations, the primary one first. A clinic with one location answers one, with the clinic 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 clinic'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 clinic with two locations","value":{"locations":[{"id":"loc_krakow","slug":"krakow","name":"Kraków","primary":true,"phone":"+48 12 100 20 30","address":{"street":"ul. Długa 1","postalCode":"30-001","city":"Kraków"},"openingHours":{"mon":[{"opens":"08:00","closes":"18:00"}]},"nfz":{"contract":false}},{"id":"loc_wieliczka","slug":"wieliczka","name":"Wieliczka","primary":false,"phone":"+48 12 555 44 33","address":{"street":"Rynek Górny 2","postalCode":"32-020","city":"Wieliczka"},"openingHours":{"tue":[{"opens":"09:00","closes":"17:00"}]}}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listLocations","tags":["Content"],"x-pp-since":"1.11.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content/list-locations"}}},"/api/v1/privacy":{"get":{"summary":"Get the privacy policy","description":"The clinic's privacy policy and the information clause for its forms: the same text the clinic's site shows at /polityka-prywatnosci 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":"/polityka-prywatnosci","title":"Polityka prywatności","lead":"Jak Klinika Wzorcowa przetwarza dane osobowe osób, które kontaktują się z nami przez tę stronę i umawiają wizyty.","clause":{"text":"Administratorem Twoich danych osobowych jest Klinika Wzorcowa sp. z o.o., ul. Przykładowa 12, 00-950 Warszawa. Dane z formularza przetwarzamy, aby odpowiedzieć na zapytanie oraz umówić i obsłużyć wizytę, w tym wysłać potwierdzenie i przypomnienie (art. 6 ust. 1 lit. b i art. 9 ust. 2 lit. h RODO). Kontakt w sprawie danych: recepcja@klinika-wzorcowa.example. Masz prawo dostępu do danych, ich sprostowania, usunięcia i ograniczenia przetwarzania, prawo sprzeciwu oraz skargi do Prezesa UODO. Szczegóły:","linkLabel":"polityka prywatności","href":"/polityka-prywatnosci"},"marketingText":"Zgoda marketingowa (Klinika Wzorcowa): chcę otrzymywać SMS-em lub e-mailem przypomnienia o wizytach kontrolnych, prośby o opinię i informacje o ofercie. Zgoda jest dobrowolna i mogę ją w każdej chwili wycofać.","sections":[{"id":"administrator","heading":"1. Administrator danych","body":"Administratorem Twoich danych osobowych jest Klinika Wzorcowa sp. z o.o., ul. Przykładowa 12, 00-950 Warszawa.\n\nW sprawach dotyczących Twoich danych osobowych skontaktujesz się z nami:\n- e-mailem: recepcja@klinika-wzorcowa.example\n- telefonicznie: +48 22 100 20 30\n- listownie: Klinika Wzorcowa sp. z o.o., ul. Przykładowa 12, 00-950 Warszawa"},{"id":"iod","heading":"2. Inspektor ochrony danych","body":"Nie wyznaczyliśmy inspektora ochrony danych. We wszystkich sprawach dotyczących Twoich danych napisz lub zadzwoń do nas, dane kontaktowe są w pkt 1."}],"missing":["nip","rpwdl"]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"The clinic'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 clinic's site is not published","value":{"error":"site not published"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getPrivacy","tags":["Content"],"x-pp-since":"1.7.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content/get-privacy"}}},"/api/v1/process":{"get":{"summary":"Get the patient process","description":"The clinic's patient process from the enquiry to the next visit: six steps, each with its actions (who acts, the channels, when, the full SMS text or what an e-mail holds) and their state, built from the clinic's settings exactly as its panel shows them (Ustawienia › Proces pacjenta). Read only, never patient data. Use it to tell patients on your own frontend what happens after they write (for example \"we call back within 15 minutes\"). Answers 403 with the code `feature_disabled` until the clinic has the patient process switched on. 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 process.","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":{"process":{"allOf":[{"$ref":"#/components/schemas/PatientProcess"}],"description":"The clinic's patient process."}},"required":["process"]},"examples":{"process":{"summary":"A clinic's own process: SMS on, its own confirmation text, a 15-minute target","value":{"process":{"version":1,"isDefault":false,"updatedAt":"2026-10-22T09:30:00.000Z","speedTargetMinutes":15,"steps":[{"key":"zapytanie","position":1,"title":"Nowe zapytanie","summary":"Pacjent wysyła formularz, dzwoni albo pisze. Recepcja dowiaduje się o tym od razu.","note":null,"actions":[{"key":"powiadomienie_recepcji","label":"E-mail do recepcji o nowym zapytaniu","who":"recepcja","channels":["email","panel"],"timing":{"kind":"immediately"},"template":{"source":"platform","text":null,"description":"Kto pisze, czego dotyczy zapytanie i link do pacjenta w panelu."},"state":"on","stateReason":null,"setupHref":"/panel/settings/powiadomienia","timingLabel":"od razu"},{"key":"sms_recepcji","label":"SMS do recepcji o nowym zapytaniu","who":"recepcja","channels":["sms"],"timing":{"kind":"immediately"},"template":{"source":"platform","text":null,"description":"Krótka informacja o nowym zapytaniu na telefon recepcji."},"state":"off","stateReason":null,"setupHref":"/panel/settings/powiadomienia#sms","timingLabel":"od razu"}]},{"key":"szybka_odpowiedz","position":2,"title":"Szybka odpowiedź","summary":"Pacjent dostaje potwierdzenie, a recepcja oddzwania, zanim pacjent zadzwoni gdzie indziej.","note":"Zadzwoń, przedstaw się, zapytaj, czego potrzebuje pacjent, i zaproponuj dwa terminy.","actions":[{"key":"sms_potwierdzenie","label":"SMS do pacjenta: otrzymaliśmy zapytanie","who":"pacjent","channels":["sms"],"timing":{"kind":"immediately"},"template":{"source":"clinic","text":"Klinika Wzorcowa: dziękujemy za wiadomość. Oddzwonimy w ciągu kwadransa, w godzinach otwarcia.","description":"Treść kliniki"},"state":"on","stateReason":null,"setupHref":"/panel/settings/powiadomienia#wiadomosci-do-pacjentow","timingLabel":"od razu"},{"key":"pierwszy_kontakt","label":"Recepcja dzwoni do pacjenta","who":"recepcja","channels":["telefon","panel"],"timing":{"kind":"within_minutes","value":15},"template":{"source":"platform","text":null,"description":"Zapytanie czeka w Recepcji; po tym czasie dostaje oznaczenie, że czeka za długo."},"state":"on","stateReason":null,"setupHref":"/panel/recepcja","timingLabel":"w ciągu 15 minut"},{"key":"sms_ponowienie","label":"SMS do pacjenta, gdy zapytanie wciąż czeka","who":"pacjent","channels":["sms"],"timing":{"kind":"after_lead_hours","value":4},"template":{"source":"platform","text":"Klinika Wzorcowa: wciąż chcemy porozmawiać o Twoim zapytaniu. Zadzwoń do nas: +48 22 100 20 30 albo odbierz, gdy oddzwonimy.","description":"Tekst platformy"},"state":"on","stateReason":null,"setupHref":"/panel/settings/powiadomienia#wiadomosci-do-pacjentow","timingLabel":"po 4 godzinach, między 8:00 a 20:00"}]},{"key":"umowienie","position":3,"title":"Umówienie wizyty","summary":"Pacjent wybiera termin na stronie albo umawia się z recepcją i dostaje potwierdzenie.","note":null,"actions":[{"key":"potwierdzenie_wizyty","label":"E-mail do pacjenta z potwierdzeniem wizyty","who":"pacjent","channels":["email"],"timing":{"kind":"immediately"},"template":{"source":"platform","text":null,"description":"Data, godzina, miejsce, plik do kalendarza i link do zmiany terminu."},"state":"on","stateReason":null,"setupHref":"/panel/settings/rezerwacja","timingLabel":"od razu"}]},{"key":"przypomnienie","position":4,"title":"Przypomnienie i przyjście","summary":"Przed wizytą pacjent dostaje przypomnienie, żeby przyszedł albo zwolnił termin.","note":null,"actions":[{"key":"przypomnienie_24h","label":"Przypomnienie dzień przed wizytą","who":"pacjent","channels":["email","sms"],"timing":{"kind":"before_visit_hours","value":24},"template":{"source":"platform","text":null,"description":"Data, godzina i miejsce wizyty z linkiem do zmiany terminu."},"state":"on","stateReason":null,"setupHref":"/panel/settings/rezerwacja#zasady","timingLabel":"dzień przed wizytą (24 h)"},{"key":"przypomnienie_2h","label":"Przypomnienie w dniu wizyty","who":"pacjent","channels":["email","sms"],"timing":{"kind":"before_visit_hours","value":2},"template":{"source":"platform","text":null,"description":"Data, godzina i miejsce wizyty z linkiem do zmiany terminu."},"state":"on","stateReason":null,"setupHref":"/panel/settings/rezerwacja#zasady","timingLabel":"2 h przed wizytą"}]},{"key":"po_wizycie","position":5,"title":"Po wizycie","summary":"Dzień po wizycie pacjent może zostawić opinię w Google.","note":null,"actions":[{"key":"opinia_google","label":"E-mail z prośbą o opinię w Google","who":"pacjent","channels":["email"],"timing":{"kind":"after_visit_days","value":1},"template":{"source":"platform","text":null,"description":"Do każdego pacjenta ze zgodą na e-mail, najwyżej raz na pół roku, bez wybierania po ocenie."},"state":"on","stateReason":null,"setupHref":"/panel/settings/powiadomienia#opinie","timingLabel":"dzień po wizycie"}]},{"key":"recall","position":6,"title":"Kolejna wizyta","summary":"Gdy zbliża się czas kolejnej wizyty (np. higienizacji), pacjent dostaje zaproszenie.","note":null,"actions":[{"key":"zaproszenie_recall","label":"Zaproszenie na kolejną wizytę","who":"pacjent","channels":["email","sms"],"timing":{"kind":"after_visit_months","value":[6,12]},"template":{"source":"platform","text":null,"description":"Zaproszenie z linkiem do strony zabiegu, tylko do pacjentów ze zgodą na wiadomości."},"state":"on","stateReason":null,"setupHref":"/panel/settings/rezerwacja#zabiegi","timingLabel":"po 6, 12 miesiącach (zależnie od zabiegu)"}]}]}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The clinic has not switched the patient process on (code feature_disabled).","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/ProcessError"},"examples":{"disabled":{"summary":"The clinic has not switched the patient process on","value":{"error":"Proces pacjenta nie jest włączony dla tej placówki.","code":"feature_disabled"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getProcess","tags":["Content"],"x-pp-since":"1.24.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content/get-process"}}},"/api/v1/pages":{"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 /content 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 clinic's site. Things moved to the trash in the clinic's panel are not listed; their address answers 404, and after a restore they come back under the same address. 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-pp-since":"1.10.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":"Klinika Wzorcowa, stomatologia w Warszawie","updatedAt":"2026-09-30T14:21:55.000Z","type":"page"},{"path":"/rezerwacja","title":"Rezerwacja wizyty","updatedAt":"2026-09-30T14:21:55.000Z","type":"page"},{"path":"/zabiegi","title":"Zabiegi i usługi","type":"services","updatedAt":"2026-09-30T14:21:55.000Z"},{"path":"/zabiegi/konsultacja-stomatologiczna","title":"Konsultacja stomatologiczna","type":"service","updatedAt":"2026-09-30T14:21:55.000Z"},{"path":"/zespol","title":"Nasi lekarze","type":"team","updatedAt":"2026-09-30T14:21:55.000Z"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listPages","tags":["Content"],"x-pp-since":"1.0.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content/list-pages"}}},"/api/v1/redirects":{"get":{"summary":"List the site's redirects","description":"The old addresses the clinic's site serves now and where each leads: the snapshot the site itself reads. A sub-page whose address changes in the panel leaves one behind, so a frontend of its own needs this list to answer the old address instead of a 404. An empty list when the clinic has no redirects. 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's redirects.","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":{"redirects":{"type":"array","items":{"$ref":"#/components/schemas/RedirectRule"},"description":"The old addresses and where they lead."}},"required":["redirects"]},"examples":{"redirects":{"summary":"A sub-page moved to a new address, and an old address of the clinic's site","value":{"redirects":[{"from":"/implanty","to":"/implanty-zebow","status":301},{"from":"/uslugi/implanty","to":"/implanty-zebow","status":301}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listRedirects","tags":["Content"],"x-pp-since":"1.28.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content/list-redirects"}}},"/api/v1/leads":{"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-pp-since":"1.3.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 Kowalska","phone":"+48 600 100 200","email":"anna.kowalska@example.com","message":"Proszę o kontakt w sprawie higienizacji.","utm_source":"google","utm_medium":"cpc","utm_campaign":"higienizacja-jesien","formularz":"landing-higienizacja"}},"aliases":{"summary":"Polish field names are accepted too","value":{"imie":"Anna Kowalska","telefon":"+48 600 100 200","wiadomosc":"Proszę o kontakt."}},"timeRequest":{"summary":"A lead with a request for a time (API 1.9.0)","value":{"name":"Jan Nowak","phone":"+48 600 100 200","timeRequest":{"windows":[{"date":"2026-10-05","from":"08:00","to":"12:00"},{"date":"2026-10-06","from":"15:00","to":"18:00"}],"serviceSlug":"higienizacja","doctorSlug":"maria-zielinska"}}}}}},"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":"Higienizacja","doctor":"lek. dent. Maria Zielińska"}}},"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":"invalid JSON","code":"invalid_json"}},"invalid_body":{"summary":"The JSON is not an object","value":{"error":"treść musi być obiektem JSON z danymi leada","code":"invalid_body"}},"missing_contact":{"summary":"?strict=1 and no phone or e-mail","value":{"error":"wymagany kontakt: telefon (pole phone/tel/telefon/phone_number/numer, min. 6 cyfr) albo e-mail (pole 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: nie ma takiego dnia w kalendarzu","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 clinic's CRM. It follows the same path as an enquiry from the clinic'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 (Polish and English 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-pp-since":"1.0.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/leads/create-lead"}}},"/api/v1/feedback":{"post":{"summary":"Send feedback","description":"Sends a remark about the clinic'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 clinic, counted separately for each source (429 feedback_rate_limited with Retry-After). E-mail addresses, phone numbers, PESEL-like numbers and tokens in addresses (e.g. a patient's /wizyta/<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 clinic site's backend","value":{"kind":"blad","category":"funkcja","message":"Na telefonie przycisk rezerwacji zasłania stopkę strony zabiegu.","pageUrl":"https://wzorcowa.pozyskajpacjenta.pl/zabiegi/implanty?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":"invalid JSON","code":"invalid_json"}},"invalid_body":{"summary":"A field failed validation","value":{"error":"category: Invalid option: expected one of \"tresc\"|\"wyglad\"|\"funkcja\"|\"backend\"|\"inne\"","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":"przekroczono limit 20 zgłoszeń na godzinę","code":"feedback_rate_limited"}},"rate_limited":{"summary":"Over 120 requests in this minute","value":{"error":"przekroczono limit 120 zapytań/min"}}}}}}},"operationId":"sendFeedback","tags":["Feedback"],"x-pp-since":"1.6.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/feedback/send-feedback"}}},"/api/v1/media":{"post":{"summary":"Upload an image","description":"Uploads an image to the clinic'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; SVGs uploaded earlier are still served, with a sandboxing CSP header). 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-pp-since":"1.4.0"},"aiGenerated":{"type":"string","enum":["1","true","0","false"],"description":"1 or true when the image was generated by AI.","x-pp-since":"1.4.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-gabinet-zabiegowy.webp","url":"/api/media-file/KdYaXquTXB/Hp7X9uzN-gabinet-zabiegowy.webp","filename":"gabinet-zabiegowy.webp","alt":"Gabinet zabiegowy z fotelem stomatologicznym","width":960,"height":600,"aiGenerated":false,"caption":"Gabinet zabiegowy, parter"}}}}}},"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":"multipart pole 'file' jest wymagane"}},"not_multipart":{"summary":"The body is not multipart/form-data","value":{"error":"oczekiwano multipart/form-data"}}}}}},"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":"plik przekracza 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":"niedozwolony typ image/svg+xml — dozwolone: image/jpeg, image/png, image/webp, image/avif"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"uploadMedia","tags":["Media"],"x-pp-since":"1.2.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/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-gabinet-zabiegowy.webp","filename":"gabinet-zabiegowy.webp","contentType":"image/webp","size":27044,"alt":"Gabinet zabiegowy z fotelem stomatologicznym","width":960,"height":600,"aiGenerated":false,"caption":"Gabinet zabiegowy, parter","createdAt":"2026-09-30T14:25:12.000Z","url":"/api/media-file/KdYaXquTXB/Hp7X9uzN-gabinet-zabiegowy.webp"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listMedia","tags":["Media"],"description":"The clinic's media library, newest first, up to 200 items.","x-pp-since":"1.2.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/media/list-media"}}},"/api/v1/booking/catalog":{"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 /booking/slots and the `serviceId` and `resourceId` of /booking/appointments. 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 (1.12.0); pass one as `location` to /booking/slots."}},"required":["services","resources"]},"examples":{"catalog":{"summary":"Bookable services (trimmed) and specialists, one of them NFZ","value":{"services":[{"id":"bsvc_konsultacja","name":"Konsultacja stomatologiczna","durationMin":30,"priceLabel":"200 zł","resourceIds":["bres_anna","bres_piotr","bres_maria"],"serviceSlug":"konsultacja-stomatologiczna","nfz":false},{"id":"bsvc_higienizacja","name":"Higienizacja (skaling + piaskowanie + fluoryzacja)","durationMin":60,"priceLabel":"290 zł","resourceIds":["bres_maria"],"serviceSlug":"higienizacja","nfz":false},{"id":"bsvc_nfz","name":"Leczenie zachowawcze (NFZ)","durationMin":30,"priceLabel":null,"resourceIds":["bres_piotr"],"serviceSlug":null,"nfz":true}],"resources":[{"id":"bres_anna","name":"Anna Wzorcowa","title":"dr n. med.","doctorSlug":"anna-wzorcowa"},{"id":"bres_piotr","name":"Piotr Nowicki","title":"lek. dent.","doctorSlug":"piotr-nowicki"},{"id":"bres_maria","name":"Maria Zielińska","title":"lek. dent. spec. stomatologii estetycznej","doctorSlug":"maria-zielinska"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getBookingCatalog","tags":["Booking"],"x-pp-since":"1.3.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/booking/get-booking-catalog"}}},"/api/v1/booking/slots":{"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_konsultacja"},{"name":"resource","in":"query","schema":{"type":"string"},"description":"The specialist id (catalog `resources[].id`); omit for any specialist.","example":"bres_piotr"},{"name":"location","in":"query","schema":{"type":"string"},"description":"The location id (catalog `locations[].id`); omit for every location.","example":"loc_wieliczka"},{"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_piotr","startAt":1791180000,"endAt":1791181800,"localDate":"2026-10-05","localStartMin":480},{"resourceId":"bres_piotr","startAt":1791180900,"endAt":1791182700,"localDate":"2026-10-05","localStartMin":495},{"resourceId":"bres_piotr","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 an NFZ one (code nfz_phone_only: booking by 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":"wymagane parametry: service, from (YYYY-MM-DD), to (YYYY-MM-DD)"}},"range_too_large":{"summary":"More than 31 days","value":{"error":"nieprawidłowy zakres dat (max 31 dni)"}},"unknown":{"summary":"Unknown service or specialist","value":{"error":"nie znaleziono usługi lub specjalisty"}},"nfz_phone_only":{"summary":"An NFZ service (phone booking only)","value":{"error":"usługa NFZ — rejestracja telefoniczna","code":"nfz_phone_only"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getSlots","tags":["Booking"],"x-pp-since":"1.2.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/booking/get-slots"}}},"/api/v1/booking/appointments":{"get":{"summary":"List appointments","description":"The clinic'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":"Jan Nowak","patientPhone":"+48600100200","patientEmail":"jan.nowak@example.com","note":"Pierwsza wizyta, ząb boli przy zimnym.","leadId":"lead_1","serviceId":"bsvc_konsultacja","serviceName":"Konsultacja stomatologiczna","resourceId":"bres_piotr","resourceName":"Piotr Nowicki","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/to muszą być datami YYYY-MM-DD"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listAppointments","tags":["Booking"],"x-pp-since":"1.2.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/booking/list-appointments"}},"post":{"summary":"Book an appointment","description":"Books a visit at a free slot from GET /booking/slots, in the same calendar as the clinic'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). Reception gets a \"Nowa wizyta\" 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_konsultacja","resourceId":"bres_piotr","startAt":1791181800,"patient":{"name":"Jan Nowak","phone":"+48 600 100 200","email":"jan.nowak@example.com"},"note":"Pierwsza wizyta, ząb boli przy zimnym."}}}}},"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 an NFZ one (code nfz_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":"invalid JSON"}},"missing_fields":{"summary":"serviceId, resourceId or startAt missing","value":{"error":"wymagane: serviceId, resourceId, startAt (epoch s, UTC)"}},"missing_patient":{"summary":"patient.name or patient.phone missing","value":{"error":"wymagane: patient.name, patient.phone"}},"nfz_phone_only":{"summary":"An NFZ service (phone booking only)","value":{"error":"usługa NFZ — rejestracja telefoniczna","code":"nfz_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_piotr","startAt":1791183600,"endAt":1791185400,"localDate":"2026-10-05","localStartMin":540},{"resourceId":"bres_piotr","startAt":1791184500,"endAt":1791186300,"localDate":"2026-10-05","localStartMin":555},{"resourceId":"bres_piotr","startAt":1791185400,"endAt":1791187200,"localDate":"2026-10-05","localStartMin":570}]}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"createAppointment","tags":["Booking"],"x-pp-since":"1.2.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/booking/create-appointment"}}},"/api/v1/services":{"get":{"summary":"List treatments","description":"The clinic's published treatments, in the clinic's order. Things moved to the trash in the clinic's panel are not listed; their address answers 404, and after a restore they come back under the same address. 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_konsultacja","slug":"konsultacja-stomatologiczna","name":"Konsultacja stomatologiczna","category":"Diagnostyka","shortDesc":"Przegląd jamy ustnej, diagnoza i plan leczenia z wyceną na piśmie, punkt startowy każdego leczenia.","priceFromLabel":"200 zł","durationLabel":"wizyta 30 min","bookingServiceId":"bsvc_konsultacja","path":"/zabiegi/konsultacja-stomatologiczna"},{"id":"svc_impl","slug":"konsultacja-implantologiczna","name":"Konsultacja implantologiczna z tomografią 3D","category":"Implantologia","shortDesc":"Tomografia CBCT, ocena warunków kostnych i kompletny plan odbudowy implantologicznej na jednej wizycie.","priceFromLabel":"350 zł","durationLabel":"wizyta 45 min","bookingServiceId":"bsvc_impl","path":"/zabiegi/konsultacja-implantologiczna"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listServices","tags":["Entities"],"x-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/list-services"}}},"/api/v1/services/{slug}":{"get":{"summary":"Get a treatment","description":"One published treatment with its content blocks, questions and answers, and related treatments. A thing moved to the trash in the clinic's panel answers 404 under its address; after a restore it answers again under the same address. 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":"higienizacja"}],"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_higienizacja","slug":"higienizacja","path":"/zabiegi/higienizacja","name":"Higienizacja","category":"Profilaktyka","shortDesc":"Skaling ultradźwiękowy, piaskowanie i fluoryzacja z instruktażem higieny, zalecana co 6 miesięcy.","priceFromLabel":"290 zł","durationLabel":"zabieg 60 min","bookingServiceId":"bsvc_higienizacja","hero":null,"bodyBlocks":[{"id":"svc-hig-body","type":"rich-text.v1","props":{"variant":"narrow","heading":"Najtańsze leczenie, jakie istnieje","body":"Regularna higienizacja usuwa kamień, zanim wywoła stan zapalny dziąseł, i pozwala wychwycić próchnicę na etapie małego wypełnienia."}},{"id":"svc-hig-cta","type":"booking.v1","props":{"heading":"Umów higienizację","subheading":"Zabieg wykonuje lek. dent. Maria Zielińska.","preselectedServiceId":"bsvc_higienizacja"}}],"faq":[{"question":"Czy higienizacja boli?","answer":"Nie, przy wrażliwych zębach możemy zastosować znieczulenie powierzchniowe. Po zabiegu nadwrażliwość mija w ciągu doby."},{"question":"Jak często powtarzać higienizację?","answer":"Standardowo co 6 miesięcy; przy aparatach ortodontycznych i implantach co 3–4 miesiące."}],"related":[{"id":"svc_kontrola","slug":"wizyta-kontrolna","path":"/zabiegi/wizyta-kontrolna","name":"Wizyta kontrolna","priceFromLabel":"150 zł"},{"id":"svc_konsultacja","slug":"konsultacja-stomatologiczna","path":"/zabiegi/konsultacja-stomatologiczna","name":"Konsultacja stomatologiczna","priceFromLabel":"200 zł"}],"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-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/get-service"}}},"/api/v1/doctors":{"get":{"summary":"List team members","description":"The clinic's published team members, in the clinic's order. Things moved to the trash in the clinic's panel are not listed; their address answers 404, and after a restore they come back under the same address. 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-wzorcowa","name":"Anna Wzorcowa","title":"dr n. med.","bookingResourceId":"bres_anna","path":"/zespol/anna-wzorcowa","specializations":["Implantologia","Chirurgia stomatologiczna","Protetyka"],"photo":null},{"id":"doc_piotr","slug":"piotr-nowicki","name":"Piotr Nowicki","title":"lek. dent.","bookingResourceId":"bres_piotr","path":"/zespol/piotr-nowicki","specializations":["Endodoncja mikroskopowa","Stomatologia zachowawcza"],"photo":null}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listDoctors","tags":["Entities"],"x-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/list-doctors"}}},"/api/v1/doctors/{slug}":{"get":{"summary":"Get a team member","description":"One published team member with the full profile. A thing moved to the trash in the clinic's panel answers 404 under its address; after a restore it answers again under the same address. 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":"maria-zielinska"}],"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_maria","slug":"maria-zielinska","path":"/zespol/maria-zielinska","name":"Maria Zielińska","title":"lek. dent.","specializations":["Stomatologia estetyczna","Wybielanie","Higienizacja"],"photo":null,"bookingResourceId":"bres_maria","bio":"Łączy profilaktykę ze stomatologią estetyczną, od higienizacji, przez wybielanie, po bonding. Projekt uśmiechu pokazuje pacjentowi zawsze przed rozpoczęciem leczenia.","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-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/get-doctor"}}},"/api/v1/case-studies":{"get":{"summary":"List case studies","description":"The clinic's published case studies, newest first. Things moved to the trash in the clinic's panel are not listed; their address answers 404, and after a restore they come back under the same address. 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_odbudowa","slug":"pelna-odbudowa-usmiechu","title":"Pełna odbudowa uśmiechu na implantach","summary":"Pacjent, 58 lat, po latach unikania dentysty: sześć implantów, korony pełnoceramiczne i nowy zgryz w cztery miesiące, bez jednego dnia bez zębów.","publishedAt":"2026-06-20T00:00:00.000Z","path":"/realizacje/pelna-odbudowa-usmiechu","image":null,"resultsNote":"Efekty zależą od indywidualnych cech pacjenta."},{"id":"cs_metamorfoza","slug":"metamorfoza-przed-slubem","title":"Metamorfoza przed ślubem, wybielanie i bonding","summary":"Panna młoda, 29 lat, sześć tygodni do ślubu: wybielanie gabinetowe i bonding brzegów siecznych, naturalny efekt bez szlifowania zębów.","publishedAt":"2026-05-15T00:00:00.000Z","path":"/realizacje/metamorfoza-przed-slubem","image":null,"resultsNote":"Efekty zależą od indywidualnych cech pacjenta."}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listCaseStudies","tags":["Entities"],"x-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/list-case-studies"}}},"/api/v1/case-studies/{slug}":{"get":{"summary":"Get a case study","description":"One published case study with before and after images, content blocks and linked treatments. A thing moved to the trash in the clinic's panel answers 404 under its address; after a restore it answers again under the same address. 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":"pelna-odbudowa-usmiechu"}],"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_odbudowa","slug":"pelna-odbudowa-usmiechu","path":"/realizacje/pelna-odbudowa-usmiechu","title":"Pełna odbudowa uśmiechu na implantach","summary":"Pacjent, 58 lat, po latach unikania dentysty: sześć implantów, korony pełnoceramiczne i nowy zgryz w cztery miesiące, bez jednego dnia bez zębów.","before":null,"after":null,"bodyBlocks":[{"id":"cs-odb-wyzwanie","type":"rich-text.v1","props":{"variant":"narrow","heading":"Wyzwanie","body":"Pan Andrzej trafił do nas po kilkunastu latach przerwy w leczeniu: brakujące zęby boczne, starte zęby przednie i zgryz obniżony tak, że zmieniał rysy twarzy. Najbardziej bał się nie zabiegów, a tego, że na czas leczenia zostanie bez zębów."}}],"services":[],"publishedAt":"2026-06-20T00:00:00.000Z","updatedAt":"2026-09-30T14:21:58.000Z","resultsNote":"Efekty zależą od indywidualnych cech pacjenta.","story":null,"pairs":[],"sessionsCount":null,"resultAfterDays":null,"doctorSlug":null}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getCaseStudy","tags":["Entities"],"x-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/get-case-study"}}},"/api/v1/posts":{"get":{"summary":"List posts","description":"The clinic's published posts, newest first. Things moved to the trash in the clinic's panel are not listed; their address answers 404, and after a restore they come back under the same address. 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_tomograf","slug":"nowy-tomograf-3d","title":"Nowy tomograf 3D już w klinice","excerpt":"Od czerwca pełną diagnostykę implantologiczną wykonasz bez wychodzenia z gabinetu, nowy tomograf CBCT skraca planowanie leczenia do jednej wizyty.","publishedAt":"2026-06-28T00:00:00.000Z","path":"/aktualnosci/nowy-tomograf-3d","cover":null},{"id":"post_promocja","slug":"letnia-promocja-higienizacja","title":"Letnia promocja na higienizację","excerpt":"Do końca sierpnia pakiet higienizacyjny, skaling, piaskowanie i fluoryzacja, w cenie 240 zł zamiast 290 zł.","publishedAt":"2026-06-15T00:00:00.000Z","path":"/aktualnosci/letnia-promocja-higienizacja","cover":null}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listPosts","tags":["Entities"],"x-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/list-posts"}}},"/api/v1/posts/{slug}":{"get":{"summary":"Get a post","description":"One published post with its content blocks and SEO fields. A thing moved to the trash in the clinic's panel answers 404 under its address; after a restore it answers again under the same address. 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":"nowy-tomograf-3d"}],"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_tomograf","slug":"nowy-tomograf-3d","path":"/aktualnosci/nowy-tomograf-3d","title":"Nowy tomograf 3D już w klinice","excerpt":"Od czerwca pełną diagnostykę implantologiczną wykonasz bez wychodzenia z gabinetu, nowy tomograf CBCT skraca planowanie leczenia do jednej wizyty.","cover":null,"publishedAt":"2026-06-28T00:00:00.000Z","bodyBlocks":[{"id":"post-tom-body","type":"rich-text.v1","props":{"variant":"narrow","body":"Do tej pory część pacjentów musiała wykonywać tomografię w zewnętrznej pracowni i wracać do nas z płytą. Od czerwca cała diagnostyka odbywa się na miejscu: badanie trwa kilkanaście sekund, a wynik omawiamy od razu, na tej samej wizycie."}}],"seoTitle":null,"seoDescription":"Klinika Wzorcowa zainwestowała w nowy tomograf CBCT: pełna diagnostyka 3D na miejscu, niższa dawka promieniowania i planowanie implantów na jednej wizycie.","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-pp-since":"1.1.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities/get-post"}}},"/api/v1/conversations":{"get":{"summary":"List conversations","description":"The clinic's conversations with patients, newest activity first, each with its patient. Filter by status, location, channel of the newest message and `updated_since`; page with `cursor`. To keep a copy in your system in step, store the time you start a sync and pass it as `updated_since` next time. Needs the scope conversations:read; every page you read is written to the clinic's audit log with the ids it showed. The response holds patient data and is never cached (`Cache-Control: no-store`); call it from your server only.","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","closed"]},"description":"Only open or only closed conversations.","example":"open"},{"name":"location","in":"query","required":false,"schema":{"type":"string"},"description":"A location id from GET /locations. The primary location also takes the conversations whose location is not known.","example":"loc_wieliczka"},{"name":"channel","in":"query","required":false,"schema":{"type":"string","enum":["email","sms","whatsapp","instagram","chat","messenger"]},"description":"Only conversations whose newest message is on this channel.","example":"sms"},{"name":"updated_since","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Only conversations changed at or after this time (ISO 8601): a new message, a read, open or closed, or a change of the lead.","example":"2026-10-12T08:00:00Z"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"`nextCursor` of the previous page; omit for the first page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Conversations per page, 1 to 100 (default 50)."}],"responses":{"200":{"description":"A page of conversations.","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":{"conversations":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"},"description":"The conversations, newest activity first."},"nextCursor":{"type":"string","nullable":true,"description":"Pass it as `cursor` for the next page; null on the last page."}},"required":["conversations","nextCursor"]},"examples":{"page":{"summary":"Open conversations, one per page","value":{"conversations":[{"id":"Hm6pD1fGz4KqW8nLs2BvT","status":"open","lead":{"id":"Kq8vN2xWc0bLd4Te7YhJm","name":"Piotr","phone":"+48 601 200 300","email":null,"message":null,"source":"inbound","stage":"new","locationId":null,"createdAt":"2026-10-12T09:15:20.000Z"},"lastChannel":"whatsapp","lastMessageAt":"2026-10-12T09:15:20.000Z","lastInboundAt":"2026-10-12T09:15:20.000Z","unreadCount":1,"createdAt":"2026-10-12T09:15:20.000Z","updatedAt":"2026-10-12T09:15:20.000Z"}],"nextCursor":"1791796520.Hm6pD1fGz4KqW8nLs2BvT"}}}}}},"400":{"description":"A parameter is not valid (code invalid_query): an unknown status or channel, a limit outside 1 to 100, a cursor that did not come from this endpoint, a time that is not ISO 8601, or a location that is not the clinic's.","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/ConversationError"},"examples":{"invalid_query":{"summary":"An unknown channel","value":{"error":"channel: jeden z email, sms, whatsapp, instagram, chat, messenger","code":"invalid_query"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The clinic has not switched conversations on for the API (code feature_disabled), or the key lacks the scope this endpoint needs (code missing_scope, the scope in `scope`). The clinic grants scopes in its panel: Ustawienia › Integracje › Klucz API.","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/ConversationError"},"examples":{"missing_scope":{"summary":"The key lacks conversations:read","value":{"error":"Klucz nie ma uprawnienia conversations:read. Placówka nadaje je w panelu: Ustawienia › Integracje › Klucz API.","code":"missing_scope","scope":"conversations:read"}},"feature_disabled":{"summary":"Conversations are not switched on for the clinic","value":{"error":"Rozmowy w API nie są włączone dla tej placówki.","code":"feature_disabled"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listConversations","tags":["Conversations"],"x-pp-since":"1.14.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/conversations/list-conversations"}}},"/api/v1/conversations/{id}":{"get":{"summary":"Get a conversation","description":"One conversation with its patient, the channels a message can go out on now (and why not, for the others), and the clinic's reply templates filled in for this patient (empty when the clinic does not use templates). Use it before POST /conversations/{id}/messages to pick a channel that will be accepted. Needs the scope conversations:read; the read is written to the clinic's audit log. The response holds patient data and is never cached (`Cache-Control: no-store`); call it from your server only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The conversation id (`conversationId` of webhook events).","example":"Vb3nQ8sLk2PzX5wRt9YcE"}],"responses":{"200":{"description":"The conversation.","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":{"conversation":{"allOf":[{"$ref":"#/components/schemas/Conversation"}],"description":"The conversation."},"channels":{"type":"array","items":{"$ref":"#/components/schemas/ConversationChannelState"},"description":"Every channel, in a fixed order, with whether a message can go out on it now."},"templates":{"type":"array","items":{"$ref":"#/components/schemas/ConversationTemplate"},"description":"The clinic's reply templates, filled in for this patient."},"identities":{"type":"array","items":{"$ref":"#/components/schemas/ConversationIdentity"},"description":"Since 1.16.0: the patient's addresses per channel, with the one a message goes to by default."},"mergedEnquiries":{"type":"array","items":{"$ref":"#/components/schemas/ConversationMergedEnquiry"},"description":"Since 1.18.0: the enquiries of the leads the clinic merged into this conversation's lead, oldest first; empty when none. The clinic's inbox shows them in the conversation at their own time."}},"required":["conversation","channels","templates","identities","mergedEnquiries"]},"examples":{"conversation":{"summary":"A conversation: SMS available, e-mail opted out (templates trimmed to two)","value":{"conversation":{"id":"Vb3nQ8sLk2PzX5wRt9YcE","status":"open","lead":{"id":"rS2bIhedVSStSQLc7DL_T","name":"Anna Kowalska","phone":"+48 600 100 200","email":"anna.kowalska@example.com","message":"Proszę o kontakt w sprawie higienizacji.","source":"form","stage":"scheduled","locationId":"loc_wieliczka","createdAt":"2026-10-12T07:41:09.000Z"},"lastChannel":"sms","lastMessageAt":"2026-10-12T08:02:44.000Z","lastInboundAt":"2026-10-12T08:02:44.000Z","unreadCount":1,"createdAt":"2026-10-12T07:52:10.000Z","updatedAt":"2026-10-12T08:02:44.000Z"},"channels":[{"channel":"email","available":false,"reason":"opted_out"},{"channel":"sms","available":true,"reason":null},{"channel":"whatsapp","available":false,"reason":"wa_window_closed"},{"channel":"instagram","available":false,"reason":"ig_not_configured"},{"channel":"chat","available":false,"reason":"chat_not_started"}],"templates":[{"id":"nHv4l_ALArlouEEfxHLOO","name":"Rezerwacja online","text":"Dzień dobry Anna, wolne terminy i rezerwacja online: https://wzorcowa.pozyskajpacjenta.pl Klinika Wzorcowa","channels":null},{"id":"Olz1kaJ9PjjlsZXlBtly3","name":"Jak do nas trafić","text":"Zapraszamy: ul. Przykładowa 12, 00-950 Warszawa. W razie pytań prosimy dzwonić: +48 22 100 20 30. Klinika Wzorcowa","channels":null}],"identities":[{"channel":"sms","address":"+48600100200","primary":true,"verified":true,"optedOut":false,"replyDefault":false,"lastInboundAt":"2026-10-10T16:20:05.000Z"},{"channel":"sms","address":"+48501300400","primary":false,"verified":true,"optedOut":false,"replyDefault":true,"lastInboundAt":"2026-10-12T08:02:44.000Z"},{"channel":"whatsapp","address":"+48600100200","primary":true,"verified":true,"optedOut":false,"replyDefault":true,"lastInboundAt":null},{"channel":"whatsapp","address":"+48501300400","primary":false,"verified":true,"optedOut":false,"replyDefault":false,"lastInboundAt":null},{"channel":"email","address":"anna.kowalska@example.com","primary":true,"verified":false,"optedOut":false,"replyDefault":true,"lastInboundAt":null}],"mergedEnquiries":[{"leadId":"Qm7tYd2wPx9LkR4sNc8Ze","text":"Czy higienizację można zrobić w sobotę?","source":"chat","channel":null,"createdAt":"2026-10-11T18:12:30.000Z"}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The clinic has not switched conversations on for the API (code feature_disabled), or the key lacks the scope this endpoint needs (code missing_scope, the scope in `scope`). The clinic grants scopes in its panel: Ustawienia › Integracje › Klucz API.","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/ConversationError"},"examples":{"missing_scope":{"summary":"The key lacks conversations:read","value":{"error":"Klucz nie ma uprawnienia conversations:read. Placówka nadaje je w panelu: Ustawienia › Integracje › Klucz API.","code":"missing_scope","scope":"conversations:read"}}}}}},"404":{"description":"No such conversation in this clinic (code not_found): an unknown id, another clinic's conversation, or the conversation of a lead the clinic archived.","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/ConversationError"},"examples":{"not_found":{"summary":"Unknown id, another clinic's conversation or an archived lead's","value":{"error":"Nie ma takiej rozmowy.","code":"not_found"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"getConversation","tags":["Conversations"],"x-pp-since":"1.14.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/conversations/get-conversation"}}},"/api/v1/conversations/{id}/messages":{"get":{"summary":"List messages of a conversation","description":"The messages of one conversation, newest first; `nextCursor` pages to older ones. A webhook `message.received` names the conversation and the message: the message is on the first page. The first chat message of a chat lead is its enquiry, in `lead.message` of the conversation. Needs the scope conversations:read; the read is written to the clinic's audit log. The response holds patient data and is never cached (`Cache-Control: no-store`); call it from your server only.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The conversation id (`conversationId` of webhook events).","example":"Vb3nQ8sLk2PzX5wRt9YcE"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"`nextCursor` of the previous page; omit for the newest messages."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Messages per page, 1 to 100 (default 50)."}],"responses":{"200":{"description":"A page of messages.","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":{"messages":{"type":"array","items":{"$ref":"#/components/schemas/ConversationMessage"},"description":"The messages, newest first."},"nextCursor":{"type":"string","nullable":true,"description":"Pass it as `cursor` for older messages; null at the first message."}},"required":["messages","nextCursor"]},"examples":{"page":{"summary":"The newest message","value":{"messages":[{"id":"gH2jK4lZ6xC8vB0nM1qWe","conversationId":"Vb3nQ8sLk2PzX5wRt9YcE","channel":"sms","direction":"inbound","status":"received","subject":null,"text":"Pasuje, dziękuję. Do zobaczenia w czwartek.","attachments":[],"createdAt":"2026-10-12T08:02:44.000Z","sentAt":null,"deliveredAt":null,"readAt":null}],"nextCursor":"1791792164.gH2jK4lZ6xC8vB0nM1qWe"}}}}}},"400":{"description":"`limit` or `cursor` is not valid (code invalid_query).","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/ConversationError"},"examples":{"invalid_query":{"summary":"limit above 100","value":{"error":"limit: od 1 do 100","code":"invalid_query"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The clinic has not switched conversations on for the API (code feature_disabled), or the key lacks the scope this endpoint needs (code missing_scope, the scope in `scope`). The clinic grants scopes in its panel: Ustawienia › Integracje › Klucz API.","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/ConversationError"},"examples":{"missing_scope":{"summary":"The key lacks conversations:read","value":{"error":"Klucz nie ma uprawnienia conversations:read. Placówka nadaje je w panelu: Ustawienia › Integracje › Klucz API.","code":"missing_scope","scope":"conversations:read"}}}}}},"404":{"description":"No such conversation in this clinic (code not_found): an unknown id, another clinic's conversation, or the conversation of a lead the clinic archived.","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/ConversationError"},"examples":{"not_found":{"summary":"Unknown id, another clinic's conversation or an archived lead's","value":{"error":"Nie ma takiej rozmowy.","code":"not_found"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"}},"operationId":"listMessages","tags":["Conversations"],"x-pp-since":"1.14.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/conversations/list-messages"}},"post":{"summary":"Send a message","description":"Sends a message to the conversation's patient on the channel you choose, through the same path as a reply from the clinic's inbox: it is stored in the conversation, reaches the patient through the clinic's own channel account, and fires the `message.sent` webhook. The rules are the inbox's: a patient who opted out of a channel gets nothing on it; WhatsApp and Instagram take free text only within 24 hours of the patient's last message on that channel; the chat only after the patient wrote on it. Since 1.16.0 the message goes to the address the patient last wrote from on that channel, or to `to` (one of `identities` of GET /conversations/{id}); an address that opted out gets nothing. A refusal is a 409 with its code, and nothing was sent. Send `text`, or `templateId` to send one of the clinic's templates filled in for the patient. Needs the scope conversations:write; every attempt is written to the clinic's audit log.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The conversation id (`conversationId` of webhook events).","example":"Vb3nQ8sLk2PzX5wRt9YcE"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageInput"},"examples":{"text":{"summary":"A text by SMS","value":{"channel":"sms","text":"Dziękujemy, do zobaczenia w czwartek o 10:00."}},"template":{"summary":"A template of the clinic, filled in for the patient","value":{"channel":"sms","templateId":"nHv4l_ALArlouEEfxHLOO"}},"to":{"summary":"Since 1.16.0: to another of the patient's numbers","value":{"channel":"sms","text":"Dziękujemy, do zobaczenia w czwartek o 10:00.","to":"+48600100200"}}}}},"description":"The channel and the text or the template, as JSON."},"responses":{"201":{"description":"The message was sent.","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":{"message":{"allOf":[{"$ref":"#/components/schemas/ConversationMessage"}],"description":"The message as stored (status sent)."}},"required":["message"]},"examples":{"sent":{"summary":"Sent by SMS","value":{"message":{"id":"gbOmbsye_UFcdrpeY4LuZ","conversationId":"Vb3nQ8sLk2PzX5wRt9YcE","channel":"sms","direction":"outbound","status":"sent","subject":null,"text":"Dziękujemy, do zobaczenia w czwartek o 10:00.","attachments":[],"createdAt":"2026-10-12T08:30:49.000Z","sentAt":"2026-10-12T08:30:49.000Z","deliveredAt":null,"readAt":null}}},"template":{"summary":"Sent from a template","value":{"message":{"id":"FcGPiB1Bsve8C3tp3Agyt","conversationId":"Vb3nQ8sLk2PzX5wRt9YcE","channel":"sms","direction":"outbound","status":"sent","subject":null,"text":"Dzień dobry Anna, wolne terminy i rezerwacja online: https://wzorcowa.pozyskajpacjenta.pl Klinika Wzorcowa","attachments":[],"createdAt":"2026-10-12T08:30:50.000Z","sentAt":"2026-10-12T08:30:50.000Z","deliveredAt":null,"readAt":null}}}}}}},"400":{"description":"The body is not JSON (code invalid_json), or not a valid message (code invalid_body): an unknown channel, an empty or too long text, or both or neither of `text` and `templateId`.","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/ConversationError"},"examples":{"invalid_body":{"summary":"Both text and templateId","value":{"error":"podaj text albo templateId (jedno z nich)","code":"invalid_body"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"The clinic has not switched conversations on for the API (code feature_disabled), or the key lacks the scope this endpoint needs (code missing_scope, the scope in `scope`). The clinic grants scopes in its panel: Ustawienia › Integracje › Klucz API.","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/ConversationError"},"examples":{"missing_scope":{"summary":"The key can read but not send","value":{"error":"Klucz nie ma uprawnienia conversations:write. Placówka nadaje je w panelu: Ustawienia › Integracje › Klucz API.","code":"missing_scope","scope":"conversations:write"}}}}}},"404":{"description":"No such conversation in this clinic (code not_found): an unknown id, another clinic's conversation, or the conversation of a lead the clinic archived.","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/ConversationError"},"examples":{"not_found":{"summary":"Unknown id, another clinic's conversation or an archived lead's","value":{"error":"Nie ma takiej rozmowy.","code":"not_found"}}}}}},"409":{"description":"The rules do not let this message out now; nothing was sent. `code` says why (see SendRefused).","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/SendRefused"},"examples":{"opted_out":{"summary":"The patient opted out of e-mail","value":{"error":"Pacjent wypisał się z tego kanału.","code":"opted_out"}},"chat_not_started":{"summary":"The patient never wrote on the chat","value":{"error":"Pacjent nie pisał na czacie.","code":"chat_not_started"}}}}}},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"description":"The channel's provider did not accept the message (code send_failed). It is stored in the conversation as failed; `retryable` says whether trying again later may work.","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/ConversationError"},"examples":{"send_failed":{"summary":"The WhatsApp provider did not take the message","value":{"error":"Dostawca kanału nie przyjął wiadomości. Jest w rozmowie jako niewysłana.","code":"send_failed","retryable":true}}}}}}},"operationId":"sendMessage","tags":["Conversations"],"x-pp-since":"1.14.0","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/conversations/send-message"}}}},"tags":[{"name":"Content","description":"The clinic's site as data: a page with its site, the site alone, the privacy policy, the patient process and every published path.","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/content"}},{"name":"Entities","description":"Treatments, team members, case studies and posts, each with a list and a detail endpoint.","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/entities"}},{"name":"Booking","description":"Bookable services and specialists, free slots, booking an appointment and the clinic's appointment list.","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/booking"}},{"name":"Leads","description":"Patient enquiries from your own forms, stored in the clinic's CRM with the same e-mail to reception as an enquiry from the clinic's site.","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/leads"}},{"name":"Media","description":"The clinic's media library: list the files and upload images.","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/media"}},{"name":"Feedback","description":"Remarks about the clinic's site or about the API, sent to the platform team. Never patient data.","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/feedback"}},{"name":"Conversations","description":"The clinic's conversations with patients on every channel: read them, read their messages and reply, by the rules the clinic's inbox applies. Needs the key scopes conversations:read and conversations:write.","externalDocs":{"url":"https://pozyskajpacjenta.pl/docs/reference/conversations"}}],"externalDocs":{"description":"Guides and reference","url":"https://pozyskajpacjenta.pl/docs"}}