Resources
TypeScript SDK
Install @pozyskajpacjenta/sdk, create a client, and handle typed errors.
@pozyskajpacjenta/sdk is a small typed client for API v1: one method per endpoint, typed responses including the kind union of pages, and typed errors. This page covers installation, the client, every method and error handling. The current version is 0.17.0.
Install
The package is not in the public npm registry: it ships as one .tgz file with the types built in. Write to us through the contact page and we send you the current file; then install it from disk:
npm install ./pozyskajpacjenta-sdk-0.17.0.tgz
# or
pnpm add ./pozyskajpacjenta-sdk-0.17.0.tgzIts only dependency from the registry is zod (the block types). It runs on Node 18 and later, Bun and Deno with the global fetch, and ships ESM and CommonJS builds with type declarations. The key is a secret: use the SDK on your server, never in a browser.
Without the SDK: types from OpenAPI
Prefer your own client? Generate types for every path and schema from the served document:
npx openapi-typescript https://app.pozyskajpacjenta.pl/api/v1/openapi.json -o pozyskajpacjenta-api.d.tsimport type { components, paths } from "./pozyskajpacjenta-api";
type Catalog =
paths["/api/v1/booking/catalog"]["get"]["responses"]["200"]["content"]["application/json"];
type Slot = components["schemas"]["Slot"];The document is public and needs no key. Its operationIds are the SDK's method names.
The client
import { PozyskajPacjentaClient } from "@pozyskajpacjenta/sdk";
const client = new PozyskajPacjentaClient({
apiKey: process.env.PP_API_KEY ?? "",
baseUrl: "https://app.pozyskajpacjenta.pl",
});apiKey(required): the clinic'spp_live_key.baseUrl(required): there is no default, so a test frontend never talks to production by accident.fetch(optional): your own implementation, for caching, retries or tests.
Dates arrive as ISO 8601 strings; slot and appointment times as epoch seconds (UTC).
Methods
| Method | Returns | Endpoint |
|---|---|---|
getContent(path?: string) | Promise<SiteContent> | GET /api/v1/content |
getSite() | Promise<SiteInfo> | GET /api/v1/site |
listLocations() | Promise<{ locations: Location[] }> | GET /api/v1/locations |
getPrivacy() | Promise<PrivacyInfo> | GET /api/v1/privacy |
listPages(options?: { kind? }) | Promise<{ pages: PageListItem[] }> | GET /api/v1/pages |
listServices() | Promise<{ services: ServiceSummary[] }> | GET /api/v1/services |
getService(slug: string) | Promise<{ service: ServiceDetail }> | GET /api/v1/services/{slug} |
listDoctors() | Promise<{ doctors: DoctorSummary[] }> | GET /api/v1/doctors |
getDoctor(slug: string) | Promise<{ doctor: DoctorDetail }> | GET /api/v1/doctors/{slug} |
listCaseStudies() | Promise<{ caseStudies: CaseStudySummary[] }> | GET /api/v1/case-studies |
getCaseStudy(slug: string) | Promise<{ caseStudy: CaseStudyDetail }> | GET /api/v1/case-studies/{slug} |
listPosts() | Promise<{ posts: PostSummary[] }> | GET /api/v1/posts |
getPost(slug: string) | Promise<{ post: PostDetail }> | GET /api/v1/posts/{slug} |
getBookingCatalog() | Promise<BookingCatalog> | GET /api/v1/booking/catalog |
getSlots(params: { serviceId, resourceId?, locationId?, from, to }) | Promise<{ slots: Slot[] }> | GET /api/v1/booking/slots |
listAppointments(params?: { from?, to? }) | Promise<{ appointments: Appointment[] }> | GET /api/v1/booking/appointments |
createAppointment(input: AppointmentInput) | Promise<AppointmentCreated> | POST /api/v1/booking/appointments |
createLead(lead: LeadInput, options?: { strict?: boolean }) | Promise<LeadCreated> | POST /api/v1/leads |
uploadMedia(file: Blob, options?: { alt?, caption?, aiGenerated?, filename? }) | Promise<MediaItem> | POST /api/v1/media |
listMedia() | Promise<{ items: MediaItem[] }> | GET /api/v1/media |
sendFeedback(input: FeedbackInput) | Promise<FeedbackCreated> | POST /api/v1/feedback |
listConversations(params?: { status?, locationId?, channel?, updatedSince?, cursor?, limit? }) | Promise<ConversationList> | GET /api/v1/conversations |
getConversation(id: string) | Promise<ConversationDetail> | GET /api/v1/conversations/{id} |
listMessages(conversationId: string, params?: { cursor?, limit? }) | Promise<MessageList> | GET /api/v1/conversations/{id}/messages |
sendMessage(conversationId: string, input: SendMessageInput) | Promise<MessageSent> | POST /api/v1/conversations/{id}/messages |
Helpers that need no request: mediaVariantUrl(url, { width, format }) and mediaSrcSet(url, { maxWidth }) build image variant addresses, phoneHref(phone) a tel: link, TIME_OF_DAY and windowFromPart(date, part) the windows of a request for a time. Every response type is exported too (SiteContent, ContentPage, BookingCatalog, Slot, Appointment, Conversation, ConversationMessage, ValidatedBlock, ThemeTokens and the entity types).
Errors
| Class or function | When |
|---|---|
ApiError | Any non-2xx answer. status is the HTTP status, body the parsed error (or the text when it is not JSON), code the error code when the endpoint sends one. |
SlotTakenError | 409 from createAppointment: the slot was taken, nothing was stored. Extends ApiError; slots are that day's free slots. |
isSendRefused(e) | 409 from sendMessage: the clinic inbox's rules did not let the message out, nothing was sent; the reason is in code. |
isApiError(e), isSlotTaken(e), isSendRefused(e) | Type guards for a catch block. |
Render a whole site
const { pages } = await client.listPages();
for (const item of pages) {
const { site, page } = await client.getContent(item.path);
// theme tokens to CSS variables once, then one switch on page.kind
}Every method has a reference page with a runnable sample: start at API reference.