# 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](https://pozyskajpacjenta.pl/kontakt) and we send you the current file; then install it from disk:

```bash Shell
npm install ./pozyskajpacjenta-sdk-0.17.0.tgz
# or
pnpm add ./pozyskajpacjenta-sdk-0.17.0.tgz
```

Its 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:

```bash Shell
npx openapi-typescript https://app.pozyskajpacjenta.pl/api/v1/openapi.json -o pozyskajpacjenta-api.d.ts
```

```ts TypeScript
import 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

```ts TypeScript
import { PozyskajPacjentaClient } from "@pozyskajpacjenta/sdk";

const client = new PozyskajPacjentaClient({
  apiKey: process.env.PP_API_KEY ?? "",
  baseUrl: "https://app.pozyskajpacjenta.pl",
});
```

- `apiKey` (required): the clinic's `pp_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)`](https://pozyskajpacjenta.pl/docs/reference/content/get-content) | `Promise<SiteContent>` | GET /api/v1/content |
| [`getSite()`](https://pozyskajpacjenta.pl/docs/reference/content/get-site) | `Promise<SiteInfo>` | GET /api/v1/site |
| [`listLocations()`](https://pozyskajpacjenta.pl/docs/reference/content/list-locations) | `Promise<{ locations: Location[] }>` | GET /api/v1/locations |
| [`getPrivacy()`](https://pozyskajpacjenta.pl/docs/reference/content/get-privacy) | `Promise<PrivacyInfo>` | GET /api/v1/privacy |
| [`listPages(options?: { kind? })`](https://pozyskajpacjenta.pl/docs/reference/content/list-pages) | `Promise<{ pages: PageListItem[] }>` | GET /api/v1/pages |
| [`listServices()`](https://pozyskajpacjenta.pl/docs/reference/entities/list-services) | `Promise<{ services: ServiceSummary[] }>` | GET /api/v1/services |
| [`getService(slug: string)`](https://pozyskajpacjenta.pl/docs/reference/entities/get-service) | `Promise<{ service: ServiceDetail }>` | GET /api/v1/services/{slug} |
| [`listDoctors()`](https://pozyskajpacjenta.pl/docs/reference/entities/list-doctors) | `Promise<{ doctors: DoctorSummary[] }>` | GET /api/v1/doctors |
| [`getDoctor(slug: string)`](https://pozyskajpacjenta.pl/docs/reference/entities/get-doctor) | `Promise<{ doctor: DoctorDetail }>` | GET /api/v1/doctors/{slug} |
| [`listCaseStudies()`](https://pozyskajpacjenta.pl/docs/reference/entities/list-case-studies) | `Promise<{ caseStudies: CaseStudySummary[] }>` | GET /api/v1/case-studies |
| [`getCaseStudy(slug: string)`](https://pozyskajpacjenta.pl/docs/reference/entities/get-case-study) | `Promise<{ caseStudy: CaseStudyDetail }>` | GET /api/v1/case-studies/{slug} |
| [`listPosts()`](https://pozyskajpacjenta.pl/docs/reference/entities/list-posts) | `Promise<{ posts: PostSummary[] }>` | GET /api/v1/posts |
| [`getPost(slug: string)`](https://pozyskajpacjenta.pl/docs/reference/entities/get-post) | `Promise<{ post: PostDetail }>` | GET /api/v1/posts/{slug} |
| [`getBookingCatalog()`](https://pozyskajpacjenta.pl/docs/reference/booking/get-booking-catalog) | `Promise<BookingCatalog>` | GET /api/v1/booking/catalog |
| [`getSlots(params: { serviceId, resourceId?, locationId?, from, to })`](https://pozyskajpacjenta.pl/docs/reference/booking/get-slots) | `Promise<{ slots: Slot[] }>` | GET /api/v1/booking/slots |
| [`listAppointments(params?: { from?, to? })`](https://pozyskajpacjenta.pl/docs/reference/booking/list-appointments) | `Promise<{ appointments: Appointment[] }>` | GET /api/v1/booking/appointments |
| [`createAppointment(input: AppointmentInput)`](https://pozyskajpacjenta.pl/docs/reference/booking/create-appointment) | `Promise<AppointmentCreated>` | POST /api/v1/booking/appointments |
| [`createLead(lead: LeadInput, options?: { strict?: boolean })`](https://pozyskajpacjenta.pl/docs/reference/leads/create-lead) | `Promise<LeadCreated>` | POST /api/v1/leads |
| [`uploadMedia(file: Blob, options?: { alt?, caption?, aiGenerated?, filename? })`](https://pozyskajpacjenta.pl/docs/reference/media/upload-media) | `Promise<MediaItem>` | POST /api/v1/media |
| [`listMedia()`](https://pozyskajpacjenta.pl/docs/reference/media/list-media) | `Promise<{ items: MediaItem[] }>` | GET /api/v1/media |
| [`sendFeedback(input: FeedbackInput)`](https://pozyskajpacjenta.pl/docs/reference/feedback/send-feedback) | `Promise<FeedbackCreated>` | POST /api/v1/feedback |
| [`listConversations(params?: { status?, locationId?, channel?, updatedSince?, cursor?, limit? })`](https://pozyskajpacjenta.pl/docs/reference/conversations/list-conversations) | `Promise<ConversationList>` | GET /api/v1/conversations |
| [`getConversation(id: string)`](https://pozyskajpacjenta.pl/docs/reference/conversations/get-conversation) | `Promise<ConversationDetail>` | GET /api/v1/conversations/{id} |
| [`listMessages(conversationId: string, params?: { cursor?, limit? })`](https://pozyskajpacjenta.pl/docs/reference/conversations/list-messages) | `Promise<MessageList>` | GET /api/v1/conversations/{id}/messages |
| [`sendMessage(conversationId: string, input: SendMessageInput)`](https://pozyskajpacjenta.pl/docs/reference/conversations/send-message) | `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

```ts TypeScript
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](https://pozyskajpacjenta.pl/docs/reference).
