Concepts
Content model
Site, pages told apart by kind, blocks, entities, theme tokens and media.
This page explains the data a clinic frontend renders: the site, its pages, the blocks inside them, the clinic's treatments, team, case studies and posts, the theme and the media. It is the same data the clinic's own site is rendered from, so everything the clinic publishes in its panel is in the API at once.
Site
site is the frame of every page: the clinic's name, its domain, the main menu, the theme tokens and the clinic profile (contact, address, map point, opening hours, NFZ contract, payments, social links, logos, registry numbers). Every profile field is optional: show what is there and hide the rest.
Get it on its own with GET /site, or together with a page from GET /content. When site.noindex is true the clinic keeps its site out of search engines: add meta robots noindex to every page (ideally the X-Robots-Tag: noindex header too), publish no sitemap, and keep robots.txt open (Allow: /) so crawlers can see the noindex.
Pages and kinds
GET /content?path=/… returns { site, page }. page is a union told apart by kind: a page the clinic built from blocks in its page editor, or one of eight pages the platform builds from the clinic's data.
| kind | Page | Main field |
|---|---|---|
page | A page built from blocks in the clinic's page editor. | blocks |
services | The treatments index (/zabiegi). | services |
service | A treatment page (/zabiegi/{slug}). | service |
team | The team page (/zespol). | doctors |
doctor | A team member's profile page (/zespol/{slug}). | doctor |
case-studies | The case studies index (/realizacje). | caseStudies |
case-study | A case study page (/realizacje/{slug}). | caseStudy |
posts | The posts index (/aktualnosci). | posts |
post | A post page (/aktualnosci/{slug}). | post |
GET /pages lists every published path with its type (the same values as kind), so you know what to expect before you fetch a page. When a block page and an entity page share a path, the block page wins, as on the clinic's site.
import type { ContentPage } from "@pozyskajpacjenta/sdk";
function render(page: ContentPage) {
switch (page.kind) {
case "page":
return page.blocks; // render by block.type
case "service":
return page.service.bodyBlocks;
case "doctor":
return page.doctor.bio;
default:
return page.title;
}
}Entity pages share a frame: title, breadcrumbs and exactly one call to action, cta (the clinic's phone or its contact page).
Blocks
Pages built in the editor, and the bodyBlocks of treatments, case studies and posts, are lists of validated blocks. Each block has an id, a type with its version (hero.v1, faq.v1) and props whose shape depends on the type.
Render the types you support and skip the rest: new block types appear over time and must never break your frontend. The block types in use today:
before-after.v1, booking.v1, booksy-embed.v1, breadcrumbs.v1, case-study-teaser.v1, cta-band.v1, faq.v1, feature-cards.v1, footer.v1, gallery.v1, hero.v1, lead-form.v1, map.v1, media-split.v1, nav.v1, post-list.v1, pricing.v1, procedure-summary.v1, process-steps.v1, related-services.v1, rich-text.v1, services-grid.v1, stats.v1, sticky-booking-cta.v1, team-grid.v1, team.v1, testimonials.v1, trust-bar.v1, znanylekarz-embed.v1.
Price list rows (pricing.v1, field items) can carry a serviceSlug: the treatment the row is about, so you can link the row to that treatment's page.
Entities
Treatments, team members, case studies and posts are resources of their own, each with a list and a detail endpoint (tag Entities). They carry the path of their page on the clinic's site, and treatments and team members link to booking: bookingServiceId on a treatment and bookingResourceId on a team member are the ids GET /booking/slots takes. null means the item cannot be booked online.
Theme
site.theme holds the clinic's design tokens: colours (light and dark), fonts, type scale, radii, shadows and motion. Map them onto CSS variables and style your components with the variables: a theme change in the panel then reaches your frontend without a code change.
const { site } = await client.getSite();
const c = site.theme.colors;
const css = `:root {
--background: ${c.background};
--foreground: ${c.foreground};
--primary: ${c.primary};
--primary-foreground: ${c.primaryForeground};
}`;Media
Images come as MediaRef objects with an absolute url, the original's width and height, alt and a caption. Put width and height on the <img> so the layout does not shift. focalX and focalY (percent) tell you which part to keep when you crop, e.g. as object-position.
Files from the media library (/api/media-file/…) have smaller variants: ?w= takes one width of 320, 480, 640, 960, 1280, 1600, 2048 px (never upscaled; any other width is a 400) and f= takes auto (AVIF or WebP by the Accept header), avif or webp. Files are served with Cache-Control: public, max-age=31536000, immutable: the address names one version of the file, so cache freely. The SDK builds variant URLs and a srcset for you (mediaVariantUrl, mediaSrcSet).
GET responses of the API itself are private to the key (Cache-Control: private, max-age=60): cache them on your server for up to a minute.