> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paymove.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> ## paymove integration rules for AI agents
>
> These rules are verified against the live API. Follow them exactly.
>
> 1. Amounts are INTEGERS in grosze (minor units). `1000` means 10.00 PLN. PLN is the only supported currency and there is no currency field in the API.
> 2. A float `price` (e.g. `12.99`) is SILENTLY TRUNCATED to 12 grosze and still returns HTTP 200. Always send an integer: `Math.round(pln * 100)`.
> 3. Authenticate with the `X-API-KEY` header. Never `Authorization: Bearer`. Sandbox keys start with `sk_test_`, production keys with `sk_live_`.
> 4. Server-side only. A call from a merchant page is rejected. Never put the key in frontend code.
> 5. Unknown request fields are silently ignored and still return HTTP 200 — a wrong body shape looks like success. Match the documented shape exactly.
> 6. A missing `externalId` returns HTTP 500 `Something went wrong`, not 400. A missing `price` or `details.returnUrl` returns HTTP 200 and a usable `redirectUrl` — no error at all. Validate the body yourself before sending it.
> 7. Always verify the `X-Paymove-Signature` header on incoming webhooks before trusting them: https://docs.paymove.io/en/webhook-signature.md
> 8. Errors are `{"status": <int>, "message": "<text>"}`. Branch on the HTTP status only — never on `message`, which is unstable and leaks internal class names.
> 9. There is no rate limiting, no HTTP 429, no HTTP 422, no `Idempotency-Key` header and no API versioning. Do not write code that handles them.
> 10. Re-POSTing an `externalId` that already exists returns HTTP 200 with the ORIGINAL `redirectUrl` and silently discards EVERY field you send — the new price, description and details are all ignored. Use `PATCH /api/pay/product/{productId}/subproduct/{externalId}` to change a price.
> 11. Webhooks fire only on `COMPLETED` by default, and `retries` defaults to `0` (no retries) unless you set it explicitly. Delivery counts as successful when the HTTP status equals `expectedCode` — the response body is never inspected.
> 12. Never fulfil an order on the `returnUrl` redirect. The checkout does not redirect there by itself: the customer has to click "back to shop", and inside the widget modal that redirect never happens. Fulfil only in the webhook handler, after verifying the signature.
> 13. Do not pin a version of `@paymove-io/sdk` — install the latest.
> 14. Full documentation index: https://docs.paymove.io/llms.txt · Copy-paste quickstart: https://docs.paymove.io/en/quickstart.md · Agent skill: https://docs.paymove.io/skill.md

# REST API

> Pełna referencja endpointów DocPay: produkt, subprodukty i kody QR, cennik, formularze, personalizacja UI, webhooki.

Dokumentacja endpointów PAY API (DocPay): produkt, subprodukt i kody QR, cennik, formularze, personalizacja UI oraz webhooki. Podstawowy scenariusz integracji (produkt + subprodukt) opisany jest w [Tutorialu](/products/docpay/tutorial).

***

## 1. Produkt

Produkt reprezentuje Twoją usługę w systemie Paymove i jest rootem całego modelu - subprodukty, cennik, formularze, customizacje i webhooki są podłączane do konkretnego produktu. Produkt tworzysz **jednorazowo**.

### Endpointy – Produkt

| Metoda | Endpoint                       | Opis                 |
| ------ | ------------------------------ | -------------------- |
| POST   | `/api/product/pay`             | Tworzy produkt.      |
| PATCH  | `/api/product/pay/{productId}` | Aktualizuje produkt. |

### Utworzenie produktu

```
POST /api/product/pay
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "partnerId": "78562c79-2f5c-4415-8af4-c871eea92ef2",
  "productType": "PAY",
  "name": "Testowy Produkt",
  "shortName": "3456",
  "location": "Warszawa",
  "timezone": "Europe/Warsaw"
}
```

| Pole          | Opis                                             |
| ------------- | ------------------------------------------------ |
| `partnerId`   | Identyfikator partnera (nadawany przez Paymove). |
| `productType` | Typ produktu - dla DocPay: `PAY`.                |
| `name`        | Nazwa produktu.                                  |
| `shortName`   | Krótka nazwa wyświetlana.                        |
| `location`    | Lokalizacja (np. miasto).                        |
| `timezone`    | Strefa czasowa IANA (np. `Europe/Warsaw`).       |

**Odpowiedź (200):**

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "id": "d0a834f9-94a4-4b3a-aa10-27d911e3633f",
  "name": "Testowy Produkt",
  "location": "Warszawa",
  "partner": {
    "id": "78562c79-2f5c-4415-8af4-c871eea92ef2",
    "name": "Nazwa Partnera Sp. z o.o.",
    "productTypes": ["PAY"]
  },
  "timezone": "Europe/Warsaw",
  "shortName": "3456",
  "emailEnabled": true,
  "smsEnabled": false,
  "fee": {
    "id": "01f9754e-78e3-4b2c-8186-d6862598a95a",
    "minimum": 30,
    "amount": 5,
    "fixed": false
  },
  "productType": "PAY",
  "status": "ACTIVE",
  "creator": "PAYMOVE",
  "reviewEnabled": false,
  "createdAt": 1783245692.623763835,
  "updatedAt": 1783245692.623763835
}
```

Pole `id` to identyfikator produktu (`productId`) używany w pozostałych endpointach.

### Aktualizacja produktu

```
PATCH /api/product/pay/{productId}
```

Możesz wysłać tylko pola do zmiany; pozostałe relacje (cennik, formularze, webhooki) pozostają bez zmian.

***

## 2. Subprodukt i kody QR

Subprodukt reprezentuje pojedynczą płatność (np. wezwanie do zapłaty, bilet) powiązaną z zewnętrznym identyfikatorem (`externalId`). W odpowiedzi generowany jest kod QR w wybranym formacie (PNG/SVG). Po zeskanowaniu kodu użytkownik trafia do płatności za dany subprodukt.

### Dane wejściowe

| Pole          | Opis                                                                                          |
| ------------- | --------------------------------------------------------------------------------------------- |
| `externalId`  | Identyfikator dokumentu w Twoim systemie. Wyświetlany w panelu Paymove jako numer dokumentu.  |
| `imageFormat` | Format grafiki kodu QR: `svg` lub `png`.                                                      |
| `bannerType`  | Typ banera/karty (np. `ticket`) - wpływa na prezentację/układ.                                |
| `price`       | Kwota w groszach (np. `25000` = 250,00 PLN).                                                  |
| `details`     | Pary klucz-wartość wyświetlane klientowi na stronie płatności (np. `"Nr. dokumentu": "..."`). |

### Endpoint – Subprodukt

| Metoda | Endpoint                                  | Opis                                                                                                                                       |
| ------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| POST   | `/api/pay/product/{productId}/subproduct` | Rejestruje subprodukt na podstawie `externalId` i generuje kod QR. Po rejestracji użytkownik może opłacić subprodukt po zeskanowaniu kodu. |

### Rejestracja subproduktu

```
POST /api/pay/product/{productId}/subproduct
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "externalId": "test-external#2",
  "imageFormat": "svg",
  "bannerType": "ticket",
  "price": 25000,
  "details": {
    "Nr. dokumentu": "test-external#2"
  }
}
```

Odpowiedź (200) to binarna zawartość obrazka z kodem QR w formacie wskazanym w `imageFormat` (`Content-Type: application/octet-stream`) - zapisz body odpowiedzi bezpośrednio do pliku. Grafika to gotowy do druku baner z kodem QR przekierowującym do płatności za ten subprodukt.

***

## 3. Cennik

Cennik definiuje opcje płatności dostępne dla klienta - np. „1 godzina", „cały dzień", „bilet weekendowy". Każda pozycja (pricing entry) to oddzielna opcja zakupu wyświetlana na stronie produktu.

### Dane pozycji cenowej

| Pole          | Opis                                               |
| ------------- | -------------------------------------------------- |
| `price`       | Wartość w groszach (np. 150 = 1,50 PLN).           |
| `description` | Opis słowny opcji (np. „1 godzina parkowania").    |
| `details`     | JSON z dodatkowymi danymi (np. `currency`, `vat`). |

### Endpointy – Cennik

| Metoda | Endpoint                                                  | Opis                                                                       |
| ------ | --------------------------------------------------------- | -------------------------------------------------------------------------- |
| GET    | `/api/pay/products/{productId}/pricing/entries`           | Zwraca wszystkie pozycje cenowe dla produktu.                              |
| POST   | `/api/pay/products/{productId}/pricing/entries`           | Tworzy nową pozycję cenową.                                                |
| PATCH  | `/api/pay/products/{productId}/pricing/entries/{entryId}` | Aktualizuje pozycję cenową.                                                |
| DELETE | `/api/pay/products/{productId}/pricing/entries/{entryId}` | Usuwa pozycję cenową. Po usunięciu wariant nie jest dostępny przy zakupie. |

### Utworzenie pozycji cennika

```
POST /api/pay/products/{productId}/pricing/entries
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "price": 150,
  "description": "1 godzina parkowania",
  "details": "{\"currency\":\"PLN\",\"vat\":23}"
}
```

### Aktualizacja pozycji cennika

```
PATCH /api/pay/products/{productId}/pricing/entries/{entryId}
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "price": 150000000,
  "details": "{\"currency\":\"PLN\",\"vat\":23}"
}
```

Zmiana jest natychmiast widoczna dla klientów wybierających daną opcję.

***

## 4. Formularze

Formularze służą do zbierania od klientów danych wymaganych do zakupu - np. e-mail, numer rejestracyjny, dane kontaktowe. Formularze są wersjonowane; każda zmiana (dodanie/edycja/usunięcie pola) tworzy nową wersję. Bieżąca wersja oznaczana jest flagą `isCurrent`.

### Dane pola formularza (Field)

| Pole         | Opis                                               |
| ------------ | -------------------------------------------------- |
| `name`       | Nazwa pola (np. `email`).                          |
| `type`       | Typ: `TEXT`, `NUMBER`, `SELECT`, `EMAIL` itd.      |
| `labels`     | Etykiety językowe (np. `pl`, `en`).                |
| `validators` | Walidacje: `required`, `pattern`, `maxLength` itd. |

### Endpointy – Formularze

| Metoda | Endpoint                                     | Opis                                                              |
| ------ | -------------------------------------------- | ----------------------------------------------------------------- |
| GET    | `/api/pay/product/{productId}/form`          | Lista formularzy dla produktów PAY.                               |
| GET    | `/api/pay/product/{productId}/form/{formId}` | Szczegóły formularza (pola, wersja, isCurrent).                   |
| POST   | `/api/pay/product/{productId}/form`          | Tworzy formularz przypisany do produktu.                          |
| DELETE | `/api/pay/product/{productId}/form/{formId}` | Usuwa formularz. Nie wpływa na dane z już zrealizowanych zakupów. |

### Endpointy – Pola (Fields)

| Metoda | Endpoint                                                     | Opis                                                                        |
| ------ | ------------------------------------------------------------ | --------------------------------------------------------------------------- |
| GET    | `/api/pay/product/{productId}/form/field/{fieldId}`          | Szczegóły pola.                                                             |
| POST   | `/api/pay/product/{productId}/form/{formId}/fields`          | Dodaje pole - tworzy nową wersję formularza, nowa wersja staje się bieżąca. |
| PATCH  | `/api/pay/product/{productId}/form/{formId}/field/{fieldId}` | Aktualizuje pole - tworzy nową wersję formularza z zaktualizowanym polem.   |
| DELETE | `/api/pay/product/{productId}/form/{formId}/field/{fieldId}` | Usuwa pole - tworzy nową wersję bez tego pola.                              |

### Utworzenie formularza

```
POST /api/pay/product/{productId}/form
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "fields": [],
  "version": 1,
  "isCurrent": true
}
```

### Dodanie pola (np. e-mail)

```
POST /api/pay/product/{productId}/form/{formId}/fields
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "name": "email",
  "type": "TEXT",
  "labels": {
    "en": "Email",
    "pl": "Adres e-mail"
  },
  "validators": {
    "required": true,
    "maxLength": 100,
    "pattern": "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$"
  }
}
```

Operacja automatycznie tworzy nową wersję formularza; poprzednia traci status bieżącej (`isCurrent`).

***

## 5. Personalizacja UI

Customization pozwala zmienić wygląd i treści widoczne na stronie zakupu produktu. Wszystkie teksty (tytuły, przyciski, opisy) są konfigurowane z poziomu API - frontend jest w pełni sterowany przez partnera bez zmian w kodzie.

### Parametry customizacji - co modyfikują

| Parametr            | Co modyfikuje                                      | Gdzie się wyświetla                                                                                                        |
| ------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `title`             | Główny tytuł strony/productu.                      | Nagłówek na górze widoku zakupu.                                                                                           |
| `subtitle`          | Tekst pod tytułem.                                 | Bezpośrednio pod tytułem (subtytuł).                                                                                       |
| `buttonText`        | Tekst przycisku akcji (np. „Kup teraz", „Zapłać"). | Przycisk potwierdzenia na stronie płatności.                                                                               |
| `summaryHeader`     | Nagłówek sekcji podsumowania.                      | Nagłówek bloku z podsumowaniem zamówienia przed płatnością.                                                                |
| `summaryFirstLine`  | Pierwsza linia w sekcji podsumowania.              | Tekst w podsumowaniu (np. opis kwoty lub produktu).                                                                        |
| `summarySecondLine` | Druga linia w sekcji podsumowania.                 | Kolejna linia w podsumowaniu.                                                                                              |
| `cardDescription`   | Opis karty produktu.                               | Opis wyświetlany na karcie/listingu produktu (np. w wyborze produktu lub w podglądzie).                                    |
| `locale`            | Język/wersja językowa (np. `pl-PL`, `en-GB`).      | Określa, która wersja tekstów (dla danej customizacji) jest używana; pozwala mieć wiele customizacji (np. osobno PL i EN). |

Dla jednego produktu możesz mieć wiele customizacji z różnymi `locale`; system wybiera odpowiednią według ustawień użytkownika lub kontekstu.

### Endpointy – Personalizacja

| Metoda | Endpoint                                          | Opis                                                                                                        |
| ------ | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| GET    | `/api/pay/product/{productId}/customization`      | Lista wszystkich customizacji.                                                                              |
| GET    | `/api/pay/product/{productId}/customization/{id}` | Szczegóły customizacji (wszystkie pola).                                                                    |
| POST   | `/api/pay/product/{productId}/customization`      | Tworzy customizację dla produktu.                                                                           |
| PATCH  | `/api/pay/product/{productId}/customization/{id}` | Aktualizuje customizację (dowolne pola). Zmiana jest od razu widoczna na froncie.                           |
| DELETE | `/api/pay/product/{productId}/customization/{id}` | Usuwa customizację. Produkt przestaje używać tej konfiguracji; inne customizacje i usługi nie są zmieniane. |

### Utworzenie customizacji

```
POST /api/pay/product/{productId}/customization
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "title": "Nowy produkt",
  "subtitle": "Subtytuł",
  "summaryHeader": "Nagłówek podsumowania",
  "summaryFirstLine": "Pierwsza linia",
  "summarySecondLine": "Druga linia",
  "buttonText": "Kup teraz",
  "cardDescription": "Opis karty",
  "locale": "pl-PL"
}
```

### Aktualizacja customizacji

```
PATCH /api/pay/product/{productId}/customization/{id}
```

Możesz wysłać tylko te pola, które chcesz zmienić, np.:

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "summaryFirstLine": "Pierwsza linia",
  "summarySecondLine": "Druga linia",
  "buttonText": "Kup teraz",
  "cardDescription": "Opis karty",
  "locale": "pl-PL"
}
```

Zmiana jest natychmiast odzwierciedlana w prezentacji produktu na stronie zakupu.

***

## 6. Webhooki

Webhooki służą do automatycznego informowania partnera o zakończonej płatności lub do pobrania cennika z systemu zewnętrznego. Po zarejestrowaniu webhooka należy przypisać go do produktu - wtedy zdarzenia związane z tym produktem są wysyłane na wskazany endpoint.

### Pola konfiguracji webhooka

| Pole               | Opis                                                                 |
| ------------------ | -------------------------------------------------------------------- |
| `endpoint`         | URL, na który Paymove wysyła żądanie (POST).                         |
| `method`           | Metoda HTTP (np. `POST`).                                            |
| `headers`          | Nagłówki dołączane do żądania (np. `Authorization`, `Content-Type`). |
| `requestTemplate`  | Szablon body żądania (zmienne podstawiane przez Paymove).            |
| `responseTemplate` | Oczekiwana struktura odpowiedzi od partnera.                         |
| `expectedCode`     | Oczekiwany kod HTTP odpowiedzi (np. 200).                            |
| `expectedResponse` | Oczekiwana treść odpowiedzi (np. `{ "status": "ok" }`).              |
| `retries`          | Liczba ponownych prób przy niepowodzeniu.                            |
| `type`             | Typ zdarzenia (np. `PAYMENT`).                                       |

### Endpointy – Webhooki

| Metoda | Endpoint                                                   | Opis                                                         |
| ------ | ---------------------------------------------------------- | ------------------------------------------------------------ |
| GET    | `/api/pay/plugin/webhook`                                  | Lista webhooków. Opcjonalne query: `productId`, `partnerId`. |
| GET    | `/api/pay/plugin/webhook/{webhookId}`                      | Szczegóły webhooka.                                          |
| GET    | `/api/pay/plugin/webhook/{webhookId}/products`             | Lista produktów powiązanych z webhookiem.                    |
| POST   | `/api/pay/plugin/webhook`                                  | Tworzy webhook.                                              |
| POST   | `/api/pay/plugin/webhook/{webhookId}/products/{productId}` | Przypisuje webhook do produktu.                              |
| PATCH  | `/api/pay/plugin/webhook/{webhookId}`                      | Aktualizuje webhook. Wpływa na wszystkie powiązane produkty. |

### Utworzenie webhooka

```
POST /api/pay/plugin/webhook
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "name": "PaymentSuccessHook",
  "endpoint": "https://example.com/webhooks/payment-success",
  "method": "POST",
  "requestTemplate": {
    "productId": "productId",
    "event": "event"
  },
  "responseTemplate": {
    "status": "ok"
  },
  "expectedCode": 200,
  "expectedResponse": "{ \"status\": \"ok\" }",
  "retries": 3,
  "partnerId": "6909ca83-410f-47c4-910d-2057f8565a8c",
  "type": "PAYMENT",
  "headers": {
    "Authorization": ["Bearer abc123"],
    "Content-Type": ["application/json"]
  }
}
```

**Odpowiedź (200):** obiekt webhooka z nadanym `id` (identyfikator webhooka) oraz polem `signingSecret`:

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "id": "18e19688-bdda-4843-8777-0f04d0143c77",
  "name": "PaymentSuccessHook",
  "endpoint": "https://example.com/webhooks/payment-success",
  "method": "POST",
  "requestTemplate": { "productId": "productId", "event": "event" },
  "responseTemplate": { "status": "ok" },
  "expectedCode": 200,
  "expectedResponse": "{ \"status\": \"ok\" }",
  "retries": 3,
  "type": "PAYMENT",
  "headers": {
    "Authorization": ["Bearer abc123"],
    "Content-Type": ["application/json"]
  },
  "signingSecret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
```

<Warning>
  `signingSecret` służy do weryfikacji podpisu żądań przychodzących od Paymove. Przechowuj go bezpiecznie.
</Warning>

Po utworzeniu wywołaj **POST** `/api/pay/plugin/webhook/{webhookId}/products/{productId}`, aby powiązać webhook z produktem (odpowiedź: obiekt webhooka). Od tego momentu zdarzenia (np. udana płatność) dla tego produktu trafiają na Twój `endpoint`.
